@ai-matrx/print 0.8.67 → 0.8.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/react.cjs +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/package.json +3 -3
package/dist/react.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx","../src/core.ts","../src/katex-css.generated.ts","../src/labels.ts","../src/qr.ts"],"sourcesContent":["/**\n * @ai-matrx/print/react — the package's ONLY React entry.\n *\n * Two components, ported verbatim from matrx-frontend:\n * PrintOptionsDialog ← lib/block-print/PrintOptionsDialog.tsx\n * LabelSheetPreview ← lib/label-print/LabelSheetPreview.tsx\n *\n * The app seams are inverted here and NOWHERE else:\n * @/components/ui/dialog → inline minimal wrappers over @radix-ui/react-dialog\n * @/components/ui/drawer → inline minimal wrappers over vaul\n * @/components/ui/button → PrintDialogButton (the design-system Button's\n * exact class strings for the used variants)\n * @/hooks/use-mobile → useIsMobile, reproduced behavior-for-behavior\n * lucide-react → inline SVGs carrying lucide's own path data\n * @/lib/block-print/* → ./core\n * ./label-templates, ./qr-labels-printer → ./labels\n *\n * NO \"use client\" directive lives in this file — tsup stamps this entry via a\n * banner (see tsup.config.ts); a second literal directive here would be\n * duplicated into dist.\n *\n * Tailwind class strings are the originals verbatim: the consuming app's theme\n * tokens (bg-primary, text-muted-foreground, pb-safe…) style these components,\n * exactly as they did in matrx-frontend.\n */\n\nimport * as React from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\nimport { twMerge } from \"tailwind-merge\";\nimport type { CslItem, DocumentExport, DocumentFormat, DocumentNotice } from \"./document\";\n\nimport type {\n BlockPrinter,\n PrintOutcome,\n PrintSetting,\n PrintSettings,\n} from \"./core\";\nimport {\n computeCellLayout,\n generateQrDataUri,\n type LabelTemplate,\n type QrEcLevel,\n type QrLabel,\n} from \"./labels\";\n\n// ─── className merge ──────────────────────────────────────────────────────────\n\n/**\n * Tailwind-aware className merge. The app's `cn` is `twMerge(clsx(inputs))`;\n * `clsx` is dropped here (every call site passes strings / false), but\n * `tailwind-merge` is KEPT on purpose — the wrappers below depend on last-wins\n * conflict resolution (e.g. the caller's `sm:max-w-sm` must beat the built-in\n * `max-w-lg`). Same precedent as @ai-matrx/kit's `./confirm`.\n */\nfunction cn(...values: Array<string | null | undefined | false>): string {\n return twMerge(values.filter(Boolean).join(\" \"));\n}\n\n// ─── Icons (lucide path data, inlined — no lucide-react dependency) ───────────\n\ntype IconProps = React.SVGProps<SVGSVGElement>;\n\n/**\n * lucide's default SVG attributes, verbatim from\n * lucide-react/dist/esm/defaultAttributes.mjs.\n */\nfunction LucideIcon({\n lucideClass,\n className,\n children,\n ...props\n}: IconProps & { lucideClass: string }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={24}\n height={24}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn(lucideClass, className)}\n aria-hidden=\"true\"\n {...props}\n >\n {children}\n </svg>\n );\n}\n\n/** lucide `printer` */\nfunction Printer(props: IconProps) {\n return (\n <LucideIcon lucideClass=\"lucide lucide-printer\" {...props}>\n <path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\" />\n <path d=\"M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6\" />\n <rect x=\"6\" y=\"14\" width=\"12\" height=\"8\" rx=\"1\" />\n </LucideIcon>\n );\n}\n\n/** lucide `x` */\nfunction X(props: IconProps) {\n return (\n <LucideIcon lucideClass=\"lucide lucide-x\" {...props}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </LucideIcon>\n );\n}\n\n/** lucide `settings-2` */\nfunction Settings2(props: IconProps) {\n return (\n <LucideIcon\n lucideClass=\"lucide lucide-settings2 lucide-settings-2\"\n {...props}\n >\n <path d=\"M14 17H5\" />\n <path d=\"M19 7h-9\" />\n <circle cx=\"17\" cy=\"17\" r=\"3\" />\n <circle cx=\"7\" cy=\"7\" r=\"3\" />\n </LucideIcon>\n );\n}\n\n// ─── useIsMobile (reproduces @/hooks/use-mobile exactly) ──────────────────────\n\nconst MOBILE_BREAKPOINT = 768;\n\n/**\n * Verbatim behavior of matrx-frontend `hooks/use-mobile.tsx`: same breakpoint,\n * same `matchMedia` change listener that re-reads `window.innerWidth`, and the\n * same \"false until mounted\" semantics that keeps SSR output stable.\n */\nfunction useIsMobile(): boolean {\n const [isMobile, setIsMobile] = useState(false);\n const [hasMounted, setHasMounted] = useState(false);\n\n useEffect(() => {\n setHasMounted(true);\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n };\n\n onChange(); // Set initial value\n mql.addEventListener(\"change\", onChange);\n\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n if (!hasMounted) {\n return false; // Return false during SSR\n }\n\n return isMobile;\n}\n\n// ─── Dialog wrappers (minimal, over @radix-ui/react-dialog) ───────────────────\n\n/**\n * `modal` is read back by DialogContent to decide whether the scrim renders and\n * whether `aria-modal` is set — the app's RadixDialogModalProvider, reduced to\n * the one fact these wrappers consume.\n */\nconst DialogModalContext = React.createContext<boolean>(true);\n\nfunction Dialog({\n children,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root>) {\n const modal = props.modal ?? true;\n return (\n <DialogModalContext.Provider value={modal}>\n <DialogPrimitive.Root {...props} modal={modal}>\n {children}\n </DialogPrimitive.Root>\n </DialogModalContext.Provider>\n );\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n // Preserve readable page context behind the modal; separation comes from a light scrim, not blur.\n \"fixed inset-0 z-[10000] bg-black/20 dark:bg-black/30 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nconst DIALOG_DESKTOP_CLASSES =\n \"fixed left-[50%] top-[50%] z-[10000] grid min-w-0 w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 overflow-x-clip border bg-background p-6 shadow-lg [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\";\n\nconst DIALOG_MOBILE_SHEET_CLASSES =\n \"fixed inset-x-0 bottom-0 left-0 right-0 top-auto z-[10000] flex min-w-0 flex-col w-full max-w-full max-h-[90dvh] translate-x-0 translate-y-0 gap-4 overflow-x-clip border-t bg-background p-4 pb-safe shadow-lg [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full duration-200 rounded-t-2xl rounded-b-none overflow-y-auto overscroll-contain data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\";\n\n// Re-asserted LAST so the sheet geometry always wins over any caller `className`.\nconst DIALOG_MOBILE_SHEET_OVERRIDE =\n \"inset-x-0 bottom-0 left-0 right-0 top-auto translate-x-0 translate-y-0 w-full max-w-full max-h-[90dvh] rounded-b-none rounded-t-2xl overflow-y-auto\";\n\nfunction DialogContent({\n className,\n children,\n mobileSheet = true,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {\n /** When true (default), the dialog becomes a bottom sheet on mobile. */\n mobileSheet?: boolean;\n}) {\n const isMobile = useIsMobile();\n const isModal = React.useContext(DialogModalContext);\n const asSheet = mobileSheet && isMobile;\n\n return (\n <DialogPrimitive.Portal>\n {isModal ? <DialogOverlay /> : null}\n <DialogPrimitive.Content\n aria-modal={isModal || undefined}\n className={cn(\n asSheet ? DIALOG_MOBILE_SHEET_CLASSES : DIALOG_DESKTOP_CLASSES,\n className,\n asSheet && DIALOG_MOBILE_SHEET_OVERRIDE,\n // A non-modal Dialog stays below the WindowPanel manager's z=1000 floor.\n !isModal && \"z-[900]\",\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute right-2 top-4 flex h-10 w-10 items-center justify-center rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground sm:right-4\"\n >\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n );\n}\n\nfunction DialogHeader({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\n // DialogContent owns an absolute close control. Reserve its hit area in\n // every header so trailing actions never render underneath it.\n \"flex flex-col space-y-1.5 pr-12 text-center sm:text-left\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogTitle({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\n// ─── Drawer wrappers (minimal, over vaul) ─────────────────────────────────────\n\ntype DrawerProps = React.ComponentProps<typeof DrawerPrimitive.Root> & {\n /** Vaul does not forward false to its underlying Radix root. */\n modal?: true;\n};\n\nfunction Drawer({\n children,\n shouldScaleBackground = true,\n ...props\n}: DrawerProps) {\n return (\n <DrawerPrimitive.Root\n modal\n shouldScaleBackground={shouldScaleBackground}\n {...props}\n >\n {children}\n </DrawerPrimitive.Root>\n );\n}\n\nfunction DrawerOverlay({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n className={cn(\"fixed inset-0 z-50 bg-black/80\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerContent({\n className,\n children,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>) {\n return (\n <DrawerPrimitive.Portal>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n aria-modal=\"true\"\n className={cn(\n \"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background pb-safe\",\n className,\n )}\n // No DrawerDescription is rendered by this package's only Drawer usage.\n aria-describedby={undefined}\n {...props}\n >\n <div className=\"mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPrimitive.Portal>\n );\n}\n\nfunction DrawerHeader({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\"grid gap-1.5 p-4 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerTitle({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className,\n )}\n {...props}\n />\n );\n}\n\n// ─── PrintDialogButton (the design-system Button, used variants only) ─────────\n\nconst BUTTON_BASE =\n \"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98] [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\";\n\nconst BUTTON_VARIANTS = {\n default: \"bg-primary text-primary-foreground shadow-sm hover:bg-primary/90\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n} as const;\n\nconst BUTTON_SIZES = {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n icon: \"h-9 w-9\",\n} as const;\n\ninterface PrintDialogButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: keyof typeof BUTTON_VARIANTS;\n size?: keyof typeof BUTTON_SIZES;\n}\n\nfunction PrintDialogButton({\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: PrintDialogButtonProps) {\n return (\n <button\n className={cn(\n BUTTON_BASE,\n BUTTON_VARIANTS[variant],\n BUTTON_SIZES[size],\n className,\n )}\n {...props}\n />\n );\n}\n\n// ══════════════════════════════════════════════════════════════════════════════\n// PrintOptionsDialog\n// ══════════════════════════════════════════════════════════════════════════════\n\nexport interface PrintOptionsDialogProps {\n printer: BlockPrinter;\n data: unknown;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /**\n * Fired after printer.print() settles, with the PrintOutcome when the\n * printer reports one. \"downloaded\" means the popup was blocked and the\n * print document was saved as an .html download instead — hosts should\n * toast that, or the fallback is indistinguishable from nothing happening.\n */\n onPrinted?: (outcome: void | PrintOutcome) => void;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction buildDefaultSettings(settings: PrintSetting[]): PrintSettings {\n const out: PrintSettings = {};\n for (const s of settings) {\n if (s.type === \"boolean\") {\n out[s.id] = s.defaultValue;\n } else if (s.type === \"range\") {\n out[s.fromId] = s.defaultFrom;\n out[s.toId] = s.defaultTo;\n } else if (s.type === \"number\" || s.type === \"select\") {\n out[s.id] = s.defaultValue;\n }\n }\n return out;\n}\n\nfunction visibleSettings(\n settings: PrintSetting[],\n variantId: string,\n): PrintSetting[] {\n return settings.filter(\n (s) => !s.appliesTo || s.appliesTo.includes(variantId),\n );\n}\n\n// ─── Inner content (shared between Dialog and Drawer) ─────────────────────────\n\nfunction PrintOptionsContent({\n printer,\n data,\n onClose,\n onPrinted,\n}: {\n printer: BlockPrinter;\n data: unknown;\n onClose: () => void;\n onPrinted?: ((outcome: void | PrintOutcome) => void) | undefined;\n}) {\n // MATRX-EXCEPTION: React state initializer — \"\" (no variant selected) when\n // the printer declares zero variants; pure UI default, not a boundary write.\n const [selectedVariant, setSelectedVariant] = useState<string>(\n printer.variants[0]?.id ?? \"\",\n );\n const [isPrinting, setIsPrinting] = useState(false);\n\n const allSettings = printer.settings ?? [];\n\n // Initialize from defaults once\n const [settingValues, setSettingValues] = useState<PrintSettings>(() =>\n buildDefaultSettings(allSettings),\n );\n\n // Settings visible for the currently selected variant\n const currentSettings = useMemo(\n () => visibleSettings(allSettings, selectedVariant),\n [allSettings, selectedVariant],\n );\n\n const toggleSetting = (id: string) => {\n setSettingValues((prev) => ({ ...prev, [id]: !prev[id] }));\n };\n\n const setNumberSetting = (id: string, value: number) => {\n setSettingValues((prev) => ({ ...prev, [id]: value }));\n };\n\n const handlePrint = async (variantId?: string, useDefaults = false) => {\n setIsPrinting(true);\n try {\n const settings = useDefaults\n ? buildDefaultSettings(allSettings)\n : settingValues;\n const outcome = await printer.print(data, variantId, settings);\n onPrinted?.(outcome);\n } finally {\n setIsPrinting(false);\n onClose();\n }\n };\n\n return (\n <div className=\"flex flex-col min-h-0 flex-1\">\n {/* ── Scrollable body: variant list + settings ── */}\n <div className=\"flex-1 overflow-y-auto min-h-0 py-2 space-y-4\">\n {/* Variant picker */}\n {printer.variants.length > 0 && (\n <div>\n <p className=\"text-sm font-medium text-foreground mb-2 px-0.5\">\n Print format\n </p>\n <div className=\"space-y-1.5\">\n {printer.variants.map((variant) => (\n <button\n key={variant.id}\n onClick={() => setSelectedVariant(variant.id)}\n className={`w-full flex flex-col items-start px-3 py-2.5 rounded-lg border text-left transition-colors ${\n selectedVariant === variant.id\n ? \"border-primary bg-primary/5 text-primary\"\n : \"border-border bg-background hover:bg-accent\"\n }`}\n >\n <span className=\"text-sm font-medium\">{variant.label}</span>\n {variant.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {variant.description}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Settings toggles — only shown when there are settings for this variant */}\n {currentSettings.length > 0 && (\n <div>\n <p className=\"text-sm font-medium text-foreground mb-2 px-0.5 flex items-center gap-1.5\">\n <Settings2 className=\"w-3.5 h-3.5\" />\n Options\n </p>\n <div className=\"space-y-1\">\n {currentSettings.map((setting) => {\n if (setting.type === \"boolean\") {\n const checked = settingValues[setting.id] === true;\n return (\n <button\n key={setting.id}\n onClick={() => toggleSetting(setting.id)}\n className=\"w-full flex items-start gap-3 px-3 py-2.5 rounded-lg border border-border bg-background hover:bg-accent text-left transition-colors\"\n >\n {/* Custom checkbox */}\n <span\n className={`mt-0.5 shrink-0 w-4 h-4 rounded border-2 flex items-center justify-center transition-colors ${\n checked\n ? \"bg-primary border-primary\"\n : \"border-muted-foreground/40\"\n }`}\n >\n {checked && (\n <svg\n className=\"w-2.5 h-2.5 text-primary-foreground\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n >\n <path\n d=\"M1.5 5l2.5 2.5 4.5-4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </span>\n <span className=\"flex flex-col min-w-0\">\n <span className=\"text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n {setting.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {setting.description}\n </span>\n )}\n </span>\n </button>\n );\n }\n\n if (setting.type === \"range\") {\n const min = setting.min ?? 0;\n const fromVal =\n (settingValues[setting.fromId] as number | undefined) ?? 0;\n const toVal =\n (settingValues[setting.toId] as number | undefined) ?? 0;\n const parse = (raw: string) => {\n if (raw === \"\") return 0;\n const n = parseInt(raw, 10);\n return Number.isNaN(n) ? 0 : Math.max(min, n);\n };\n return (\n <div\n key={setting.id}\n className=\"w-full flex items-start gap-3 px-3 py-2.5 rounded-lg border border-border bg-background\"\n >\n <span className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n {setting.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {setting.description}\n </span>\n )}\n </span>\n <div className=\"flex items-center gap-1.5 shrink-0 pt-0.5\">\n <input\n type=\"number\"\n inputMode=\"numeric\"\n min={min}\n value={fromVal === 0 ? \"\" : fromVal}\n onChange={(e) =>\n setNumberSetting(\n setting.fromId,\n parse(e.target.value),\n )\n }\n placeholder={setting.fromPlaceholder ?? \"start\"}\n aria-label={`${setting.label} — from`}\n className=\"w-14 h-7 text-xs text-center rounded border border-input bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n <span className=\"text-xs text-muted-foreground\">\n to\n </span>\n <input\n type=\"number\"\n inputMode=\"numeric\"\n min={min}\n value={toVal === 0 ? \"\" : toVal}\n onChange={(e) =>\n setNumberSetting(\n setting.toId,\n parse(e.target.value),\n )\n }\n placeholder={setting.toPlaceholder ?? \"end\"}\n aria-label={`${setting.label} — to`}\n className=\"w-14 h-7 text-xs text-center rounded border border-input bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n </div>\n </div>\n );\n }\n\n if (setting.type === \"number\") {\n const min = setting.min ?? 0;\n const val =\n (settingValues[setting.id] as number | undefined) ??\n setting.defaultValue;\n const parse = (raw: string) => {\n if (raw === \"\") return setting.defaultValue;\n const n = parseInt(raw, 10);\n if (Number.isNaN(n)) return setting.defaultValue;\n const bounded = Math.max(min, n);\n return setting.max !== undefined\n ? Math.min(setting.max, bounded)\n : bounded;\n };\n return (\n <div\n key={setting.id}\n className=\"w-full flex items-start gap-3 px-3 py-2.5 rounded-lg border border-border bg-background\"\n >\n <span className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n {setting.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {setting.description}\n </span>\n )}\n </span>\n <input\n type=\"number\"\n inputMode=\"numeric\"\n min={min}\n max={setting.max}\n value={val}\n onChange={(e) =>\n setNumberSetting(setting.id, parse(e.target.value))\n }\n aria-label={setting.label}\n className=\"w-16 h-7 text-xs text-center rounded border border-input bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-ring shrink-0 mt-0.5\"\n />\n </div>\n );\n }\n\n if (setting.type === \"select\") {\n const val =\n (settingValues[setting.id] as string | undefined) ??\n setting.defaultValue;\n return (\n <div\n key={setting.id}\n className=\"w-full flex items-start gap-3 px-3 py-2.5 rounded-lg border border-border bg-background\"\n >\n <span className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n {setting.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {setting.description}\n </span>\n )}\n </span>\n <div className=\"flex items-center gap-0.5 shrink-0 rounded-md border border-input p-0.5 mt-0.5\">\n {setting.options.map((opt) => (\n <button\n key={opt.value}\n onClick={() =>\n setSettingValues((prev) => ({\n ...prev,\n [setting.id]: opt.value,\n }))\n }\n className={`px-2 h-6 text-xs rounded transition-colors ${\n val === opt.value\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent\"\n }`}\n >\n {opt.label}\n </button>\n ))}\n </div>\n </div>\n );\n }\n\n return null;\n })}\n </div>\n </div>\n )}\n </div>\n\n {/* ── Action buttons — always pinned at bottom ── */}\n <div className=\"flex flex-col gap-2 pt-3 pb-1 border-t border-border mt-2 shrink-0\">\n <PrintDialogButton\n onClick={() => handlePrint(selectedVariant || undefined)}\n disabled={isPrinting}\n className=\"w-full\"\n >\n <Printer className=\"w-4 h-4 mr-2\" />\n {isPrinting ? \"Opening print view...\" : \"Print / Save PDF\"}\n </PrintDialogButton>\n {printer.variants.length > 0 && (\n <PrintDialogButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => handlePrint(undefined, true)}\n disabled={isPrinting}\n className=\"w-full text-muted-foreground text-xs\"\n >\n Print with defaults\n </PrintDialogButton>\n )}\n </div>\n </div>\n );\n}\n\n// ─── Public component ─────────────────────────────────────────────────────────\n\n/**\n * Shared pre-print options UI — shown when a BlockPrinter has variants or settings.\n * Desktop: Dialog. Mobile: Drawer (bottom sheet).\n */\nexport function PrintOptionsDialog({\n printer,\n data,\n open,\n onOpenChange,\n onPrinted,\n}: PrintOptionsDialogProps) {\n const isMobile = useIsMobile();\n const handleClose = () => onOpenChange(false);\n\n if (isMobile) {\n return (\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent className=\"flex flex-col max-h-[85dvh]\">\n <DrawerHeader className=\"flex items-center justify-between pb-2 shrink-0\">\n <DrawerTitle className=\"flex items-center gap-2\">\n <Printer className=\"w-4 h-4\" />\n {printer.label}\n </DrawerTitle>\n <PrintDialogButton\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleClose}\n className=\"h-7 w-7\"\n >\n <X className=\"w-4 h-4\" />\n </PrintDialogButton>\n </DrawerHeader>\n <div className=\"flex flex-col min-h-0 flex-1 overflow-hidden px-4 pb-4\">\n <PrintOptionsContent\n printer={printer}\n data={data}\n onClose={handleClose}\n onPrinted={onPrinted}\n />\n </div>\n </DrawerContent>\n </Drawer>\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-sm flex flex-col max-h-[85dvh]\">\n <DialogHeader className=\"shrink-0\">\n <DialogTitle className=\"flex items-center gap-2\">\n <Printer className=\"w-4 h-4\" />\n {printer.label}\n </DialogTitle>\n <DialogDescription className=\"sr-only\">\n Choose a print format and options for this content.\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col min-h-0 flex-1 overflow-hidden\">\n <PrintOptionsContent\n printer={printer}\n data={data}\n onClose={handleClose}\n onPrinted={onPrinted}\n />\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────────────────\n\n/**\n * Manages open state for PrintOptionsDialog.\n * If the printer has variants or settings, shows the dialog.\n * If it has neither, calls printer.print() immediately with no settings.\n */\nexport function usePrintOptions(\n printer: BlockPrinter | null,\n data: unknown,\n onPrinted?: (outcome: void | PrintOutcome) => void,\n) {\n const [open, setOpen] = useState(false);\n\n const triggerPrint = async () => {\n if (!printer) return;\n const hasOptions =\n printer.variants.length > 0 || (printer.settings?.length ?? 0) > 0;\n if (!hasOptions) {\n const outcome = await printer.print(data);\n onPrinted?.(outcome);\n } else {\n setOpen(true);\n }\n };\n\n return { open, setOpen, triggerPrint };\n}\n\n// ══════════════════════════════════════════════════════════════════════════════\n// LabelSheetPreview\n// ══════════════════════════════════════════════════════════════════════════════\n\n/**\n * LabelSheetPreview — scaled on-screen preview of one label sheet, driven by\n * the same LabelTemplate geometry as the printer (percent-positioned cells,\n * so it is exactly proportional at any width). Also renders a calibration\n * view (outlines only) so a user can eyeball the grid before printing.\n *\n * QR images are generated client-side to data URIs via `qrcode` (deferred\n * import at effect time).\n */\n\nexport interface LabelSheetPreviewProps {\n template: LabelTemplate;\n labels: QrLabel[];\n /** 1-based first cell to fill — leading cells render empty. */\n startAtLabel?: number;\n showCaption?: boolean;\n ecLevel?: QrEcLevel;\n /** Outlines-only calibration view. */\n calibration?: boolean;\n /** Which sheet to show (0-based). */\n pageIndex?: number;\n className?: string;\n}\n\nexport function LabelSheetPreview({\n template: t,\n labels,\n startAtLabel = 1,\n showCaption = true,\n ecLevel = \"M\",\n calibration = false,\n pageIndex = 0,\n className,\n}: LabelSheetPreviewProps) {\n const [uris, setUris] = useState<Map<string, string>>(new Map());\n\n const perPage = t.cols * t.rows;\n const leadingBlanks = Math.max(0, startAtLabel - 1) % perPage;\n const pageStart = pageIndex * perPage - leadingBlanks;\n const layout = computeCellLayout(t, showCaption);\n\n // Labels occupying this page's cells (null = blank cell)\n const cells: (QrLabel | null)[] = Array.from({ length: perPage }, (_, i) => {\n const li = pageStart + i;\n // `?? null` is required by noUncheckedIndexedAccess; the bounds check\n // already guarantees the element exists, so behavior is unchanged.\n return li >= 0 && li < labels.length ? (labels[li] ?? null) : null;\n });\n\n const valuesKey = Array.from(\n new Set(\n cells.filter((c): c is QrLabel => c !== null).map((c) => c.qrValue),\n ),\n ).join(\"\\u0000\");\n const qrIn = layout.qrIn;\n\n useEffect(() => {\n if (calibration) return;\n let cancelled = false;\n const values = valuesKey ? valuesKey.split(\"\\u0000\") : [];\n void (async () => {\n const next = new Map<string, string>();\n for (const v of values) {\n try {\n next.set(v, await generateQrDataUri(v, ecLevel, qrIn));\n } catch (err) {\n console.error(\"[LabelSheetPreview] QR generation failed:\", err);\n }\n }\n if (!cancelled) setUris(next);\n })();\n return () => {\n cancelled = true;\n };\n }, [calibration, ecLevel, qrIn, valuesKey]);\n\n const pct = (v: number, base: number) => `${(v / base) * 100}%`;\n const radiusPct = t.round\n ? \"50%\"\n : t.cornerRadiusIn\n ? `${(t.cornerRadiusIn / t.labelWIn) * 100}% / ${(t.cornerRadiusIn / t.labelHIn) * 100}%`\n : \"0\";\n\n return (\n <div\n className={`relative w-full bg-white border border-border rounded-md shadow-sm overflow-hidden ${className ?? \"\"}`}\n style={{ aspectRatio: `${t.sheetWIn} / ${t.sheetHIn}` }}\n aria-label={`Label sheet preview — ${t.stockCode}`}\n >\n {cells.map((label, i) => {\n const col = i % t.cols;\n const row = Math.floor(i / t.cols);\n const x = t.marginLeftIn + col * (t.labelWIn + t.gutterXIn);\n const y = t.marginTopIn + row * (t.labelHIn + t.gutterYIn);\n const uri = label ? uris.get(label.qrValue) : undefined;\n const wide = layout.wide;\n return (\n <div\n key={i}\n className={\n calibration || !label\n ? \"absolute border border-dashed border-slate-300\"\n : \"absolute border border-slate-200\"\n }\n style={{\n left: pct(x, t.sheetWIn),\n top: pct(y, t.sheetHIn),\n width: pct(t.labelWIn, t.sheetWIn),\n height: pct(t.labelHIn, t.sheetHIn),\n borderRadius: radiusPct,\n overflow: \"hidden\",\n }}\n >\n {calibration ? (\n <div className=\"w-full h-full flex items-center justify-center text-[8px] text-slate-400\">\n {i + 1}\n </div>\n ) : label ? (\n <div\n className={`w-full h-full flex ${wide ? \"flex-row items-center\" : \"flex-col items-center justify-center\"} gap-[2%] p-[3%]`}\n >\n {uri ? (\n <img\n src={uri}\n alt=\"\"\n className=\"block\"\n style={\n wide\n ? { height: \"88%\", aspectRatio: \"1\" }\n : {\n width: `${(layout.qrIn / t.labelWIn) * 100}%`,\n aspectRatio: \"1\",\n }\n }\n />\n ) : (\n <div\n className=\"bg-slate-100 animate-pulse rounded-sm\"\n style={\n wide\n ? { height: \"88%\", aspectRatio: \"1\" }\n : { width: \"70%\", aspectRatio: \"1\" }\n }\n />\n )}\n <div\n className={`min-w-0 overflow-hidden text-black leading-tight ${wide ? \"flex-1 text-left\" : \"text-center w-full\"}`}\n style={{ fontSize: \"min(1.4vw, 10px)\" }}\n >\n {showCaption && (\n <div className=\"font-bold truncate\">\n {label.caption ?? label.qrValue}\n </div>\n )}\n {wide &&\n label.lines\n ?.filter((l) => l.trim())\n .map((l, li) => (\n <div key={li} className=\"truncate text-slate-600\">\n {l}\n </div>\n ))}\n </div>\n </div>\n ) : null}\n </div>\n );\n })}\n </div>\n );\n}\n\n// ─── DocumentPrintPreview ─────────────────────────────────────────────────────\n\n\nexport interface DocumentPrintPreviewProps {\n /** Markdown source, frontmatter settings included. */\n markdown: string;\n /** File name / fallback title when the frontmatter has none. */\n title?: string;\n className?: string;\n /** Called for every degraded or dropped piece (also listed in the panel). */\n onNotice?: (notice: DocumentNotice) => void;\n /**\n * Resolves citation keys the document's frontmatter does not define\n * (e.g. `[@research:<id>]`, `[@file:<id>]`) to CSL-JSON references.\n */\n resolveReferences?: (keys: string[]) => Promise<CslItem[]>;\n /**\n * Draws the PDF pages when the browser cannot show a PDF inline (Chrome\n * with \"Download PDFs\" on, Android, touch devices). Receives a same-origin\n * `blob:` URL of the exact PDF. Without it, such browsers get the Web view.\n */\n renderPages?: (pdfUrl: string) => React.ReactNode;\n /**\n * Prints the PDF's pages when the browser has no inline PDF viewer to print\n * from. Without it, Print prints the Web view.\n */\n printPages?: (pdfUrl: string) => Promise<void>;\n}\n\n/**\n * True when this browser shows a PDF inside a frame with all its pages and can\n * print it from there. Chrome with \"Download PDFs\" on (and Android) reports\n * `pdfViewerEnabled === false` and paints only a file name and an Open button;\n * touch browsers render just the first page.\n */\nfunction hasInlinePdfViewer(): boolean {\n if (typeof navigator === \"undefined\") return true;\n if (navigator.pdfViewerEnabled === false) return false;\n return !(typeof window !== \"undefined\" && window.matchMedia?.(\"(pointer: coarse)\").matches);\n}\n\n/** Prints an HTML document through a hidden same-origin frame. */\nfunction printHtml(html: string): void {\n const frame = document.createElement(\"iframe\");\n frame.setAttribute(\"aria-hidden\", \"true\");\n frame.style.cssText = \"position:fixed;right:0;bottom:0;width:0;height:0;border:0;\";\n document.body.appendChild(frame);\n const win = frame.contentWindow;\n if (!win) {\n frame.remove();\n throw new Error(\"Printing is blocked in this browser.\");\n }\n win.document.open();\n win.document.write(html);\n win.document.close();\n const run = () => {\n win.focus();\n win.print();\n setTimeout(() => frame.remove(), 1000);\n };\n if (win.document.readyState === \"complete\") setTimeout(run, 50);\n else win.addEventListener(\"load\", run, { once: true });\n}\n\nconst EXPORT_BUTTONS: Array<{ format: DocumentFormat; label: string }> = [\n { format: \"pdf\", label: \"PDF\" },\n { format: \"docx\", label: \"Word\" },\n { format: \"epub\", label: \"EPUB\" },\n { format: \"html\", label: \"HTML\" },\n { format: \"markdown\", label: \"Markdown\" },\n];\n\n/**\n * Print preview for a markdown document: the real PDF (the same file the PDF\n * download gives you — pages, header/footer, TOC page numbers), a \"Web\" view\n * of the HTML with a clickable TOC, and one-click export to PDF, Word, EPUB,\n * HTML and Markdown. Everything comes from one parsed tree, so what you see\n * is what every format contains. Engines load lazily on mount.\n */\nexport function DocumentPrintPreview({\n markdown,\n title,\n className,\n onNotice,\n resolveReferences,\n renderPages,\n printPages,\n}: DocumentPrintPreviewProps) {\n const [inlineViewer] = useState(hasInlinePdfViewer);\n const canShowPages = inlineViewer || Boolean(renderPages);\n const [view, setView] = useState<\"pages\" | \"web\">(canShowPages ? \"pages\" : \"web\");\n const [pdf, setPdf] = useState<{ url: string; pages: number } | null>(null);\n const [html, setHtml] = useState<string>(\"\");\n const [notices, setNotices] = useState<DocumentNotice[]>([]);\n const [summary, setSummary] = useState<string>(\"\");\n const [error, setError] = useState<string | null>(null);\n const [busy, setBusy] = useState<DocumentFormat | \"render\" | null>(\"render\");\n const noticeRef = React.useRef(onNotice);\n noticeRef.current = onNotice;\n const frameRef = React.useRef<HTMLIFrameElement>(null);\n\n useEffect(() => {\n let cancelled = false;\n let url: string | null = null;\n setBusy(\"render\");\n const timer = setTimeout(async () => {\n try {\n const doc = await import(\"@ai-matrx/print/document\");\n const exp = await doc.exportDocument(markdown, \"pdf\", { title, resolveReferences });\n if (cancelled) return;\n url = URL.createObjectURL(new Blob([exp.bytes as BlobPart], { type: \"application/pdf\" }));\n const images = await doc.resolveDocumentImages(exp.tree);\n if (cancelled) return;\n setPdf({ url, pages: exp.pageCount ?? 0 });\n setHtml(doc.documentToHtml(exp.tree, { images }));\n const s = exp.tree.settings;\n setSummary(\n [\n doc.PAGE_SIZES[s.page.size].label,\n s.page.orientation === \"landscape\" ? \"Landscape\" : \"Portrait\",\n exp.tree.figures.length ? `${exp.tree.figures.length} figure${exp.tree.figures.length === 1 ? \"\" : \"s\"}` : \"\",\n exp.tree.tables.length ? `${exp.tree.tables.length} table${exp.tree.tables.length === 1 ? \"\" : \"s\"}` : \"\",\n exp.tree.bibliography.length ? `${exp.tree.bibliography.length} reference${exp.tree.bibliography.length === 1 ? \"\" : \"s\"}` : \"\",\n ]\n .filter(Boolean)\n .join(\" · \"),\n );\n setNotices(exp.notices);\n for (const n of exp.notices) noticeRef.current?.(n);\n setError(null);\n } catch (err) {\n if (!cancelled) setError(err instanceof Error ? err.message : String(err));\n } finally {\n if (!cancelled) setBusy(null);\n }\n }, 350);\n return () => {\n cancelled = true;\n clearTimeout(timer);\n if (url) URL.revokeObjectURL(url);\n };\n }, [markdown, title, resolveReferences]);\n\n const download = async (format: DocumentFormat) => {\n setBusy(format);\n try {\n const doc = await import(\"@ai-matrx/print/document\");\n const exp: DocumentExport = await doc.exportDocument(markdown, format, { title, resolveReferences });\n doc.downloadDocumentExport(exp);\n setError(null);\n } catch (err) {\n setError(`${format.toUpperCase()} export failed: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n setBusy(null);\n }\n };\n\n const print = async () => {\n if (!pdf) return;\n try {\n // The inline PDF viewer prints the exact pages shown.\n if (inlineViewer && frameRef.current?.contentWindow) {\n frameRef.current.contentWindow.print();\n return;\n }\n // No inline viewer: the frame holds a placeholder, so print the pages\n // another way — the host's page printer, else the Web view.\n if (printPages) await printPages(pdf.url);\n else printHtml(html);\n } catch (err) {\n setError(`Print failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n };\n\n const btn =\n \"inline-flex h-7 items-center gap-1 rounded-md border border-border bg-background px-2.5 text-xs font-medium text-foreground hover:bg-accent disabled:opacity-50\";\n return (\n <div className={cn(\"flex min-h-0 flex-1 flex-col\", className)} data-testid=\"document-print-preview\">\n <div className=\"flex flex-wrap items-center gap-2 border-b border-border px-3 py-1.5 text-xs\">\n <div className=\"inline-flex overflow-hidden rounded-md border border-border\" role=\"tablist\">\n {(canShowPages ? ([\"pages\", \"web\"] as const) : ([\"web\"] as const)).map((v) => (\n <button\n key={v}\n type=\"button\"\n role=\"tab\"\n aria-selected={view === v}\n onClick={() => setView(v)}\n className={cn(\"px-2.5 py-1 text-xs\", view === v ? \"bg-accent font-medium text-foreground\" : \"text-muted-foreground\")}\n >\n {v === \"pages\" ? \"Pages\" : \"Web\"}\n </button>\n ))}\n </div>\n <span className=\"text-muted-foreground\" data-testid=\"document-print-summary\">\n {busy === \"render\" && !pdf ? \"Laying out pages…\" : `${pdf?.pages ?? 0} page${pdf?.pages === 1 ? \"\" : \"s\"}${summary ? ` · ${summary}` : \"\"}`}\n </span>\n <div className=\"ml-auto flex flex-wrap items-center gap-1.5\">\n <button type=\"button\" className={btn} onClick={() => void print()} disabled={!pdf}>\n Print\n </button>\n {EXPORT_BUTTONS.map((b) => (\n <button key={b.format} type=\"button\" className={btn} onClick={() => void download(b.format)} disabled={busy === b.format}>\n {busy === b.format ? \"Saving…\" : b.label}\n </button>\n ))}\n </div>\n </div>\n {error && <p className=\"border-b border-border bg-destructive/10 px-3 py-1.5 text-xs text-destructive\">{error}</p>}\n {notices.length > 0 && (\n <details className=\"border-b border-border px-3 py-1.5 text-xs\">\n <summary className=\"cursor-pointer text-amber-700 dark:text-amber-400\">\n {notices.length} thing{notices.length === 1 ? \"\" : \"s\"} to fix before printing\n </summary>\n <ul className=\"mt-1 space-y-1\">\n {notices.map((n, i) => (\n <li key={i}>\n <span className=\"text-foreground\">{n.message}</span> <span className=\"text-muted-foreground\">{n.remedy}</span>\n </li>\n ))}\n </ul>\n </details>\n )}\n <div className=\"min-h-0 flex-1 bg-muted/40\">\n {view === \"pages\" ? (\n pdf ? (\n inlineViewer ? (\n <iframe ref={frameRef} title=\"Print preview\" src={pdf.url} className=\"h-full min-h-[480px] w-full border-0\" />\n ) : (\n <div className=\"h-full min-h-[480px] w-full\">{renderPages?.(pdf.url)}</div>\n )\n ) : (\n <p className=\"p-8 text-center text-xs text-muted-foreground\">{error ? \"No preview.\" : \"Laying out pages…\"}</p>\n )\n ) : (\n <iframe title=\"Web preview\" srcDoc={html} sandbox=\"allow-same-origin\" className=\"h-full min-h-[480px] w-full border-0 bg-white\" />\n )}\n </div>\n </div>\n );\n}\n","/**\n * @ai-matrx/print/core — the Block Print contract & print-window composition.\n *\n * Ported VERBATIM from matrx-frontend `lib/block-print/block-print-utils.ts`\n * (2026-08-29 state, including the `number` and `select` PrintSetting types the\n * commerce label lane added). Zero dependencies; touches `window` only at call\n * time, so the module stays import-inert and RSC-safe.\n *\n * BlockPrinter is the only interface a host system needs to know about.\n * Each printable unit exports its own BlockPrinter alongside its render\n * component; host code only ever calls print(data, variantId, settings).\n *\n * `buildFitTextScript` is the ONE auto-shrink script (the flashcards\n * FIT_TEXT pattern, parameterized by selector so the label lane shares it).\n */\n\n// THE ONE HTML escaper (duplication census row 14). Nine copies of\n// `escapeHtml` were live across the fleet and THEY DID NOT ESCAPE THE SAME\n// CHARACTERS — a string safe through one was an attribute break-out through\n// another. It moved to `@ai-matrx/kit/html-escape` with a decided character\n// set: an HTML escaper must not require this package's jspdf + html2canvas\n// graph. Not re-exported from here; import `@ai-matrx/kit/html-escape`.\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport { KATEX_CSS, KATEX_CSS_MARKER } from \"./katex-css.generated\";\n\n// ─── Contract ────────────────────────────────────────────────────────────────\n\nexport interface PrintVariant {\n id: string;\n label: string;\n description?: string;\n}\n\n/**\n * A single user-configurable setting shown in the print dialog.\n * Extend the discriminated union for new control types\n * without breaking existing printers.\n */\nexport type PrintSetting =\n | {\n type: \"boolean\";\n id: string;\n label: string;\n description?: string;\n /** Default value shown when the dialog first opens */\n defaultValue: boolean;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /**\n * A numeric \"from → to\" range control, rendered as two small number inputs.\n * Writes two entries into the resolved PrintSettings map — one under fromId,\n * one under toId. Printers interpret 0 (or missing) as \"no bound\" by convention.\n */\n type: \"range\";\n /** Stable id used as React key — not written to the settings map */\n id: string;\n label: string;\n description?: string;\n /** Settings map key for the lower bound value */\n fromId: string;\n /** Settings map key for the upper bound value */\n toId: string;\n defaultFrom: number;\n defaultTo: number;\n /** Minimum accepted value for each input (inclusive). Default 0. */\n min?: number;\n /** Placeholder shown when the input is empty (0). Default \"start\" / \"end\". */\n fromPlaceholder?: string;\n toPlaceholder?: string;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /** A single numeric input (e.g. \"start at label #\", \"copies\"). */\n type: \"number\";\n id: string;\n label: string;\n description?: string;\n defaultValue: number;\n /** Minimum accepted value (inclusive). Default 0. */\n min?: number;\n /** Maximum accepted value (inclusive). Omit = unbounded. */\n max?: number;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /** A one-of-N choice rendered as a compact segmented row. */\n type: \"select\";\n id: string;\n label: string;\n description?: string;\n options: { value: string; label: string }[];\n defaultValue: string;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n };\n\n/** A resolved map of setting id → current value, passed to print(). */\nexport type PrintSettings = Record<string, boolean | string | number>;\n\n/**\n * The self-contained print capability that each block can export.\n * Host code only ever calls print(data, variantId, settings) — it never inspects internals.\n */\nexport interface BlockPrinter {\n /** Tooltip / aria-label for the print button in the block header */\n label: string;\n\n /**\n * Variants offered to the user before printing.\n * Empty array = no options dialog shown; prints immediately with defaults.\n */\n variants: PrintVariant[];\n\n /**\n * Optional user-configurable settings shown below the variant picker.\n * Omit entirely if the block has no configurable settings.\n */\n settings?: PrintSetting[];\n\n /**\n * Execute the print.\n * data — the block's structured data (serverData or parsed content)\n * variantId — which variant was chosen (undefined = default / no variants)\n * settings — resolved setting values chosen by the user\n *\n * MAY resolve to a PrintOutcome (\"opened\" | \"downloaded\") so the host can\n * tell the user when the popup-blocked download fallback fired; `void`\n * stays legal for printers that don't go through a print window.\n */\n print: (\n data: unknown,\n variantId?: string,\n settings?: PrintSettings,\n ) => void | PrintOutcome | Promise<void | PrintOutcome>;\n}\n\n// ─── Shared Utilities ─────────────────────────────────────────────────────────\n\nconst PRINT_STYLES = `\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;\n font-size: 11pt;\n line-height: 1.6;\n color: #1a1a1a;\n background: #fff;\n max-width: 860px;\n margin: 0 auto;\n padding: 28px 36px;\n }\n\n .print-actions {\n display: flex;\n gap: 10px;\n margin-bottom: 24px;\n padding-bottom: 18px;\n border-bottom: 2px solid #e5e7eb;\n }\n\n .print-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 20px;\n border-radius: 8px;\n font-size: 13px;\n font-family: inherit;\n cursor: pointer;\n border: none;\n font-weight: 600;\n }\n\n .print-btn-primary { background: #2563eb; color: #fff; }\n .print-btn-secondary { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; }\n\n h1 { font-size: 22pt; font-weight: 700; margin-bottom: 0.5em; }\n h2 { font-size: 16pt; font-weight: 700; margin: 1.2em 0 0.4em; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.2em; }\n h3 { font-size: 13pt; font-weight: 600; margin: 1em 0 0.3em; }\n p { margin-bottom: 0.7em; }\n\n table {\n width: 100%;\n border-collapse: collapse;\n margin: 1em 0;\n font-size: 10pt;\n }\n th {\n background: #1e293b;\n color: #f8fafc;\n font-weight: 600;\n text-align: left;\n padding: 7px 12px;\n border: 1px solid #334155;\n }\n td {\n padding: 6px 12px;\n border: 1px solid #e2e8f0;\n vertical-align: top;\n }\n tr:nth-child(even) td { background: #f8fafc; }\n\n @media print {\n body { max-width: 100%; padding: 0; }\n .print-actions { display: none !important; }\n th { -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n }\n`;\n\n/**\n * Wrap body HTML in a complete, styled print document.\n */\nexport function buildPrintDocument(\n bodyHtml: string,\n title = \"AI Response\",\n extraStyles = \"\",\n): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeHtml(title)}</title>\n <style>\n${PRINT_STYLES}\n${extraStyles}\n </style>\n</head>\n<body>\n <div class=\"print-actions\">\n <button class=\"print-btn print-btn-primary\" onclick=\"window.print()\">Print / Save as PDF</button>\n <button class=\"print-btn print-btn-secondary\" onclick=\"window.close()\">Close</button>\n </div>\n <div class=\"content\">\n${bodyHtml}\n </div>\n</body>\n</html>`;\n}\n\n/**\n * How a print request actually reached the user.\n *\n * \"opened\" — a print window/tab was opened and the document written into it.\n * \"downloaded\" — the popup was blocked, so the document was downloaded as an\n * .html file instead (the contract's fallback). Hosts SHOULD\n * surface this to the user (e.g. a toast saying the print file\n * was downloaded) — a blocked popup plus a silent download is\n * indistinguishable from \"nothing happened\", which is exactly\n * how QA filed it (2026-08-30, F5/F6/F8).\n */\nexport type PrintOutcome = \"opened\" | \"downloaded\";\n\n/**\n * Open a new browser window and write a complete HTML document into it.\n * Falls back to downloading an .html file if the popup is blocked.\n *\n * Returns which path was taken so callers can tell the user when the\n * fallback fired. `windowFeatures` customizes the popup geometry (the\n * flashcards fixed-geometry variants pass a wider window).\n */\n/**\n * Every printed document leaves through here, so this is where typeset math\n * gets its stylesheet: when the page carries KaTeX output and does not already\n * carry the sheet, it is added to <head>. A page with no math is untouched,\n * byte for byte (the flashcard golden master holds every printer to that).\n */\nexport function withMathStyles(htmlDocument: string): string {\n if (!htmlDocument.includes('class=\"katex\"') || htmlDocument.includes(KATEX_CSS_MARKER)) {\n return htmlDocument;\n }\n const style = `<style data-matrx-katex>${KATEX_CSS}</style>`;\n const head = htmlDocument.search(/<\\/head>/i);\n return head >= 0\n ? htmlDocument.slice(0, head) + style + htmlDocument.slice(head)\n : style + htmlDocument;\n}\n\nexport function openPrintWindow(\n rawDocument: string,\n filename = \"print\",\n windowFeatures = \"width=920,height=720,scrollbars=yes\",\n): PrintOutcome {\n const htmlDocument = withMathStyles(rawDocument);\n const win = window.open(\"\", \"_blank\", windowFeatures);\n // Popup blocked — fall back to file download.\n if (!win) return openPrintWindowFallback(htmlDocument, filename);\n win.document.open();\n win.document.write(htmlDocument);\n win.document.close();\n return \"opened\";\n}\n\n/** A print window opened before its document is ready (see `openPendingPrintWindow`). */\nexport interface PendingPrintWindow {\n /** Write the finished document; downloads it when the popup was blocked or closed. */\n write(htmlDocument: string): PrintOutcome;\n}\n\n/**\n * Open the print window NOW — inside the click, where popup blockers allow it —\n * and write the document later, once async preparation (drawing diagrams) is\n * done. The window says \"Preparing…\" meanwhile.\n */\nexport function openPendingPrintWindow(\n filename = \"print\",\n windowFeatures = \"width=920,height=720,scrollbars=yes\",\n): PendingPrintWindow {\n const win = window.open(\"\", \"_blank\", windowFeatures);\n if (win) {\n win.document.open();\n win.document.write(\n '<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Preparing…</title></head><body style=\"font:14px -apple-system,BlinkMacSystemFont,sans-serif;color:#555;padding:2rem\">Preparing the document for print…</body></html>',\n );\n win.document.close();\n }\n return {\n write(rawDocument: string): PrintOutcome {\n const htmlDocument = withMathStyles(rawDocument);\n if (!win || win.closed) return openPrintWindowFallback(htmlDocument, filename);\n win.document.open();\n win.document.write(htmlDocument);\n win.document.close();\n return \"opened\";\n },\n };\n}\n\nfunction openPrintWindowFallback(htmlDocument: string, filename: string): PrintOutcome {\n const blob = new Blob([htmlDocument], { type: \"text/html;charset=utf-8\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `${filename.replace(/\\s+/g, \"-\").toLowerCase()}.html`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n // Revoke on a macrotask so the browser has started the blob fetch —\n // a synchronous revoke races the download in some engines.\n setTimeout(() => URL.revokeObjectURL(url), 0);\n return \"downloaded\";\n}\n\n/**\n * Shorthand: build the document and open it in one call.\n * Returns the same PrintOutcome as openPrintWindow.\n */\nexport function printHtmlContent(\n bodyHtml: string,\n title = \"AI Response\",\n extraStyles = \"\",\n): PrintOutcome {\n const doc = buildPrintDocument(bodyHtml, title, extraStyles);\n return openPrintWindow(doc, title);\n}\n\n/**\n * Build the auto-shrink script that runs INSIDE a printed window: for every\n * element matching each selector, its font-size is reduced (0.5pt steps, 6pt\n * floor) until it fits its parent element's box. This is the flashcards\n * FIT_TEXT behavior verbatim, parameterized by selector so every lane\n * (flashcards card text, Avery text, label captions) shares the ONE loop.\n *\n * Pass the same selectors the original used to get byte-identical behavior:\n * flashcards passes `[\".card-text .card-text-inner\", \".avery-text-wrap .avery-text\"]`.\n */\nexport function buildFitTextScript(selectors: readonly string[]): string {\n const selectorsJson = JSON.stringify([...selectors]);\n return `\n<script>\n(function() {\n function fitText(el, container) {\n // container is the bounding box we must fit inside\n var maxW = container.clientWidth;\n var maxH = container.clientHeight;\n var size = parseFloat(window.getComputedStyle(el).fontSize);\n var minSize = 6;\n // Give the element a chance to reflow at current size before measuring\n el.style.fontSize = size + \"pt\";\n while (size > minSize && (el.scrollWidth > maxW + 1 || el.scrollHeight > maxH + 1)) {\n size -= 0.5;\n el.style.fontSize = size + \"pt\";\n }\n }\n function runAll() {\n var selectors = ${selectorsJson};\n for (var s = 0; s < selectors.length; s++) {\n var els = document.querySelectorAll(selectors[s]);\n for (var i = 0; i < els.length; i++) {\n fitText(els[i], els[i].parentElement);\n }\n }\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", runAll);\n } else {\n // document.write pages may already be loaded\n setTimeout(runAll, 0);\n }\n})();\n</script>`;\n}\n\n","// GENERATED by scripts/gen-katex-css.mjs from katex@0.18.9 — do not edit.\nexport const KATEX_VERSION = \"0.18.9\";\n/** Leads KATEX_CSS; lets openPrintWindow see the sheet is already present. */\nexport const KATEX_CSS_MARKER = \"/* matrx-katex@0.18.9 */\";\nexport const KATEX_CSS = \"/* matrx-katex@0.18.9 */@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.ttf) format(\\\"truetype\\\")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:\\\"0.18.9\\\"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.katex-newline{display:block}.katex .katex-base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .katex-base,.katex .katex-strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .katex-vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .katex-thinbox{display:inline-flex;flex-direction:row;max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline,.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-rule,.katex .katex-underline .underline-line,.katex .mfrac .frac-line{min-height:1px}.katex .mspace{display:inline-block}.katex .katex-smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.katex-inner,.katex .llap>.katex-inner,.katex .rlap>.katex-inner{position:absolute}.katex .clap>.katex-fix,.katex .llap>.katex-fix,.katex .rlap>.katex-fix{display:inline-block}.katex .llap>.katex-inner{right:0}.katex .clap>.katex-inner,.katex .rlap>.katex-inner{left:0}.katex .clap>.katex-inner>span{margin-left:-50%;margin-right:50%}.katex .katex-rule{border:0 solid;display:inline-block;position:relative}.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.katex-root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .katex-sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .katex-sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .katex-sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .katex-sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .katex-sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .katex-sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .katex-sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .katex-sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .katex-sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .katex-sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .katex-sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .katex-sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .katex-sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .katex-sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .katex-sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .katex-sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .katex-sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .katex-sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .katex-sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .katex-sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .katex-sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .katex-sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .katex-sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .katex-sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .katex-sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .katex-sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .katex-sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .katex-sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .katex-sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .katex-sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .katex-sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .katex-sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .katex-sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .katex-sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .katex-sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .katex-sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .katex-sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .katex-sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .katex-sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .katex-sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .katex-sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .katex-sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .katex-sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .katex-sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .katex-sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .katex-sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .katex-sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .katex-sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .katex-sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .katex-sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .katex-sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .katex-sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .katex-sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .katex-sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .katex-sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .katex-sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .katex-sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .katex-sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .katex-sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .katex-sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .katex-sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .katex-sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .katex-sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .katex-sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .katex-sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .katex-sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .katex-sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .katex-sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .katex-sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .katex-sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .katex-sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .katex-sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .katex-sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .katex-sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .katex-sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .katex-sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .katex-sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .katex-sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .katex-sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .katex-sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .katex-sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .katex-sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .katex-sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .katex-sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .katex-sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .katex-sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .katex-sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .katex-sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .katex-sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .katex-sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .katex-sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .katex-sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .katex-sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .katex-sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .katex-sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .katex-sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .katex-sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .katex-sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .katex-sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .katex-sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .katex-sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .katex-sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .katex-sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .katex-sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .katex-sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .katex-sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .katex-sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .katex-sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .katex-sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .katex-sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .katex-sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .katex-sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .katex-sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .katex-sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .katex-sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .katex-sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .katex-sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .katex-sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .katex-sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .katex-sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .katex-sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .katex-accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .katex-accent .accent-body{position:relative}.katex .katex-accent .accent-body:not(.accent-full){width:0}.katex .katex-overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .katex-stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .katex-stretchy:after,.katex .katex-stretchy:before{content:\\\"\\\"}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .katex-sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .reflectbox{display:inline-block;transform:scaleX(-1)}.katex .eqn-num:before{content:\\\"(\\\" counter(katexEqnNo) \\\")\\\";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:\\\"(\\\" counter(mmlEqnNo) \\\")\\\";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.katex-tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.katex-tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}\\n\";\n","/**\n * @ai-matrx/print/labels — the label-sheet engine.\n *\n * ONE module carrying the three matrx-frontend files this lane was extracted\n * from, ported behaviour-verbatim (2026-08-29 state):\n * lib/label-print/label-templates.ts → the template registry\n * lib/label-print/qr-labels-printer.ts → qrLabelsPrinter / printQrLabelSheet\n * lib/label-print/qr-labels-pdf.ts → downloadLabelsPdf\n *\n * Warehouse-grade QR labels on standard 8.5\" × 11\" label stock (Avery and\n * compatibles) AND on continuous/die-cut roll stock. ONE generic grid renderer\n * driven by the LabelTemplate registry — geometry is data, never per-stock CSS.\n *\n * Fidelity rules (the reasons this exists):\n * - The print window is a fresh, unauthenticated document: every QR image is\n * inlined as a data URI, never fetched.\n * - Spec-compliant quiet zone: the `qrcode` lib renders 4 modules of white\n * margin INSIDE each data URI (its default; we pass it explicitly).\n * - `print-color-adjust: exact` on code cells so the browser never \"helps\".\n * - Modules are crisp: each QR is rasterized at 300 dpi for its printed size,\n * and `image-rendering: pixelated` stops resampling blur.\n * - Captions auto-shrink to fit their box (FIT_TEXT pattern) — they never\n * overflow onto the neighboring label.\n * - `startAtLabel` inserts LEADING blank cells so a partially-used sheet can\n * be reused; trailing cells pad out the last page.\n *\n * THE IMAGE RULE — the one deliberate exception to \"data URIs only\": a label's\n * optional `imageUrl` is a DURABLE http(s) URL and is emitted as a plain\n * `<img src>`. Anything that is not http(s) is SKIPPED rather than rendered.\n * The QR itself is never a URL; it is always an inlined data URI.\n *\n * SEAM INVERSIONS versus the matrx-frontend originals (the only changes to the\n * ported behaviour):\n * 1. `@/lib/block-print/block-print-utils` → `./core` (identical export names).\n * 2. The deferred `import(\"qrcode\")` + `QRCode.toDataURL(...)` call is now\n * `generateQrDataUrl` from `./qr`, which performs the same lazy import at\n * call time and maps 1:1 — errorCorrectionLevel → ecLevel, margin →\n * quietZoneModules, width → widthPx. The original also passed\n * `color: { dark: \"#000000\", light: \"#ffffff\" }`, which is byte-for-byte the\n * `qrcode` default, so dropping it changes no output.\n * 3. LP_FIT_SCRIPT is KEPT VERBATIM rather than replaced by core's\n * `buildFitTextScript`. The two scripts are NOT behaviourally identical for\n * label cells: core's steps and writes font sizes in **pt** (and pre-assigns\n * `size + \"pt\"` from a px-derived number before measuring) with a **6pt**\n * floor, while the label script steps and writes **px** with a **4px** floor\n * and no pre-assignment. On a 9pt caption inside a 0.28in box those two\n * produce different final sizes, so swapping them would regress captions on\n * the small Avery stocks.\n *\n * Docs: lib/label-print/FEATURE.md (matrx-frontend), NOTES-CHANGELOG.md.\n */\n\nimport {\n openPrintWindow,\n type BlockPrinter,\n type PrintOutcome,\n type PrintSettings,\n} from \"./core\";\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport { generateQrDataUrl, minQrVersion, qrModuleCount } from \"./qr\";\n\n// ─── Template registry ───────────────────────────────────────────────────────\n\n/**\n * Label-sheet template registry — the geometry source of truth for QR label\n * printing on standard 8.5\" × 11\" label stock (Avery and compatibles) and on\n * roll stock.\n *\n * ONE generic grid renderer is driven entirely by these numbers — never a\n * hand-written CSS block per stock. All dimensions in inches, taken from\n * Avery's published templates:\n * - horizontal pitch = labelWIn + gutterXIn; vertical pitch = labelHIn + gutterYIn\n * - a template is valid when marginLeft*2 + cols*labelW + (cols-1)*gutterX = sheetW\n * (same vertically) — `assertTemplateGeometry` checks this in dev.\n */\nexport interface LabelTemplate {\n id: string;\n name: string;\n /** Stock code as printed on the box (e.g. \"Avery 5160\"). */\n stockCode: string;\n sheetWIn: number;\n sheetHIn: number;\n marginTopIn: number;\n marginLeftIn: number;\n labelWIn: number;\n labelHIn: number;\n cols: number;\n rows: number;\n /** Horizontal gap BETWEEN columns (not the outer margin). */\n gutterXIn: number;\n /** Vertical gap BETWEEN rows. */\n gutterYIn: number;\n /** Corner radius; equal to labelWIn/2 for round labels. */\n cornerRadiusIn?: number;\n /** True round label (die-cut circle) — preview/calibration draw a circle. */\n round?: boolean;\n /**\n * Stock family. Absent means \"sheet\" — a multi-up page of labels. \"roll\" is\n * a continuous/die-cut roll where the sheet IS the label: margins 0, a 1×1\n * grid, and `@page` sized to the single label.\n */\n kind?: \"sheet\" | \"roll\";\n}\n\n/**\n * The registry's first row, hoisted to a named const so the \"no template\n * matched\" fallback can reference it without an unchecked index access.\n *\n * 5160 — 1\" × 2⅝\" address, 30-up (3×10). Top/bottom margin 0.5\",\n * side margin 0.1875\", 0.125\" column gutter, zero row gutter.\n */\nconst AVERY_5160: LabelTemplate = {\n id: \"avery-5160\",\n name: 'Address 1\" × 2⅝\" — 30 per sheet',\n stockCode: \"Avery 5160 / 8160\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.5,\n marginLeftIn: 0.1875,\n labelWIn: 2.625,\n labelHIn: 1,\n cols: 3,\n rows: 10,\n gutterXIn: 0.125,\n gutterYIn: 0,\n cornerRadiusIn: 0.1,\n};\n\n/**\n * Seeded templates. Sheet geometry verified against Avery's published template\n * dimensions (avery.com template pages for each SKU):\n *\n * 5160 — see AVERY_5160 above.\n * 5163 — 2\" × 4\" shipping, 10-up (2×5). Top/bottom 0.5\", side 0.15625\",\n * 0.1875\" column gutter, zero row gutter.\n * 5164 — 3⅓\" × 4\" shipping, 6-up (2×3). Same horizontal grid as 5163.\n * 22806 — 2\" × 2\" rounded square, 12-up (3×4). Margins 0.5\" sides /\n * 0.75\" top, 0.75\" column gutter, 0.5\" row gutter — i.e. Avery's\n * published 2.75\" horizontal pitch and 2.5\" vertical pitch. (The\n * matrx-frontend original had 0.375\"/0.375\" here and tiled only 7.5\"\n * of the 8.5\" sheet; see NOTES-CHANGELOG.md.)\n * 22807 — 2\" round, 12-up (3×4). Same grid as 22806, circular die-cut.\n *\n * Roll stock (`kind: \"roll\"`) — the sheet IS the label, so every roll row is a\n * 1×1 grid with zero margins and zero gutters, and its die-cut size is given\n * as width × height in the feed orientation:\n *\n * DK-1202 — Brother die-cut shipping, 62mm × 100mm = 2.4\" × 3.9\".\n * DK-11201 — Brother die-cut address, 29mm × 90mm = 1.14\" × 3.5\".\n * 30252 — DYMO address, 1⅛\" × 3½\".\n * 4x6 — the generic 4\" × 6\" thermal shipping label.\n */\nexport const LABEL_TEMPLATES: readonly LabelTemplate[] = [\n AVERY_5160,\n {\n id: \"avery-5163\",\n name: 'Shipping 2\" × 4\" — 10 per sheet',\n stockCode: \"Avery 5163 / 8163\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.5,\n marginLeftIn: 0.15625,\n labelWIn: 4,\n labelHIn: 2,\n cols: 2,\n rows: 5,\n gutterXIn: 0.1875,\n gutterYIn: 0,\n cornerRadiusIn: 0.125,\n },\n {\n id: \"avery-5164\",\n name: 'Shipping 3⅓\" × 4\" — 6 per sheet',\n stockCode: \"Avery 5164 / 8164\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.5,\n marginLeftIn: 0.15625,\n labelWIn: 4,\n labelHIn: 3.3333333,\n cols: 2,\n rows: 3,\n gutterXIn: 0.1875,\n gutterYIn: 0,\n cornerRadiusIn: 0.125,\n },\n {\n id: \"avery-22806\",\n name: 'Square 2\" × 2\" — 12 per sheet',\n stockCode: \"Avery 22806\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.75,\n marginLeftIn: 0.5,\n labelWIn: 2,\n labelHIn: 2,\n cols: 3,\n rows: 4,\n gutterXIn: 0.75,\n gutterYIn: 0.5,\n cornerRadiusIn: 0.09375,\n },\n {\n id: \"avery-22807\",\n name: 'Round 2\" — 12 per sheet',\n stockCode: \"Avery 22807\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.75,\n marginLeftIn: 0.5,\n labelWIn: 2,\n labelHIn: 2,\n cols: 3,\n rows: 4,\n gutterXIn: 0.75,\n gutterYIn: 0.5,\n cornerRadiusIn: 1,\n round: true,\n },\n {\n id: \"brother-dk-1202\",\n name: 'Shipping roll 2.4\" × 3.9\" — die-cut',\n stockCode: \"Brother DK-1202\",\n sheetWIn: 2.4,\n sheetHIn: 3.9,\n marginTopIn: 0,\n marginLeftIn: 0,\n labelWIn: 2.4,\n labelHIn: 3.9,\n cols: 1,\n rows: 1,\n gutterXIn: 0,\n gutterYIn: 0,\n kind: \"roll\",\n },\n {\n id: \"brother-dk-11201\",\n name: 'Address roll 1.14\" × 3.5\" — die-cut',\n stockCode: \"Brother DK-11201\",\n sheetWIn: 1.14,\n sheetHIn: 3.5,\n marginTopIn: 0,\n marginLeftIn: 0,\n labelWIn: 1.14,\n labelHIn: 3.5,\n cols: 1,\n rows: 1,\n gutterXIn: 0,\n gutterYIn: 0,\n kind: \"roll\",\n },\n {\n id: \"dymo-30252\",\n name: 'Address roll 1⅛\" × 3½\"',\n stockCode: \"DYMO 30252\",\n sheetWIn: 1.125,\n sheetHIn: 3.5,\n marginTopIn: 0,\n marginLeftIn: 0,\n labelWIn: 1.125,\n labelHIn: 3.5,\n cols: 1,\n rows: 1,\n gutterXIn: 0,\n gutterYIn: 0,\n kind: \"roll\",\n },\n {\n id: \"roll-4x6\",\n name: 'Shipping roll 4\" × 6\"',\n stockCode: \"Thermal 4x6\",\n sheetWIn: 4,\n sheetHIn: 6,\n marginTopIn: 0,\n marginLeftIn: 0,\n labelWIn: 4,\n labelHIn: 6,\n cols: 1,\n rows: 1,\n gutterXIn: 0,\n gutterYIn: 0,\n kind: \"roll\",\n },\n];\n\nexport function getLabelTemplate(id: string): LabelTemplate | undefined {\n return LABEL_TEMPLATES.find((t) => t.id === id);\n}\n\n/**\n * Build a `custom` template for stock not in the registry. Callers supply the\n * full geometry; id is fixed to \"custom\".\n *\n * The geometry spread comes FIRST so an explicitly-present `name: undefined` /\n * `stockCode: undefined` cannot overwrite the resolved default (see\n * NOTES-CHANGELOG.md).\n */\nexport function customLabelTemplate(\n geometry: Omit<LabelTemplate, \"id\" | \"name\" | \"stockCode\"> &\n Partial<Pick<LabelTemplate, \"name\" | \"stockCode\">>,\n): LabelTemplate {\n return {\n ...geometry,\n id: \"custom\",\n name: geometry.name ?? \"Custom stock\",\n stockCode: geometry.stockCode ?? \"custom\",\n };\n}\n\n/** Dev-time sanity check: the grid must tile the sheet exactly (±1/64\"). */\nexport function assertTemplateGeometry(t: LabelTemplate): void {\n const w =\n t.marginLeftIn * 2 + t.cols * t.labelWIn + (t.cols - 1) * t.gutterXIn;\n const h =\n t.marginTopIn * 2 + t.rows * t.labelHIn + (t.rows - 1) * t.gutterYIn;\n const tol = 1 / 64;\n if (Math.abs(w - t.sheetWIn) > tol || Math.abs(h - t.sheetHIn) > tol) {\n console.error(\n `[label-print] Template ${t.id} does not tile its sheet: grid ${w.toFixed(4)}×${h.toFixed(4)}in vs sheet ${t.sheetWIn}×${t.sheetHIn}in`,\n );\n }\n}\n\n// Pure math, no side effects beyond a console.error on drift — the module\n// stays import-inert. `process` is guarded because this ships as an npm\n// package that runs in browsers, where a bare `process.env` read throws.\nif (\n typeof process === \"undefined\" ||\n process.env.NODE_ENV !== \"production\"\n) {\n LABEL_TEMPLATES.forEach(assertTemplateGeometry);\n}\n\n// ─── Data contract ───────────────────────────────────────────────────────────\n\nexport interface QrLabel {\n /** The value encoded in the QR code (SKU, URL, id…). */\n qrValue: string;\n /** Bold headline under/next to the code. Defaults to qrValue when captions are on. */\n caption?: string;\n /** Additional small text lines (wide labels only — there is no room on tiny stock). */\n lines?: string[];\n /**\n * A single short, LARGE, bold token (a garment size, a bin letter, a grade).\n * Rendered in the caption zone ABOVE the caption at ~2× the caption size and\n * auto-shrunk by the fit script.\n */\n badge?: string;\n /**\n * Durable http(s) image URL rendered in the lines zone with\n * `object-fit: contain`. Anything that is not http(s) is skipped — THE IMAGE\n * RULE. Ignored by the PDF lane, which cannot fetch a remote image.\n */\n imageUrl?: string;\n}\n\nexport interface QrLabelPrintData {\n labels: QrLabel[];\n /** Default template; the dialog's variant picker overrides it. */\n templateId: string;\n /** Optional custom-stock geometry used when templateId === \"custom\". */\n customTemplate?: LabelTemplate;\n}\n\nexport type QrEcLevel = \"L\" | \"M\" | \"Q\";\n\nexport interface QrLabelSettings {\n startAtLabel: number; // 1-based first cell to print on\n copiesPerCode: number;\n showCaption: boolean;\n showLines: boolean;\n rangeFrom: number; // 1-based, 0 = no bound\n rangeTo: number;\n ecLevel: QrEcLevel;\n calibrationPage: boolean;\n}\n\nconst DEFAULTS: QrLabelSettings = {\n startAtLabel: 1,\n copiesPerCode: 1,\n showCaption: true,\n showLines: true,\n rangeFrom: 0,\n rangeTo: 0,\n ecLevel: \"M\",\n calibrationPage: false,\n};\n\nfunction resolveSettings(s?: PrintSettings): QrLabelSettings {\n const num = (v: unknown, d: number, min = 0) =>\n typeof v === \"number\" && !Number.isNaN(v) ? Math.max(min, v) : d;\n const ec = s?.ecLevel;\n return {\n startAtLabel: num(s?.startAtLabel, DEFAULTS.startAtLabel, 1),\n copiesPerCode: num(s?.copiesPerCode, DEFAULTS.copiesPerCode, 1),\n showCaption: (s?.showCaption ?? DEFAULTS.showCaption) as boolean,\n showLines: (s?.showLines ?? DEFAULTS.showLines) as boolean,\n rangeFrom: num(s?.rangeFrom, 0),\n rangeTo: num(s?.rangeTo, 0),\n ecLevel: ec === \"L\" || ec === \"Q\" ? ec : \"M\",\n calibrationPage: (s?.calibrationPage ?? false) as boolean,\n };\n}\n\n// ─── QR generation (data URIs, inlined) ──────────────────────────────────────\n\nconst PRINT_DPI = 300;\n/** Quiet zone: QR spec requires ≥4 modules of clear margin around the symbol. */\nconst QUIET_ZONE_MODULES = 4;\n\nexport async function generateQrDataUri(\n value: string,\n ecLevel: QrEcLevel,\n sizeInches: number,\n): Promise<string> {\n // Deferred to call time (user click) — `./qr` lazy-imports `qrcode` inside\n // generateQrDataUrl, so this module never puts it on a shell graph.\n return generateQrDataUrl(value, {\n ecLevel,\n quietZoneModules: QUIET_ZONE_MODULES,\n widthPx: Math.round(sizeInches * PRINT_DPI),\n });\n}\n\n// ─── Cell geometry ───────────────────────────────────────────────────────────\n\ninterface CellLayout {\n /** true = QR left, text right; false = QR on top, caption below. */\n wide: boolean;\n qrIn: number;\n padIn: number;\n /** Height reserved above the caption for a `badge`; 0 when there is none. */\n badgeZoneIn: number;\n}\n\nexport function computeCellLayout(\n t: LabelTemplate,\n showCaption: boolean,\n hasBadge = false,\n): CellLayout {\n const padIn = Math.min(0.08, t.labelHIn * 0.06);\n const wide = t.labelWIn / t.labelHIn >= 1.8;\n const badgeZoneIn = hasBadge ? Math.min(0.4, t.labelHIn * 0.22) : 0;\n if (wide) {\n return { wide, qrIn: t.labelHIn - padIn * 2, padIn, badgeZoneIn };\n }\n // Stacked. Round labels inscribe the content square inside the circle.\n const inset = t.round ? t.labelWIn * 0.15 : 0;\n const captionZone = showCaption ? Math.min(0.28, t.labelHIn * 0.16) : 0;\n const qrIn = Math.min(\n t.labelWIn - padIn * 2 - inset * 2,\n t.labelHIn - padIn * 2 - inset * 2 - captionZone - badgeZoneIn,\n );\n return { wide, qrIn, padIn, badgeZoneIn };\n}\n\n// ─── Generic grid renderer ───────────────────────────────────────────────────\n\nfunction sheetStyles(t: LabelTemplate): string {\n const radius = t.round\n ? `${t.labelWIn / 2}in`\n : t.cornerRadiusIn\n ? `${t.cornerRadiusIn}in`\n : \"0\";\n return `\n @page { size: ${t.sheetWIn}in ${t.sheetHIn}in; margin: 0; }\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n html, body { width: ${t.sheetWIn}in; background: #fff; color: #000;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif; }\n\n .lp-page {\n width: ${t.sheetWIn}in; height: ${t.sheetHIn}in;\n position: relative; overflow: hidden;\n page-break-after: always; break-after: page;\n border-bottom: 1px solid #e2e8f0; /* screen-only separator */\n }\n .lp-page:last-child { page-break-after: auto; break-after: auto; border-bottom: none; }\n\n .lp-cell {\n position: absolute;\n width: ${t.labelWIn}in; height: ${t.labelHIn}in;\n border-radius: ${radius};\n overflow: hidden;\n -webkit-print-color-adjust: exact; print-color-adjust: exact;\n }\n .lp-qr {\n display: block;\n image-rendering: pixelated; /* no resampling blur on crisp modules */\n }\n .lp-cell-wide { display: flex; align-items: center; }\n .lp-cell-stacked { display: flex; flex-direction: column; align-items: center; justify-content: center; }\n\n .lp-text { flex: 1 1 auto; min-width: 0; overflow: hidden; align-self: stretch;\n display: flex; flex-direction: column; justify-content: center; }\n .lp-fit-wrap { overflow: hidden; }\n .lp-caption { font-weight: 700; line-height: 1.15; word-break: break-word; }\n .lp-line { line-height: 1.25; word-break: break-word; }\n .lp-caption-stacked { text-align: center; width: 100%; }\n\n /* Badge — the LARGE token above the caption (garment size, bin letter). */\n .lp-badge { font-weight: 800; line-height: 1.05; word-break: break-word;\n -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n .lp-badge-stacked { text-align: center; width: 100%; }\n\n /* Durable http(s) artwork in the lines zone. */\n .lp-img { display: block; width: 100%; object-fit: contain; }\n\n /* Calibration */\n .lp-cal-cell { border: 0.5pt dashed #94a3b8; background: transparent; }\n .lp-cal-label { position: absolute; inset: 0; display: flex; align-items: center;\n justify-content: center; font-size: 7pt; color: #94a3b8; text-align: center; padding: 4px; }\n .lp-crop { position: absolute; background: #94a3b8;\n -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n\n @media print {\n html, body { height: auto; overflow: visible; }\n .lp-page { border-bottom: none; }\n .screen-only { display: none !important; }\n }\n`;\n}\n\nfunction cellPosition(t: LabelTemplate, index: number): { x: number; y: number } {\n const col = index % t.cols;\n const row = Math.floor(index / t.cols);\n return {\n x: t.marginLeftIn + col * (t.labelWIn + t.gutterXIn),\n y: t.marginTopIn + row * (t.labelHIn + t.gutterYIn),\n };\n}\n\n/**\n * Caption auto-shrink — same pattern as the flashcards FIT_TEXT_SCRIPT.\n *\n * Kept verbatim instead of core's `buildFitTextScript`: this loop measures and\n * writes **px** with a 4px floor, core's writes **pt** with a 6pt floor and\n * pre-assigns the size before measuring. See the module header.\n */\nconst LP_FIT_SCRIPT = `\n<script>\n(function () {\n function fit(el, box) {\n var size = parseFloat(window.getComputedStyle(el).fontSize);\n var min = 4;\n while (size > min && (el.scrollWidth > box.clientWidth + 1 || el.scrollHeight > box.clientHeight + 1)) {\n size -= 0.5;\n el.style.fontSize = size + \"px\";\n }\n }\n function run() {\n var els = document.querySelectorAll(\".lp-fit-wrap .lp-fit\");\n for (var i = 0; i < els.length; i++) fit(els[i], els[i].parentElement);\n }\n if (document.readyState === \"loading\") document.addEventListener(\"DOMContentLoaded\", run);\n else setTimeout(run, 0);\n})();\n</script>`;\n\ninterface RenderedCell {\n html: string; // inner HTML of the cell, \"\" for a blank pad cell\n}\n\n/** THE IMAGE RULE: only a durable http(s) URL is ever rendered. */\nfunction isHttpUrl(value: string | undefined): value is string {\n return typeof value === \"string\" && /^https?:\\/\\//i.test(value.trim());\n}\n\nfunction buildLabelCellHtml(\n label: QrLabel,\n qrDataUri: string,\n t: LabelTemplate,\n layout: CellLayout,\n s: QrLabelSettings,\n): string {\n const qr = qrDataUri\n ? `<img class=\"lp-qr\" src=\"${qrDataUri}\" alt=\"\" style=\"width:${layout.qrIn}in;height:${layout.qrIn}in;\" />`\n : \"\";\n const captionText = s.showCaption ? (label.caption ?? label.qrValue) : \"\";\n const badgeText = label.badge?.trim() ? label.badge.trim() : \"\";\n\n if (layout.wide) {\n const lines =\n s.showLines && label.lines?.length\n ? label.lines\n .filter((l) => l.trim())\n .map(\n (l) =>\n `<div class=\"lp-line\" style=\"font-size:8pt;\">${escapeHtml(l)}</div>`,\n )\n .join(\"\")\n : \"\";\n const caption = captionText\n ? `<div class=\"lp-fit-wrap\" style=\"max-height:${(t.labelHIn * 0.3).toFixed(3)}in;\"><div class=\"lp-fit lp-caption\" style=\"font-size:12pt;\">${escapeHtml(captionText)}</div></div>`\n : \"\";\n const badge = badgeText\n ? `<div class=\"lp-fit-wrap\" style=\"max-height:${layout.badgeZoneIn.toFixed(3)}in;\"><div class=\"lp-fit lp-badge\" style=\"font-size:24pt;\">${escapeHtml(badgeText)}</div></div>`\n : \"\";\n const image = isHttpUrl(label.imageUrl)\n ? `<img class=\"lp-img\" src=\"${escapeHtml(label.imageUrl)}\" alt=\"\" style=\"height:${(t.labelHIn * 0.35).toFixed(3)}in;\" />`\n : \"\";\n return `<div class=\"lp-cell-wide\" style=\"height:100%;padding:${layout.padIn}in;gap:${layout.padIn}in;\">\n ${qr}\n <div class=\"lp-text\">${badge}${caption}${lines}${image}</div>\n </div>`;\n }\n\n const caption = captionText\n ? `<div class=\"lp-fit-wrap lp-caption-stacked\" style=\"height:${Math.min(0.28, t.labelHIn * 0.16).toFixed(3)}in;\"><div class=\"lp-fit lp-caption\" style=\"font-size:9pt;\">${escapeHtml(captionText)}</div></div>`\n : \"\";\n const badge = badgeText\n ? `<div class=\"lp-fit-wrap lp-badge-stacked\" style=\"height:${layout.badgeZoneIn.toFixed(3)}in;\"><div class=\"lp-fit lp-badge\" style=\"font-size:18pt;\">${escapeHtml(badgeText)}</div></div>`\n : \"\";\n return `<div class=\"lp-cell-stacked\" style=\"height:100%;padding:${layout.padIn}in;\">\n ${qr}\n ${badge}\n ${caption}\n </div>`;\n}\n\nfunction renderPages(t: LabelTemplate, cells: RenderedCell[]): string {\n const perPage = t.cols * t.rows;\n const pages: string[] = [];\n for (let p = 0; p < Math.max(1, Math.ceil(cells.length / perPage)); p++) {\n const batch = cells.slice(p * perPage, (p + 1) * perPage);\n const cellHtml = batch\n .map((c, i) => {\n const { x, y } = cellPosition(t, i);\n return `<div class=\"lp-cell\" style=\"left:${x}in;top:${y}in;\">${c.html}</div>`;\n })\n .join(\"\\n\");\n pages.push(`<div class=\"lp-page\">${cellHtml}</div>`);\n }\n return pages.join(\"\\n\");\n}\n\nfunction openSheetWindow(\n t: LabelTemplate,\n bodyHtml: string,\n title: string,\n withFitScript: boolean,\n warning?: string,\n): PrintOutcome {\n const warningHtml = warning\n ? `<div class=\"screen-only lp-warning\" style=\"font-family:sans-serif;font-size:12px;font-weight:600;padding:9px 16px;background:#fef2f2;color:#991b1b;border-bottom:1px solid #fecaca;\">${escapeHtml(warning)}</div>`\n : \"\";\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>${escapeHtml(title)} — ${escapeHtml(t.stockCode)}</title>\n <style>${sheetStyles(t)}</style>\n</head>\n<body>\n${warningHtml}\n<div class=\"screen-only\" style=\"font-family:sans-serif;font-size:12px;padding:10px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0;display:flex;gap:10px;align-items:center;flex-wrap:wrap;\">\n <button onclick=\"window.print()\" style=\"padding:7px 18px;background:#111;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">Print / Save PDF</button>\n <button onclick=\"window.close()\" style=\"padding:7px 14px;background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px;font-size:13px;cursor:pointer;\">Close</button>\n <span style=\"color:#64748b;font-size:11px;\">${escapeHtml(t.stockCode)} · ${escapeHtml(t.name)} · set printer to <strong>100% scale (no \"fit to page\"), no margins, Portrait</strong></span>\n</div>\n${bodyHtml}\n${withFitScript ? LP_FIT_SCRIPT : \"\"}\n</body>\n</html>`;\n return openPrintWindow(html, title);\n}\n\n// ─── Label selection (range → copies → startAt offset) ──────────────────────\n\nexport function expandLabels(labels: QrLabel[], s: QrLabelSettings): QrLabel[] {\n const from = s.rangeFrom > 0 ? s.rangeFrom : 1;\n const to = s.rangeTo > 0 ? Math.min(s.rangeTo, labels.length) : labels.length;\n const subset = labels.slice(from - 1, to);\n if (s.copiesPerCode <= 1) return subset;\n const out: QrLabel[] = [];\n for (const l of subset)\n for (let i = 0; i < s.copiesPerCode; i++) out.push(l);\n return out;\n}\n\n// ─── Calibration test page ───────────────────────────────────────────────────\n\nfunction buildCalibrationBody(t: LabelTemplate): string {\n const perPage = t.cols * t.rows;\n const cells: string[] = [];\n for (let i = 0; i < perPage; i++) {\n const { x, y } = cellPosition(t, i);\n cells.push(\n `<div class=\"lp-cell lp-cal-cell\" style=\"left:${x}in;top:${y}in;\"><div class=\"lp-cal-label\">${i + 1}</div></div>`,\n );\n }\n // Crop marks at the four corners of the label grid area\n const gridR = t.sheetWIn - t.marginLeftIn;\n const gridB = t.sheetHIn - t.marginTopIn;\n const mark = (x: number, y: number, h: boolean) =>\n `<div class=\"lp-crop\" style=\"left:${x}in;top:${y}in;width:${h ? 0.25 : 0.0104}in;height:${h ? 0.0104 : 0.25}in;\"></div>`;\n const marks = [\n mark(t.marginLeftIn - 0.25, t.marginTopIn, true),\n mark(t.marginLeftIn, t.marginTopIn - 0.25, false),\n mark(gridR, t.marginTopIn, true),\n mark(gridR - 0.0104, t.marginTopIn - 0.25, false),\n mark(t.marginLeftIn - 0.25, gridB - 0.0104, true),\n mark(t.marginLeftIn, gridB, false),\n mark(gridR, gridB - 0.0104, true),\n mark(gridR - 0.0104, gridB, false),\n ].join(\"\");\n const banner = `<div style=\"position:absolute;left:0;right:0;top:${Math.max(0, t.marginTopIn - 0.3).toFixed(2)}in;text-align:center;font-size:7pt;color:#94a3b8;\">Calibration — ${escapeHtml(t.stockCode)} · hold against your label sheet: every outline must sit on a label</div>`;\n return `<div class=\"lp-page\">${banner}${marks}${cells.join(\"\\n\")}</div>`;\n}\n\n/** Print the grid outline + crop marks only — verify alignment before burning labels. */\nexport function printCalibrationSheet(template: LabelTemplate): PrintOutcome {\n return openSheetWindow(\n template,\n buildCalibrationBody(template),\n \"Label calibration\",\n false,\n );\n}\n\n// ─── Scannability ────────────────────────────────────────────────────────────\n\nconst MM_PER_IN = 25.4;\n\n/**\n * The printed-module floor a 300 dpi thermal printer and a warehouse scanner\n * can both resolve. Below this a symbol prints, looks fine on screen, and\n * refuses to scan on the dock.\n */\nexport const MIN_SCANNABLE_MODULE_MM = 0.4;\n\nexport interface ScannabilityReport {\n ok: boolean;\n version: number;\n moduleSizeMm: number;\n minModuleSizeMm: number;\n message?: string;\n}\n\n/**\n * Will a payload of `payloadByteLen` bytes actually scan off this stock?\n *\n * The printed side comes from the SAME layout brain the printer uses\n * (`computeCellLayout`), evaluated with captions ON — the smaller, and\n * therefore conservative, QR box. Module size divides that side by the symbol's\n * modules PLUS the two 4-module quiet zones the data URI carries inside it.\n *\n * Never throws: an over-capacity payload comes back as `ok: false` with\n * `version: 0` and the capacity error as the message.\n */\nexport function assertScannable(\n template: LabelTemplate,\n payloadByteLen: number,\n ecLevel: QrEcLevel = DEFAULTS.ecLevel,\n): ScannabilityReport {\n const layout = computeCellLayout(template, true);\n const printedSideMm = layout.qrIn * MM_PER_IN;\n\n let version: number;\n try {\n version = minQrVersion(payloadByteLen, ecLevel);\n } catch (err) {\n return {\n ok: false,\n version: 0,\n moduleSizeMm: 0,\n minModuleSizeMm: MIN_SCANNABLE_MODULE_MM,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n\n const modulesPerSide = qrModuleCount(version) + QUIET_ZONE_MODULES * 2;\n const moduleSizeMm = printedSideMm / modulesPerSide;\n if (moduleSizeMm >= MIN_SCANNABLE_MODULE_MM) {\n return {\n ok: true,\n version,\n moduleSizeMm,\n minModuleSizeMm: MIN_SCANNABLE_MODULE_MM,\n };\n }\n return {\n ok: false,\n version,\n moduleSizeMm,\n minModuleSizeMm: MIN_SCANNABLE_MODULE_MM,\n message:\n `A ${payloadByteLen}-byte payload needs a version ${version} QR, which prints at ` +\n `${moduleSizeMm.toFixed(3)}mm per module on ${template.stockCode} — below the ` +\n `${MIN_SCANNABLE_MODULE_MM}mm floor a scanner can resolve. Shorten the payload ` +\n `(a short resolver URL is the intended fix) or move to larger label stock.`,\n };\n}\n\n/** The batch's worst case: the longest payload decides whether we warn. */\nfunction batchScannabilityWarning(\n t: LabelTemplate,\n labels: readonly QrLabel[],\n ecLevel: QrEcLevel,\n): string | undefined {\n const encoder = new TextEncoder();\n let worst = -1;\n for (const l of labels) {\n if (!l.qrValue) continue;\n const len = encoder.encode(l.qrValue).length;\n if (len > worst) worst = len;\n }\n if (worst < 0) return undefined;\n const report = assertScannable(t, worst, ecLevel);\n return report.ok ? undefined : report.message;\n}\n\n// ─── The printer ─────────────────────────────────────────────────────────────\n\nfunction resolveTemplate(\n data: QrLabelPrintData,\n variantId?: string,\n): LabelTemplate {\n if (variantId && variantId !== \"custom\") {\n const t = getLabelTemplate(variantId);\n if (t) return t;\n }\n if (data.templateId === \"custom\" && data.customTemplate)\n return data.customTemplate;\n return (\n getLabelTemplate(data.templateId) ?? data.customTemplate ?? AVERY_5160\n );\n}\n\n/** Core render path — also used by the demo page's direct print entry point. */\nexport async function printQrLabelSheet(\n data: QrLabelPrintData,\n variantId?: string,\n rawSettings?: PrintSettings,\n): Promise<PrintOutcome> {\n const t = resolveTemplate(data, variantId);\n const s = resolveSettings(rawSettings);\n\n if (s.calibrationPage) {\n return printCalibrationSheet(t);\n }\n\n const labels = expandLabels(data.labels ?? [], s);\n if (!labels.length) {\n return openSheetWindow(\n t,\n `<div class=\"lp-page\"><div style=\"position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#94a3b8;font-size:11pt;\">No labels to print.</div></div>`,\n \"QR labels\",\n false,\n );\n }\n\n const hasBadge = labels.some((l) => (l.badge ?? \"\").trim() !== \"\");\n const layout = computeCellLayout(t, s.showCaption, hasBadge);\n\n // Generate every data URI up front (dedup identical values). A label with no\n // qrValue (a text-only LabelFormat) simply carries no code.\n const uriByValue = new Map<string, string>();\n for (const l of labels) {\n if (l.qrValue && !uriByValue.has(l.qrValue)) {\n uriByValue.set(\n l.qrValue,\n await generateQrDataUri(l.qrValue, s.ecLevel, layout.qrIn),\n );\n }\n }\n\n // Leading blank cells for a partially-used sheet, then labels, then pad.\n const leadingBlanks = Math.max(0, s.startAtLabel - 1) % (t.cols * t.rows);\n const cells: RenderedCell[] = [\n ...Array.from({ length: leadingBlanks }, () => ({ html: \"\" })),\n ...labels.map((l) => ({\n html: buildLabelCellHtml(\n l,\n uriByValue.get(l.qrValue) ?? \"\",\n t,\n layout,\n s,\n ),\n })),\n ];\n const perPage = t.cols * t.rows;\n const tail = cells.length % perPage;\n if (tail > 0)\n for (let i = 0; i < perPage - tail; i++) cells.push({ html: \"\" });\n\n return openSheetWindow(\n t,\n renderPages(t, cells),\n \"QR labels\",\n true,\n batchScannabilityWarning(t, labels, s.ecLevel),\n );\n}\n\nexport const qrLabelsPrinter: BlockPrinter = {\n label: \"Print QR label sheet\",\n variants: LABEL_TEMPLATES.map((t) => ({\n id: t.id,\n label: t.name,\n description: t.stockCode,\n })),\n settings: [\n {\n type: \"number\",\n id: \"startAtLabel\",\n label: \"Start at label #\",\n description: \"Skip already-used positions on a partial sheet\",\n defaultValue: 1,\n min: 1,\n },\n {\n type: \"number\",\n id: \"copiesPerCode\",\n label: \"Copies per code\",\n defaultValue: 1,\n min: 1,\n max: 999,\n },\n {\n type: \"boolean\",\n id: \"showCaption\",\n label: \"Show caption\",\n description: \"Print the caption (or the code value) beside each QR\",\n defaultValue: true,\n },\n {\n type: \"boolean\",\n id: \"showLines\",\n label: \"Show detail lines\",\n description: \"Extra text lines on wide labels\",\n defaultValue: true,\n },\n {\n type: \"range\",\n id: \"labelRange\",\n label: \"Label range\",\n description: \"Print only labels N to M (blank = all)\",\n fromId: \"rangeFrom\",\n toId: \"rangeTo\",\n defaultFrom: 0,\n defaultTo: 0,\n },\n {\n type: \"select\",\n id: \"ecLevel\",\n label: \"Error correction\",\n description: \"Higher survives more damage; M is the warehouse default\",\n options: [\n { value: \"L\", label: \"L\" },\n { value: \"M\", label: \"M\" },\n { value: \"Q\", label: \"Q\" },\n ],\n defaultValue: \"M\",\n },\n {\n type: \"boolean\",\n id: \"calibrationPage\",\n label: \"Calibration test page\",\n description: \"Print outlines + crop marks only to verify alignment\",\n defaultValue: false,\n },\n ],\n print: (data, variantId, settings) =>\n printQrLabelSheet(data as QrLabelPrintData, variantId, settings),\n};\n\n// ─── PDF lane ────────────────────────────────────────────────────────────────\n\n/**\n * downloadLabelsPdf() renders the same registry-driven grid as the printer\n * straight into a jsPDF file, for users who want a file instead of the browser\n * print dialog.\n *\n * jspdf is imported dynamically INSIDE the function body (code-splitting rule 6\n * — click-time machinery never rides a static edge).\n */\n\nconst PT_PER_IN = 72;\n\nfunction resolvePdfSettings(s?: PrintSettings): QrLabelSettings {\n const num = (v: unknown, d: number, min = 0) =>\n typeof v === \"number\" && !Number.isNaN(v) ? Math.max(min, v) : d;\n const ec = s?.ecLevel;\n return {\n startAtLabel: num(s?.startAtLabel, 1, 1),\n copiesPerCode: num(s?.copiesPerCode, 1, 1),\n showCaption: (s?.showCaption ?? true) as boolean,\n showLines: (s?.showLines ?? true) as boolean,\n rangeFrom: num(s?.rangeFrom, 0),\n rangeTo: num(s?.rangeTo, 0),\n ecLevel: ec === \"L\" || ec === \"Q\" ? ec : \"M\",\n calibrationPage: false,\n };\n}\n\n/** jsPDF's splitTextToSize is loosely typed; take the first line safely. */\nfunction firstLine(parts: unknown): string {\n if (Array.isArray(parts)) {\n const head: unknown = parts[0];\n return typeof head === \"string\" ? head : \"\";\n }\n return typeof parts === \"string\" ? parts : \"\";\n}\n\n/**\n * Generate a PDF of QR labels for the given template and hand it to the\n * browser as a download. Same data seam as the printer: `{ labels, templateId }`.\n */\nexport async function downloadLabelsPdf(\n data: QrLabelPrintData,\n templateId?: string,\n rawSettings?: PrintSettings,\n filename = \"qr-labels.pdf\",\n): Promise<void> {\n const t =\n (templateId === \"custom\" || data.templateId === \"custom\"\n ? data.customTemplate\n : undefined) ??\n getLabelTemplate(templateId ?? data.templateId) ??\n AVERY_5160;\n const s = resolvePdfSettings(rawSettings);\n const labels = expandLabels(data.labels ?? [], s);\n if (!labels.length) return;\n\n // Never blocks the download — the PDF lane has no screen-only header to\n // carry the banner, so an unscannable batch screams on the console instead.\n const warning = batchScannabilityWarning(t, labels, s.ecLevel);\n if (warning !== undefined) console.warn(`[label-print] ${warning}`);\n\n const { jsPDF } = await import(\"jspdf\");\n const doc = new jsPDF({\n unit: \"pt\",\n format: [t.sheetWIn * PT_PER_IN, t.sheetHIn * PT_PER_IN],\n });\n\n const hasBadge = labels.some((l) => (l.badge ?? \"\").trim() !== \"\");\n const layout = computeCellLayout(t, s.showCaption, hasBadge);\n const qrPt = layout.qrIn * PT_PER_IN;\n const padPt = layout.padIn * PT_PER_IN;\n const perPage = t.cols * t.rows;\n const leadingBlanks = Math.max(0, s.startAtLabel - 1) % perPage;\n\n const uriByValue = new Map<string, string>();\n for (const l of labels) {\n if (l.qrValue && !uriByValue.has(l.qrValue)) {\n uriByValue.set(\n l.qrValue,\n await generateQrDataUri(l.qrValue, s.ecLevel, layout.qrIn),\n );\n }\n }\n\n for (const [i, label] of labels.entries()) {\n const cellIndex = i + leadingBlanks;\n if (cellIndex > 0 && cellIndex % perPage === 0) doc.addPage();\n const within = cellIndex % perPage;\n const col = within % t.cols;\n const row = Math.floor(within / t.cols);\n const x =\n (t.marginLeftIn + col * (t.labelWIn + t.gutterXIn)) * PT_PER_IN;\n const y = (t.marginTopIn + row * (t.labelHIn + t.gutterYIn)) * PT_PER_IN;\n const caption = s.showCaption ? (label.caption ?? label.qrValue) : \"\";\n const badge = label.badge?.trim() ? label.badge.trim() : \"\";\n const uri = uriByValue.get(label.qrValue) ?? \"\";\n\n if (layout.wide) {\n // QR left, text right\n const qrY = y + (t.labelHIn * PT_PER_IN - qrPt) / 2;\n if (uri) doc.addImage(uri, \"PNG\", x + padPt, qrY, qrPt, qrPt);\n const textX = x + padPt + qrPt + padPt;\n const textMaxW = t.labelWIn * PT_PER_IN - (textX - x) - padPt;\n let cursorY = y + padPt + 12;\n if (badge) {\n doc.setFont(\"helvetica\", \"bold\");\n doc.setFontSize(24);\n cursorY = y + padPt + 22;\n doc.text(firstLine(doc.splitTextToSize(badge, textMaxW)), textX, cursorY);\n cursorY += 16;\n }\n if (caption) {\n doc.setFont(\"helvetica\", \"bold\");\n doc.setFontSize(12);\n doc.text(firstLine(doc.splitTextToSize(caption, textMaxW)), textX, cursorY);\n cursorY += 14;\n }\n if (s.showLines && label.lines?.length) {\n doc.setFont(\"helvetica\", \"normal\");\n doc.setFontSize(8);\n for (const line of label.lines.filter((l) => l.trim())) {\n if (cursorY > y + t.labelHIn * PT_PER_IN - padPt) break;\n doc.text(firstLine(doc.splitTextToSize(line, textMaxW)), textX, cursorY);\n cursorY += 10;\n }\n }\n } else {\n // Stacked, centered\n const cx = x + t.labelWIn * PT_PER_IN / 2;\n const contentH = qrPt + (caption ? 12 : 0) + (badge ? 22 : 0);\n const qrY = y + (t.labelHIn * PT_PER_IN - contentH) / 2;\n if (uri) doc.addImage(uri, \"PNG\", cx - qrPt / 2, qrY, qrPt, qrPt);\n const maxW = (t.labelWIn - layout.padIn * 2) * PT_PER_IN;\n let textY = qrY + qrPt;\n if (badge) {\n doc.setFont(\"helvetica\", \"bold\");\n doc.setFontSize(18);\n textY += 18;\n doc.text(firstLine(doc.splitTextToSize(badge, maxW)), cx, textY, {\n align: \"center\",\n });\n }\n if (caption) {\n doc.setFont(\"helvetica\", \"bold\");\n doc.setFontSize(9);\n doc.text(firstLine(doc.splitTextToSize(caption, maxW)), cx, textY + 10, {\n align: \"center\",\n });\n }\n }\n }\n\n doc.save(filename);\n}\n\n// ─── Label formats (the custom-format model) ─────────────────────────────────\n\n/** Where an element's text comes from: a row column, or a fixed string. */\nexport type LabelField = { field: string } | { literal: string };\n\nexport type LabelElement =\n | { kind: \"qr\"; source: LabelField }\n | {\n kind: \"text\";\n source: LabelField;\n role: \"title\" | \"detail\" | \"badge\";\n /**\n * Cap on how many lines the value may occupy. The value is split on\n * newlines; beyond `maxLines` the extra lines are dropped and the last\n * kept line gains an ellipsis.\n */\n maxLines?: number;\n }\n /** Durable http(s) URL only — THE IMAGE RULE. A non-http value is SKIPPED. */\n | { kind: \"image\"; source: LabelField };\n\nexport interface LabelFormat {\n id: string;\n name: string;\n elements: LabelElement[];\n}\n\nfunction resolveLabelField(\n source: LabelField,\n row: Record<string, string>,\n): string {\n if (\"literal\" in source) return source.literal;\n return row[source.field] ?? \"\";\n}\n\nfunction capToLines(value: string, maxLines: number | undefined): string[] {\n const parts = value\n .split(\"\\n\")\n .map((p) => p.trim())\n .filter((p) => p !== \"\");\n if (maxLines === undefined || maxLines <= 0 || parts.length <= maxLines) {\n return parts;\n }\n const kept = parts.slice(0, maxLines);\n const last = kept[kept.length - 1];\n if (last !== undefined) kept[kept.length - 1] = `${last}…`;\n return kept;\n}\n\n/**\n * Map rows through a format into the printer's label shape.\n *\n * - the qr element's resolved value → `qrValue` (first one wins)\n * - the FIRST title-role text → `caption`\n * - the FIRST badge-role text → `badge`\n * - detail-role texts, in element order → `lines[]`\n * - the first http(s) image element → `imageUrl`\n *\n * A row missing a `field` resolves to \"\" and that element is skipped.\n */\nexport function applyLabelFormat(\n format: LabelFormat,\n rows: Record<string, string>[],\n): { labels: QrLabelPrintData[\"labels\"] } {\n const labels: QrLabel[] = [];\n for (const row of rows) {\n let qrValue = \"\";\n let caption: string | undefined;\n let badge: string | undefined;\n let imageUrl: string | undefined;\n const lines: string[] = [];\n\n for (const el of format.elements) {\n const raw = resolveLabelField(el.source, row).trim();\n if (raw === \"\") continue;\n if (el.kind === \"qr\") {\n if (qrValue === \"\") qrValue = raw;\n continue;\n }\n if (el.kind === \"image\") {\n if (imageUrl === undefined && isHttpUrl(raw)) imageUrl = raw;\n continue;\n }\n const capped = capToLines(raw, el.maxLines);\n if (capped.length === 0) continue;\n if (el.role === \"title\") {\n if (caption === undefined) caption = capped.join(\" \");\n } else if (el.role === \"badge\") {\n if (badge === undefined) badge = capped.join(\" \");\n } else {\n lines.push(...capped);\n }\n }\n\n const label: QrLabel = { qrValue };\n if (caption !== undefined) label.caption = caption;\n if (lines.length > 0) label.lines = lines;\n if (badge !== undefined) label.badge = badge;\n if (imageUrl !== undefined) label.imageUrl = imageUrl;\n labels.push(label);\n }\n return { labels };\n}\n\n/**\n * The shipped formats. Every preset reads the QR payload from the `qrValue`\n * column, so a row is always `{ qrValue, …format-specific columns }`.\n *\n * NOTE on `asset-spec`: the cell renderer auto-shrinks only the caption/badge\n * (`.lp-fit`); detail lines are fixed 8pt, so the title never loses size to a\n * long spec list.\n */\nexport const LABEL_FORMAT_PRESETS: readonly LabelFormat[] = [\n {\n id: \"qr-only\",\n name: \"QR only\",\n elements: [{ kind: \"qr\", source: { field: \"qrValue\" } }],\n },\n {\n id: \"qr-caption\",\n name: \"QR + caption\",\n elements: [\n { kind: \"qr\", source: { field: \"qrValue\" } },\n { kind: \"text\", source: { field: \"caption\" }, role: \"title\" },\n ],\n },\n {\n id: \"garment\",\n name: \"Garment tag\",\n elements: [\n { kind: \"qr\", source: { field: \"qrValue\" } },\n { kind: \"text\", source: { field: \"size\" }, role: \"badge\" },\n { kind: \"text\", source: { field: \"sku\" }, role: \"title\" },\n { kind: \"text\", source: { field: \"color\" }, role: \"detail\" },\n { kind: \"text\", source: { field: \"price\" }, role: \"detail\" },\n ],\n },\n {\n id: \"asset-spec\",\n name: \"Asset spec\",\n elements: [\n { kind: \"text\", source: { field: \"title\" }, role: \"title\" },\n { kind: \"text\", source: { field: \"spec1\" }, role: \"detail\" },\n { kind: \"text\", source: { field: \"spec2\" }, role: \"detail\" },\n { kind: \"text\", source: { field: \"spec3\" }, role: \"detail\" },\n { kind: \"text\", source: { field: \"spec4\" }, role: \"detail\" },\n { kind: \"qr\", source: { field: \"qrValue\" } },\n ],\n },\n];\n","/**\n * @ai-matrx/print/qr — QR GENERATION.\n *\n * New code (no port source). Decoding is NOT here: it lives in\n * `@ai-matrx/kit/qr`. This module only ever turns a value into a symbol, and\n * builds the two payload shapes the platform actually prints:\n *\n * 1. a SHORT RESOLVER URL (`https://origin/l/<code>`) — the default\n * e-commerce payload. The code value is an OPAQUE INPUT: minting,\n * uniqueness, collision handling and lookup are all server-side concerns.\n * Nothing in this file generates, randomizes, or validates the identity of\n * a code beyond \"it is a non-empty string we will percent-encode\".\n * 2. a GS1 DIGITAL LINK (`https://origin/01/<gtin14>[/10/<lot>][/21/<serial>][?17=YYMMDD]`)\n * for supply-chain interoperability — the thing a retailer's scanner\n * understands without knowing anything about AI Matrx.\n *\n * Two hard defaults that are NOT style choices:\n * - Error correction defaults to \"M\", never \"L\". These end up on warehouse\n * labels that get scuffed, wrapped around curved stock and read at an\n * angle; \"L\" is only ever right for a huge, clean, flat code.\n * - The quiet zone is CLAMPED to >= 4 modules (ISO/IEC 18004 §6.3.8 minimum)\n * no matter what the caller passes. A tight quiet zone is the single most\n * common cause of a label that scans on the design monitor and fails at the\n * dock door.\n *\n * The module is import-inert: `qrcode` is only ever pulled in via\n * `await import(\"qrcode\")` inside a function body, so importing this file from\n * a React Server Component (or any tree-shaken bundle that never draws a QR)\n * costs nothing. No module-level mutable state, no React, no DOM at import\n * time.\n */\n\nimport type { QRCodeToDataURLOptions, QRCodeToStringOptions } from \"qrcode\";\n\n// ─── Contract ────────────────────────────────────────────────────────────────\n\nexport type QrEcLevel = \"L\" | \"M\" | \"Q\" | \"H\";\n\nexport interface QrOptions {\n /** Error correction. Default \"M\" — NEVER \"L\" (warehouse labels get scuffed; L is only right for huge clean codes). */\n ecLevel?: QrEcLevel;\n /** Quiet zone in modules. Default 4. CLAMPED to >= 4 at runtime — ISO 18004 spec minimum, not a style choice. */\n quietZoneModules?: number;\n /** Rendered width in pixels (dataURL/canvas lanes). */\n widthPx?: number;\n}\n\n/** ISO/IEC 18004 §6.3.8 minimum quiet zone. Callers cannot go below this. */\nexport const QR_MIN_QUIET_ZONE_MODULES = 4;\n\n/** The default error correction level for every Matrx-printed code. */\nexport const DEFAULT_QR_EC_LEVEL: QrEcLevel = \"M\";\n\n/** Lowest and highest QR symbol versions (ISO/IEC 18004). */\nexport const QR_MIN_VERSION = 1;\nexport const QR_MAX_VERSION = 40;\n\n// ─── Capacity table ──────────────────────────────────────────────────────────\n\n/**\n * ISO/IEC 18004 BYTE-mode data capacity, versions 1–40 × [L, M, Q, H],\n * in bytes (8-bit characters).\n *\n * This is the full spec table, not an approximation. It is cross-checked in\n * `qr.test.ts` against the `qrcode` reference implementation for a spread of\n * versions at all four levels — every entry that test samples must both fit at\n * exactly `capacity` bytes and fail at `capacity + 1`.\n *\n * Anchors worth eyeballing: v1 = L17/M14/Q11/H7 · v2 M=26 · v3 M=42 ·\n * v4 M=62 · v10 M=213 · v40 = L2953/M2331/Q1663/H1273.\n */\nconst BYTE_CAPACITY: readonly (readonly [number, number, number, number])[] = [\n /* v1 */ [17, 14, 11, 7],\n /* v2 */ [32, 26, 20, 14],\n /* v3 */ [53, 42, 32, 24],\n /* v4 */ [78, 62, 46, 34],\n /* v5 */ [106, 84, 60, 44],\n /* v6 */ [134, 106, 74, 58],\n /* v7 */ [154, 122, 86, 64],\n /* v8 */ [192, 152, 108, 84],\n /* v9 */ [230, 180, 130, 98],\n /* v10 */ [271, 213, 151, 119],\n /* v11 */ [321, 251, 177, 137],\n /* v12 */ [367, 287, 203, 155],\n /* v13 */ [425, 331, 241, 177],\n /* v14 */ [458, 362, 258, 194],\n /* v15 */ [520, 412, 292, 220],\n /* v16 */ [586, 450, 322, 250],\n /* v17 */ [644, 504, 364, 280],\n /* v18 */ [718, 560, 394, 310],\n /* v19 */ [792, 624, 442, 338],\n /* v20 */ [858, 666, 482, 382],\n /* v21 */ [929, 711, 509, 403],\n /* v22 */ [1003, 779, 565, 439],\n /* v23 */ [1091, 857, 611, 461],\n /* v24 */ [1171, 911, 661, 511],\n /* v25 */ [1273, 997, 715, 535],\n /* v26 */ [1367, 1059, 751, 593],\n /* v27 */ [1465, 1125, 805, 625],\n /* v28 */ [1528, 1190, 868, 658],\n /* v29 */ [1628, 1264, 908, 698],\n /* v30 */ [1732, 1370, 982, 742],\n /* v31 */ [1840, 1452, 1030, 790],\n /* v32 */ [1952, 1538, 1112, 842],\n /* v33 */ [2068, 1628, 1168, 898],\n /* v34 */ [2188, 1722, 1228, 958],\n /* v35 */ [2303, 1809, 1283, 983],\n /* v36 */ [2431, 1911, 1351, 1051],\n /* v37 */ [2563, 1989, 1423, 1093],\n /* v38 */ [2699, 2099, 1499, 1139],\n /* v39 */ [2809, 2213, 1579, 1219],\n /* v40 */ [2953, 2331, 1663, 1273],\n];\n\nconst EC_COLUMN: Readonly<Record<QrEcLevel, 0 | 1 | 2 | 3>> = { L: 0, M: 1, Q: 2, H: 3 };\n\nfunction assertEcLevel(ecLevel: QrEcLevel): 0 | 1 | 2 | 3 {\n const column = EC_COLUMN[ecLevel];\n if (column === undefined) {\n throw new Error(`Unknown QR error correction level \"${String(ecLevel)}\". Expected one of \"L\", \"M\", \"Q\", \"H\".`);\n }\n return column;\n}\n\nfunction assertVersion(version: number): number {\n if (typeof version !== \"number\" || !Number.isInteger(version) || version < QR_MIN_VERSION || version > QR_MAX_VERSION) {\n throw new Error(\n `QR version must be an integer between ${QR_MIN_VERSION} and ${QR_MAX_VERSION}, got ${String(version)}.`,\n );\n }\n return version;\n}\n\n/**\n * BYTE-mode capacity in bytes for a given version/level.\n * Throws on an out-of-range version or unknown level.\n */\nexport function qrByteCapacity(version: number, ecLevel: QrEcLevel): number {\n const column = assertEcLevel(ecLevel);\n const row = BYTE_CAPACITY[assertVersion(version) - 1];\n if (row === undefined) {\n throw new Error(`No QR capacity row for version ${version}.`);\n }\n return row[column];\n}\n\n/** Modules per side for a QR version: 17 + 4*version. */\nexport function qrModuleCount(version: number): number {\n return 17 + 4 * assertVersion(version);\n}\n\n/**\n * Smallest QR version (1-40) whose BYTE-mode capacity at `ecLevel` fits\n * `payloadByteLen`. Throws a clear Error when nothing fits.\n *\n * NOTE: this is the *byte* length, not the character length — callers holding\n * a JS string should measure it with `new TextEncoder().encode(s).length`, not\n * `s.length`, or every non-ASCII character will silently under-count.\n */\nexport function minQrVersion(payloadByteLen: number, ecLevel: QrEcLevel): number {\n const column = assertEcLevel(ecLevel);\n if (typeof payloadByteLen !== \"number\" || !Number.isInteger(payloadByteLen) || payloadByteLen < 0) {\n throw new Error(`QR payload byte length must be a non-negative integer, got ${String(payloadByteLen)}.`);\n }\n for (let version = QR_MIN_VERSION; version <= QR_MAX_VERSION; version += 1) {\n const row = BYTE_CAPACITY[version - 1];\n if (row !== undefined && row[column] >= payloadByteLen) {\n return version;\n }\n }\n const max = qrByteCapacity(QR_MAX_VERSION, ecLevel);\n throw new Error(\n `QR payload of ${payloadByteLen} bytes does not fit any QR version at error correction level \"${ecLevel}\" ` +\n `(version ${QR_MAX_VERSION} holds ${max} bytes). Shorten the payload — a short resolver URL is the intended ` +\n `fix — or, only for a large clean code, drop to a lower error correction level.`,\n );\n}\n\n// ─── Rendering ───────────────────────────────────────────────────────────────\n\nfunction resolveEcLevel(ecLevel: QrEcLevel | undefined): QrEcLevel {\n if (ecLevel === undefined) return DEFAULT_QR_EC_LEVEL;\n assertEcLevel(ecLevel);\n return ecLevel;\n}\n\n/**\n * `margin` in the `qrcode` library IS the quiet zone measured in modules.\n * Anything below the ISO minimum is silently raised — a caller asking for 0 is\n * asking for a label that will not scan, and we would rather print a slightly\n * smaller symbol than an unreadable one.\n */\nfunction resolveQuietZone(quietZoneModules: number | undefined): number {\n if (typeof quietZoneModules !== \"number\" || !Number.isFinite(quietZoneModules)) {\n return QR_MIN_QUIET_ZONE_MODULES;\n }\n return Math.max(QR_MIN_QUIET_ZONE_MODULES, Math.floor(quietZoneModules));\n}\n\nfunction resolveWidthPx(widthPx: number | undefined): number | undefined {\n if (widthPx === undefined) return undefined;\n if (typeof widthPx !== \"number\" || !Number.isFinite(widthPx) || widthPx <= 0) {\n throw new Error(`QR widthPx must be a positive number, got ${String(widthPx)}.`);\n }\n return Math.round(widthPx);\n}\n\nfunction assertValue(value: string): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(\"QR value must be a non-empty string.\");\n }\n return value;\n}\n\n/** PNG data URL. Lazy-imports \"qrcode\" at call time (import-inert module). */\nexport async function generateQrDataUrl(value: string, opts?: QrOptions): Promise<string> {\n assertValue(value);\n const width = resolveWidthPx(opts?.widthPx);\n const options: QRCodeToDataURLOptions = {\n errorCorrectionLevel: resolveEcLevel(opts?.ecLevel),\n margin: resolveQuietZone(opts?.quietZoneModules),\n };\n // exactOptionalPropertyTypes: omit the key entirely rather than assigning undefined.\n if (width !== undefined) options.width = width;\n\n const QRCode = await import(\"qrcode\");\n return QRCode.toDataURL(value, options);\n}\n\n/** SVG markup string. Same lazy import. */\nexport async function generateQrSvg(value: string, opts?: QrOptions): Promise<string> {\n assertValue(value);\n const width = resolveWidthPx(opts?.widthPx);\n const options: QRCodeToStringOptions = {\n type: \"svg\",\n errorCorrectionLevel: resolveEcLevel(opts?.ecLevel),\n margin: resolveQuietZone(opts?.quietZoneModules),\n };\n if (width !== undefined) options.width = width;\n\n const QRCode = await import(\"qrcode\");\n return QRCode.toString(value, options);\n}\n\n// ─── Payload builders ────────────────────────────────────────────────────────\n\n/**\n * Validate + normalize an origin for a payload builder.\n * Accepts `https://x.com`, `https://x.com/`, `https://x.com/go` — returns it\n * with every trailing slash stripped so callers can always concatenate a\n * leading-slash path without producing `//`.\n */\nfunction normalizeOrigin(origin: string): string {\n if (typeof origin !== \"string\" || origin.trim() === \"\") {\n throw new Error('QR origin is required — pass something like \"https://example.com\".');\n }\n const trimmed = origin.trim();\n let parsed: URL;\n try {\n parsed = new URL(trimmed);\n } catch {\n throw new Error(`QR origin \"${origin}\" is not a valid absolute URL. Expected something like \"https://example.com\".`);\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(`QR origin \"${origin}\" must use http or https, got \"${parsed.protocol}\".`);\n }\n if (parsed.search !== \"\" || parsed.hash !== \"\") {\n throw new Error(`QR origin \"${origin}\" must not carry a query string or fragment.`);\n }\n return trimmed.replace(/\\/+$/, \"\");\n}\n\n/**\n * Short resolver URL — the platform's default e-commerce payload.\n *\n * The CODE VALUE IS AN OPAQUE INPUT: minting and uniqueness are server-side,\n * never generated here. This function's whole job is to put a code the server\n * already owns behind a URL short enough to stay in a low QR version.\n *\n * Result looks like `https://x.com/l/AB12cd`.\n */\nexport function buildShortResolverUrl(opts: { origin: string; code: string; path?: string }): string {\n const origin = normalizeOrigin(opts.origin);\n\n const code = opts.code;\n if (typeof code !== \"string\" || code.trim() === \"\") {\n throw new Error(\"QR resolver code is required — it is minted server-side and passed in here as an opaque value.\");\n }\n\n const rawPath = opts.path === undefined ? \"/l\" : opts.path;\n if (typeof rawPath !== \"string\") {\n throw new Error(`QR resolver path must be a string, got ${String(rawPath)}.`);\n }\n if (rawPath.includes(\"?\") || rawPath.includes(\"#\")) {\n throw new Error(`QR resolver path \"${rawPath}\" must not contain a query string or fragment.`);\n }\n const cleanedPath = rawPath.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const prefix = cleanedPath === \"\" ? \"\" : `/${cleanedPath}`;\n\n return `${origin}${prefix}/${encodeURIComponent(code)}`;\n}\n\n/**\n * GS1 mod-10 check digit over the payload (i.e. the digits WITHOUT the check\n * digit): weights 3 and 1 alternating from the right. Length-independent, so a\n * GTIN-12 and its zero-padded GTIN-14 form produce the same digit — which is\n * exactly why we validate BEFORE padding.\n */\nfunction gs1CheckDigit(payloadDigits: string): number {\n let sum = 0;\n let weight = 3;\n for (let i = payloadDigits.length - 1; i >= 0; i -= 1) {\n sum += (payloadDigits.charCodeAt(i) - 48) * weight;\n weight = weight === 3 ? 1 : 3;\n }\n return (10 - (sum % 10)) % 10;\n}\n\n/** Validate a GTIN-8/12/13/14 (own check digit) and return it zero-padded to GTIN-14. */\nfunction normalizeGtin(gtin: string): string {\n if (typeof gtin !== \"string\" || gtin.trim() === \"\") {\n throw new Error(\"GTIN is required for a GS1 Digital Link.\");\n }\n const raw = gtin.trim();\n if (!/^[0-9]+$/.test(raw)) {\n throw new Error(`GTIN \"${gtin}\" must contain digits only.`);\n }\n if (raw.length !== 8 && raw.length !== 12 && raw.length !== 13 && raw.length !== 14) {\n throw new Error(`GTIN \"${gtin}\" must be 8, 12, 13, or 14 digits, got ${raw.length}.`);\n }\n const expected = gs1CheckDigit(raw.slice(0, -1));\n const actual = raw.charCodeAt(raw.length - 1) - 48;\n if (expected !== actual) {\n throw new Error(`GTIN \"${raw}\" has an invalid GS1 check digit: expected ${expected}, got ${actual}.`);\n }\n return raw.padStart(14, \"0\");\n}\n\nfunction daysInMonth(year: number, month: number): number {\n return new Date(Date.UTC(year, month, 0)).getUTCDate();\n}\n\n/**\n * AI 17 expiration date — STRICT \"YYMMDD\", 6 digits, nothing else.\n * `DD = 00` is honoured: GS1 defines it as \"last day of the month\".\n */\nfunction normalizeExpiry(expiry: string): string {\n if (typeof expiry !== \"string\" || !/^[0-9]{6}$/.test(expiry.trim())) {\n throw new Error(`GS1 expiry (AI 17) must be exactly 6 digits in YYMMDD form, got \"${String(expiry)}\".`);\n }\n const raw = expiry.trim();\n const year = 2000 + Number(raw.slice(0, 2));\n const month = Number(raw.slice(2, 4));\n const day = Number(raw.slice(4, 6));\n if (month < 1 || month > 12) {\n throw new Error(`GS1 expiry (AI 17) \"${raw}\" has an invalid month \"${raw.slice(2, 4)}\" — expected 01-12.`);\n }\n const max = daysInMonth(year, month);\n // DD=00 is the GS1 \"end of month\" convention and stays legal.\n if (day > max) {\n throw new Error(\n `GS1 expiry (AI 17) \"${raw}\" has an invalid day \"${raw.slice(4, 6)}\" — ${year}-${raw.slice(2, 4)} has ${max} days ` +\n `(00 is legal and means end of month).`,\n );\n }\n return raw;\n}\n\nfunction assertQualifier(value: string, ai: string, name: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw new Error(`GS1 ${name} (AI ${ai}) must be a non-empty string when provided.`);\n }\n return value;\n}\n\n/** GS1 Digital Link builder for supply-chain interoperability. */\nexport function buildGs1DigitalLink(opts: {\n origin: string;\n gtin: string;\n lot?: string;\n serial?: string;\n expiry?: string;\n}): string {\n const origin = normalizeOrigin(opts.origin);\n const gtin14 = normalizeGtin(opts.gtin);\n\n // Path order is fixed by the GS1 Digital Link grammar:\n // /01/{gtin14} then the optional qualifiers /10/{lot} then /21/{serial}.\n let url = `${origin}/01/${gtin14}`;\n if (opts.lot !== undefined) {\n url += `/10/${encodeURIComponent(assertQualifier(opts.lot, \"10\", \"lot\"))}`;\n }\n if (opts.serial !== undefined) {\n url += `/21/${encodeURIComponent(assertQualifier(opts.serial, \"21\", \"serial\"))}`;\n }\n // AI 17 is a DATA ATTRIBUTE, not a path qualifier — it belongs in the query\n // string, never as a /17/ path segment.\n if (opts.expiry !== undefined) {\n url += `?17=${normalizeExpiry(opts.expiry)}`;\n }\n return url;\n}\n"],"mappings":"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","names":["import_html_escape","caption","badge","DrawerPrimitive","renderPages"]}
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx","../src/core.ts","../src/katex-css.generated.ts","../src/labels.ts","../src/qr.ts"],"sourcesContent":["/**\n * @ai-matrx/print/react — the package's ONLY React entry.\n *\n * Two components, ported verbatim from matrx-frontend:\n * PrintOptionsDialog ← lib/block-print/PrintOptionsDialog.tsx\n * LabelSheetPreview ← lib/label-print/LabelSheetPreview.tsx\n *\n * The app seams are inverted here and NOWHERE else:\n * @/components/ui/dialog → inline minimal wrappers over @radix-ui/react-dialog\n * @/components/ui/drawer → inline minimal wrappers over vaul\n * @/components/ui/button → PrintDialogButton (the design-system Button's\n * exact class strings for the used variants)\n * @/hooks/use-mobile → useIsMobile, reproduced behavior-for-behavior\n * lucide-react → inline SVGs carrying lucide's own path data\n * @/lib/block-print/* → ./core\n * ./label-templates, ./qr-labels-printer → ./labels\n *\n * NO \"use client\" directive lives in this file — tsup stamps this entry via a\n * banner (see tsup.config.ts); a second literal directive here would be\n * duplicated into dist.\n *\n * Tailwind class strings are the originals verbatim: the consuming app's theme\n * tokens (bg-primary, text-muted-foreground, pb-safe…) style these components,\n * exactly as they did in matrx-frontend.\n */\n\nimport * as React from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\nimport { twMerge } from \"tailwind-merge\";\nimport type { CslItem, DocumentExport, DocumentFormat, DocumentNotice } from \"./document\";\n\nimport type {\n BlockPrinter,\n PrintOutcome,\n PrintSetting,\n PrintSettings,\n} from \"./core\";\nimport {\n computeCellLayout,\n generateQrDataUri,\n type LabelTemplate,\n type QrEcLevel,\n type QrLabel,\n} from \"./labels\";\n\n// ─── className merge ──────────────────────────────────────────────────────────\n\n/**\n * Tailwind-aware className merge. The app's `cn` is `twMerge(clsx(inputs))`;\n * `clsx` is dropped here (every call site passes strings / false), but\n * `tailwind-merge` is KEPT on purpose — the wrappers below depend on last-wins\n * conflict resolution (e.g. the caller's `sm:max-w-sm` must beat the built-in\n * `max-w-lg`). Same precedent as @ai-matrx/kit's `./confirm`.\n */\nfunction cn(...values: Array<string | null | undefined | false>): string {\n return twMerge(values.filter(Boolean).join(\" \"));\n}\n\n// ─── Icons (lucide path data, inlined — no lucide-react dependency) ───────────\n\ntype IconProps = React.SVGProps<SVGSVGElement>;\n\n/**\n * lucide's default SVG attributes, verbatim from\n * lucide-react/dist/esm/defaultAttributes.mjs.\n */\nfunction LucideIcon({\n lucideClass,\n className,\n children,\n ...props\n}: IconProps & { lucideClass: string }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={24}\n height={24}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={cn(lucideClass, className)}\n aria-hidden=\"true\"\n {...props}\n >\n {children}\n </svg>\n );\n}\n\n/** lucide `printer` */\nfunction Printer(props: IconProps) {\n return (\n <LucideIcon lucideClass=\"lucide lucide-printer\" {...props}>\n <path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\" />\n <path d=\"M6 9V3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6\" />\n <rect x=\"6\" y=\"14\" width=\"12\" height=\"8\" rx=\"1\" />\n </LucideIcon>\n );\n}\n\n/** lucide `x` */\nfunction X(props: IconProps) {\n return (\n <LucideIcon lucideClass=\"lucide lucide-x\" {...props}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </LucideIcon>\n );\n}\n\n/** lucide `settings-2` */\nfunction Settings2(props: IconProps) {\n return (\n <LucideIcon\n lucideClass=\"lucide lucide-settings2 lucide-settings-2\"\n {...props}\n >\n <path d=\"M14 17H5\" />\n <path d=\"M19 7h-9\" />\n <circle cx=\"17\" cy=\"17\" r=\"3\" />\n <circle cx=\"7\" cy=\"7\" r=\"3\" />\n </LucideIcon>\n );\n}\n\n// ─── useIsMobile (reproduces @/hooks/use-mobile exactly) ──────────────────────\n\nconst MOBILE_BREAKPOINT = 768;\n\n/**\n * Verbatim behavior of matrx-frontend `hooks/use-mobile.tsx`: same breakpoint,\n * same `matchMedia` change listener that re-reads `window.innerWidth`, and the\n * same \"false until mounted\" semantics that keeps SSR output stable.\n */\nfunction useIsMobile(): boolean {\n const [isMobile, setIsMobile] = useState(false);\n const [hasMounted, setHasMounted] = useState(false);\n\n useEffect(() => {\n setHasMounted(true);\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n };\n\n onChange(); // Set initial value\n mql.addEventListener(\"change\", onChange);\n\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n if (!hasMounted) {\n return false; // Return false during SSR\n }\n\n return isMobile;\n}\n\n// ─── Dialog wrappers (minimal, over @radix-ui/react-dialog) ───────────────────\n\n/**\n * `modal` is read back by DialogContent to decide whether the scrim renders and\n * whether `aria-modal` is set — the app's RadixDialogModalProvider, reduced to\n * the one fact these wrappers consume.\n */\nconst DialogModalContext = React.createContext<boolean>(true);\n\nfunction Dialog({\n children,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root>) {\n const modal = props.modal ?? true;\n return (\n <DialogModalContext.Provider value={modal}>\n <DialogPrimitive.Root {...props} modal={modal}>\n {children}\n </DialogPrimitive.Root>\n </DialogModalContext.Provider>\n );\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>) {\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n // Preserve readable page context behind the modal; separation comes from a light scrim, not blur.\n \"fixed inset-0 z-[10000] bg-black/20 dark:bg-black/30 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nconst DIALOG_DESKTOP_CLASSES =\n \"fixed left-[50%] top-[50%] z-[10000] grid min-w-0 w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 overflow-x-clip border bg-background p-6 shadow-lg [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\";\n\nconst DIALOG_MOBILE_SHEET_CLASSES =\n \"fixed inset-x-0 bottom-0 left-0 right-0 top-auto z-[10000] flex min-w-0 flex-col w-full max-w-full max-h-[90dvh] translate-x-0 translate-y-0 gap-4 overflow-x-clip border-t bg-background p-4 pb-safe shadow-lg [overflow-wrap:anywhere] [&>*]:min-w-0 [&>*]:max-w-full duration-200 rounded-t-2xl rounded-b-none overflow-y-auto overscroll-contain data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\";\n\n// Re-asserted LAST so the sheet geometry always wins over any caller `className`.\nconst DIALOG_MOBILE_SHEET_OVERRIDE =\n \"inset-x-0 bottom-0 left-0 right-0 top-auto translate-x-0 translate-y-0 w-full max-w-full max-h-[90dvh] rounded-b-none rounded-t-2xl overflow-y-auto\";\n\nfunction DialogContent({\n className,\n children,\n mobileSheet = true,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {\n /** When true (default), the dialog becomes a bottom sheet on mobile. */\n mobileSheet?: boolean;\n}) {\n const isMobile = useIsMobile();\n const isModal = React.useContext(DialogModalContext);\n const asSheet = mobileSheet && isMobile;\n\n return (\n <DialogPrimitive.Portal>\n {isModal ? <DialogOverlay /> : null}\n <DialogPrimitive.Content\n aria-modal={isModal || undefined}\n className={cn(\n asSheet ? DIALOG_MOBILE_SHEET_CLASSES : DIALOG_DESKTOP_CLASSES,\n className,\n asSheet && DIALOG_MOBILE_SHEET_OVERRIDE,\n // A non-modal Dialog stays below the WindowPanel manager's z=1000 floor.\n !isModal && \"z-[900]\",\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"absolute right-2 top-4 flex h-10 w-10 items-center justify-center rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground sm:right-4\"\n >\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n );\n}\n\nfunction DialogHeader({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\n // DialogContent owns an absolute close control. Reserve its hit area in\n // every header so trailing actions never render underneath it.\n \"flex flex-col space-y-1.5 pr-12 text-center sm:text-left\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogTitle({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n}\n\n// ─── Drawer wrappers (minimal, over vaul) ─────────────────────────────────────\n\ntype DrawerProps = React.ComponentProps<typeof DrawerPrimitive.Root> & {\n /** Vaul does not forward false to its underlying Radix root. */\n modal?: true;\n};\n\nfunction Drawer({\n children,\n shouldScaleBackground = true,\n ...props\n}: DrawerProps) {\n return (\n <DrawerPrimitive.Root\n modal\n shouldScaleBackground={shouldScaleBackground}\n {...props}\n >\n {children}\n </DrawerPrimitive.Root>\n );\n}\n\nfunction DrawerOverlay({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n className={cn(\"fixed inset-0 z-50 bg-black/80\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerContent({\n className,\n children,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>) {\n return (\n <DrawerPrimitive.Portal>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n aria-modal=\"true\"\n className={cn(\n \"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background pb-safe\",\n className,\n )}\n // No DrawerDescription is rendered by this package's only Drawer usage.\n aria-describedby={undefined}\n {...props}\n >\n <div className=\"mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPrimitive.Portal>\n );\n}\n\nfunction DrawerHeader({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={cn(\"grid gap-1.5 p-4 text-center sm:text-left\", className)}\n {...props}\n />\n );\n}\n\nfunction DrawerTitle({\n className,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className,\n )}\n {...props}\n />\n );\n}\n\n// ─── PrintDialogButton (the design-system Button, used variants only) ─────────\n\nconst BUTTON_BASE =\n \"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98] [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\";\n\nconst BUTTON_VARIANTS = {\n default: \"bg-primary text-primary-foreground shadow-sm hover:bg-primary/90\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n} as const;\n\nconst BUTTON_SIZES = {\n default: \"h-9 px-4 py-2\",\n sm: \"h-8 rounded-md px-3 text-xs\",\n icon: \"h-9 w-9\",\n} as const;\n\ninterface PrintDialogButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: keyof typeof BUTTON_VARIANTS;\n size?: keyof typeof BUTTON_SIZES;\n}\n\nfunction PrintDialogButton({\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: PrintDialogButtonProps) {\n return (\n <button\n className={cn(\n BUTTON_BASE,\n BUTTON_VARIANTS[variant],\n BUTTON_SIZES[size],\n className,\n )}\n {...props}\n />\n );\n}\n\n// ══════════════════════════════════════════════════════════════════════════════\n// PrintOptionsDialog\n// ══════════════════════════════════════════════════════════════════════════════\n\nexport interface PrintOptionsDialogProps {\n printer: BlockPrinter;\n data: unknown;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /**\n * Fired after printer.print() settles, with the PrintOutcome when the\n * printer reports one. \"downloaded\" means the popup was blocked and the\n * print document was saved as an .html download instead — hosts should\n * toast that, or the fallback is indistinguishable from nothing happening.\n */\n onPrinted?: (outcome: void | PrintOutcome) => void;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction buildDefaultSettings(settings: PrintSetting[]): PrintSettings {\n const out: PrintSettings = {};\n for (const s of settings) {\n if (s.type === \"boolean\") {\n out[s.id] = s.defaultValue;\n } else if (s.type === \"range\") {\n out[s.fromId] = s.defaultFrom;\n out[s.toId] = s.defaultTo;\n } else if (s.type === \"number\" || s.type === \"select\") {\n out[s.id] = s.defaultValue;\n }\n }\n return out;\n}\n\nfunction visibleSettings(\n settings: PrintSetting[],\n variantId: string,\n): PrintSetting[] {\n return settings.filter(\n (s) => !s.appliesTo || s.appliesTo.includes(variantId),\n );\n}\n\n// ─── Inner content (shared between Dialog and Drawer) ─────────────────────────\n\nfunction PrintOptionsContent({\n printer,\n data,\n onClose,\n onPrinted,\n}: {\n printer: BlockPrinter;\n data: unknown;\n onClose: () => void;\n onPrinted?: ((outcome: void | PrintOutcome) => void) | undefined;\n}) {\n // MATRX-EXCEPTION: React state initializer — \"\" (no variant selected) when\n // the printer declares zero variants; pure UI default, not a boundary write.\n const [selectedVariant, setSelectedVariant] = useState<string>(\n printer.variants[0]?.id ?? \"\",\n );\n const [isPrinting, setIsPrinting] = useState(false);\n\n const allSettings = printer.settings ?? [];\n\n // Initialize from defaults once\n const [settingValues, setSettingValues] = useState<PrintSettings>(() =>\n buildDefaultSettings(allSettings),\n );\n\n // Settings visible for the currently selected variant\n const currentSettings = useMemo(\n () => visibleSettings(allSettings, selectedVariant),\n [allSettings, selectedVariant],\n );\n\n const toggleSetting = (id: string) => {\n setSettingValues((prev) => ({ ...prev, [id]: !prev[id] }));\n };\n\n const setNumberSetting = (id: string, value: number) => {\n setSettingValues((prev) => ({ ...prev, [id]: value }));\n };\n\n const handlePrint = async (variantId?: string, useDefaults = false) => {\n setIsPrinting(true);\n try {\n const settings = useDefaults\n ? buildDefaultSettings(allSettings)\n : settingValues;\n const outcome = await printer.print(data, variantId, settings);\n onPrinted?.(outcome);\n } finally {\n setIsPrinting(false);\n onClose();\n }\n };\n\n return (\n <div className=\"flex flex-col min-h-0 flex-1\">\n {/* ── Scrollable body: variant list + settings ── */}\n <div className=\"flex-1 overflow-y-auto min-h-0 py-2 space-y-4\">\n {/* Variant picker */}\n {printer.variants.length > 0 && (\n <div>\n <p className=\"text-sm font-medium text-foreground mb-2 px-0.5\">\n Print format\n </p>\n <div className=\"space-y-1.5\">\n {printer.variants.map((variant) => (\n <button\n key={variant.id}\n onClick={() => setSelectedVariant(variant.id)}\n className={`w-full flex flex-col items-start px-3 py-2.5 rounded-lg border text-left transition-colors ${\n selectedVariant === variant.id\n ? \"border-primary bg-primary/5 text-primary\"\n : \"border-border bg-background hover:bg-accent\"\n }`}\n >\n <span className=\"text-sm font-medium\">{variant.label}</span>\n {variant.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {variant.description}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {/* Settings toggles — only shown when there are settings for this variant */}\n {currentSettings.length > 0 && (\n <div>\n <p className=\"text-sm font-medium text-foreground mb-2 px-0.5 flex items-center gap-1.5\">\n <Settings2 className=\"w-3.5 h-3.5\" />\n Options\n </p>\n <div className=\"space-y-1\">\n {currentSettings.map((setting) => {\n if (setting.type === \"boolean\") {\n const checked = settingValues[setting.id] === true;\n return (\n <button\n key={setting.id}\n onClick={() => toggleSetting(setting.id)}\n className=\"w-full flex items-start gap-3 px-3 py-2.5 rounded-lg border border-border bg-background hover:bg-accent text-left transition-colors\"\n >\n {/* Custom checkbox */}\n <span\n className={`mt-0.5 shrink-0 w-4 h-4 rounded border-2 flex items-center justify-center transition-colors ${\n checked\n ? \"bg-primary border-primary\"\n : \"border-muted-foreground/40\"\n }`}\n >\n {checked && (\n <svg\n className=\"w-2.5 h-2.5 text-primary-foreground\"\n viewBox=\"0 0 10 10\"\n fill=\"none\"\n >\n <path\n d=\"M1.5 5l2.5 2.5 4.5-4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </span>\n <span className=\"flex flex-col min-w-0\">\n <span className=\"text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n {setting.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {setting.description}\n </span>\n )}\n </span>\n </button>\n );\n }\n\n if (setting.type === \"range\") {\n const min = setting.min ?? 0;\n const fromVal =\n (settingValues[setting.fromId] as number | undefined) ?? 0;\n const toVal =\n (settingValues[setting.toId] as number | undefined) ?? 0;\n const parse = (raw: string) => {\n if (raw === \"\") return 0;\n const n = parseInt(raw, 10);\n return Number.isNaN(n) ? 0 : Math.max(min, n);\n };\n return (\n <div\n key={setting.id}\n className=\"w-full flex items-start gap-3 px-3 py-2.5 rounded-lg border border-border bg-background\"\n >\n <span className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n {setting.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {setting.description}\n </span>\n )}\n </span>\n <div className=\"flex items-center gap-1.5 shrink-0 pt-0.5\">\n <input\n type=\"number\"\n inputMode=\"numeric\"\n min={min}\n value={fromVal === 0 ? \"\" : fromVal}\n onChange={(e) =>\n setNumberSetting(\n setting.fromId,\n parse(e.target.value),\n )\n }\n placeholder={setting.fromPlaceholder ?? \"start\"}\n aria-label={`${setting.label} — from`}\n className=\"w-14 h-7 text-xs text-center rounded border border-input bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n <span className=\"text-xs text-muted-foreground\">\n to\n </span>\n <input\n type=\"number\"\n inputMode=\"numeric\"\n min={min}\n value={toVal === 0 ? \"\" : toVal}\n onChange={(e) =>\n setNumberSetting(\n setting.toId,\n parse(e.target.value),\n )\n }\n placeholder={setting.toPlaceholder ?? \"end\"}\n aria-label={`${setting.label} — to`}\n className=\"w-14 h-7 text-xs text-center rounded border border-input bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n </div>\n </div>\n );\n }\n\n if (setting.type === \"number\") {\n const min = setting.min ?? 0;\n const val =\n (settingValues[setting.id] as number | undefined) ??\n setting.defaultValue;\n const parse = (raw: string) => {\n if (raw === \"\") return setting.defaultValue;\n const n = parseInt(raw, 10);\n if (Number.isNaN(n)) return setting.defaultValue;\n const bounded = Math.max(min, n);\n return setting.max !== undefined\n ? Math.min(setting.max, bounded)\n : bounded;\n };\n return (\n <div\n key={setting.id}\n className=\"w-full flex items-start gap-3 px-3 py-2.5 rounded-lg border border-border bg-background\"\n >\n <span className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n {setting.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {setting.description}\n </span>\n )}\n </span>\n <input\n type=\"number\"\n inputMode=\"numeric\"\n min={min}\n max={setting.max}\n value={val}\n onChange={(e) =>\n setNumberSetting(setting.id, parse(e.target.value))\n }\n aria-label={setting.label}\n className=\"w-16 h-7 text-xs text-center rounded border border-input bg-background text-foreground focus:outline-none focus:ring-1 focus:ring-ring shrink-0 mt-0.5\"\n />\n </div>\n );\n }\n\n if (setting.type === \"select\") {\n const val =\n (settingValues[setting.id] as string | undefined) ??\n setting.defaultValue;\n return (\n <div\n key={setting.id}\n className=\"w-full flex items-start gap-3 px-3 py-2.5 rounded-lg border border-border bg-background\"\n >\n <span className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"text-sm font-medium text-foreground\">\n {setting.label}\n </span>\n {setting.description && (\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n {setting.description}\n </span>\n )}\n </span>\n <div className=\"flex items-center gap-0.5 shrink-0 rounded-md border border-input p-0.5 mt-0.5\">\n {setting.options.map((opt) => (\n <button\n key={opt.value}\n onClick={() =>\n setSettingValues((prev) => ({\n ...prev,\n [setting.id]: opt.value,\n }))\n }\n className={`px-2 h-6 text-xs rounded transition-colors ${\n val === opt.value\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent\"\n }`}\n >\n {opt.label}\n </button>\n ))}\n </div>\n </div>\n );\n }\n\n return null;\n })}\n </div>\n </div>\n )}\n </div>\n\n {/* ── Action buttons — always pinned at bottom ── */}\n <div className=\"flex flex-col gap-2 pt-3 pb-1 border-t border-border mt-2 shrink-0\">\n <PrintDialogButton\n onClick={() => handlePrint(selectedVariant || undefined)}\n disabled={isPrinting}\n className=\"w-full\"\n >\n <Printer className=\"w-4 h-4 mr-2\" />\n {isPrinting ? \"Opening print view...\" : \"Print / Save PDF\"}\n </PrintDialogButton>\n {printer.variants.length > 0 && (\n <PrintDialogButton\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => handlePrint(undefined, true)}\n disabled={isPrinting}\n className=\"w-full text-muted-foreground text-xs\"\n >\n Print with defaults\n </PrintDialogButton>\n )}\n </div>\n </div>\n );\n}\n\n// ─── Public component ─────────────────────────────────────────────────────────\n\n/**\n * Shared pre-print options UI — shown when a BlockPrinter has variants or settings.\n * Desktop: Dialog. Mobile: Drawer (bottom sheet).\n */\nexport function PrintOptionsDialog({\n printer,\n data,\n open,\n onOpenChange,\n onPrinted,\n}: PrintOptionsDialogProps) {\n const isMobile = useIsMobile();\n const handleClose = () => onOpenChange(false);\n\n if (isMobile) {\n return (\n <Drawer open={open} onOpenChange={onOpenChange}>\n <DrawerContent className=\"flex flex-col max-h-[85dvh]\">\n <DrawerHeader className=\"flex items-center justify-between pb-2 shrink-0\">\n <DrawerTitle className=\"flex items-center gap-2\">\n <Printer className=\"w-4 h-4\" />\n {printer.label}\n </DrawerTitle>\n <PrintDialogButton\n variant=\"ghost\"\n size=\"icon\"\n onClick={handleClose}\n className=\"h-7 w-7\"\n >\n <X className=\"w-4 h-4\" />\n </PrintDialogButton>\n </DrawerHeader>\n <div className=\"flex flex-col min-h-0 flex-1 overflow-hidden px-4 pb-4\">\n <PrintOptionsContent\n printer={printer}\n data={data}\n onClose={handleClose}\n onPrinted={onPrinted}\n />\n </div>\n </DrawerContent>\n </Drawer>\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-sm flex flex-col max-h-[85dvh]\">\n <DialogHeader className=\"shrink-0\">\n <DialogTitle className=\"flex items-center gap-2\">\n <Printer className=\"w-4 h-4\" />\n {printer.label}\n </DialogTitle>\n <DialogDescription className=\"sr-only\">\n Choose a print format and options for this content.\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col min-h-0 flex-1 overflow-hidden\">\n <PrintOptionsContent\n printer={printer}\n data={data}\n onClose={handleClose}\n onPrinted={onPrinted}\n />\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────────────────\n\n/**\n * Manages open state for PrintOptionsDialog.\n * If the printer has variants or settings, shows the dialog.\n * If it has neither, calls printer.print() immediately with no settings.\n */\nexport function usePrintOptions(\n printer: BlockPrinter | null,\n data: unknown,\n onPrinted?: (outcome: void | PrintOutcome) => void,\n) {\n const [open, setOpen] = useState(false);\n\n const triggerPrint = async () => {\n if (!printer) return;\n const hasOptions =\n printer.variants.length > 0 || (printer.settings?.length ?? 0) > 0;\n if (!hasOptions) {\n const outcome = await printer.print(data);\n onPrinted?.(outcome);\n } else {\n setOpen(true);\n }\n };\n\n return { open, setOpen, triggerPrint };\n}\n\n// ══════════════════════════════════════════════════════════════════════════════\n// LabelSheetPreview\n// ══════════════════════════════════════════════════════════════════════════════\n\n/**\n * LabelSheetPreview — scaled on-screen preview of one label sheet, driven by\n * the same LabelTemplate geometry as the printer (percent-positioned cells,\n * so it is exactly proportional at any width). Also renders a calibration\n * view (outlines only) so a user can eyeball the grid before printing.\n *\n * QR images are generated client-side to data URIs via `qrcode` (deferred\n * import at effect time).\n */\n\nexport interface LabelSheetPreviewProps {\n template: LabelTemplate;\n labels: QrLabel[];\n /** 1-based first cell to fill — leading cells render empty. */\n startAtLabel?: number;\n showCaption?: boolean;\n ecLevel?: QrEcLevel;\n /** Outlines-only calibration view. */\n calibration?: boolean;\n /** Which sheet to show (0-based). */\n pageIndex?: number;\n className?: string;\n}\n\nexport function LabelSheetPreview({\n template: t,\n labels,\n startAtLabel = 1,\n showCaption = true,\n ecLevel = \"M\",\n calibration = false,\n pageIndex = 0,\n className,\n}: LabelSheetPreviewProps) {\n const [uris, setUris] = useState<Map<string, string>>(new Map());\n\n const perPage = t.cols * t.rows;\n const leadingBlanks = Math.max(0, startAtLabel - 1) % perPage;\n const pageStart = pageIndex * perPage - leadingBlanks;\n const layout = computeCellLayout(t, showCaption);\n\n // Labels occupying this page's cells (null = blank cell)\n const cells: (QrLabel | null)[] = Array.from({ length: perPage }, (_, i) => {\n const li = pageStart + i;\n // `?? null` is required by noUncheckedIndexedAccess; the bounds check\n // already guarantees the element exists, so behavior is unchanged.\n return li >= 0 && li < labels.length ? (labels[li] ?? null) : null;\n });\n\n const valuesKey = Array.from(\n new Set(\n cells.filter((c): c is QrLabel => c !== null).map((c) => c.qrValue),\n ),\n ).join(\"\\u0000\");\n const qrIn = layout.qrIn;\n\n useEffect(() => {\n if (calibration) return;\n let cancelled = false;\n const values = valuesKey ? valuesKey.split(\"\\u0000\") : [];\n void (async () => {\n const next = new Map<string, string>();\n for (const v of values) {\n try {\n next.set(v, await generateQrDataUri(v, ecLevel, qrIn));\n } catch (err) {\n console.error(\"[LabelSheetPreview] QR generation failed:\", err);\n }\n }\n if (!cancelled) setUris(next);\n })();\n return () => {\n cancelled = true;\n };\n }, [calibration, ecLevel, qrIn, valuesKey]);\n\n const pct = (v: number, base: number) => `${(v / base) * 100}%`;\n const radiusPct = t.round\n ? \"50%\"\n : t.cornerRadiusIn\n ? `${(t.cornerRadiusIn / t.labelWIn) * 100}% / ${(t.cornerRadiusIn / t.labelHIn) * 100}%`\n : \"0\";\n\n return (\n <div\n className={`relative w-full bg-white border border-border rounded-md shadow-sm overflow-hidden ${className ?? \"\"}`}\n style={{ aspectRatio: `${t.sheetWIn} / ${t.sheetHIn}` }}\n aria-label={`Label sheet preview — ${t.stockCode}`}\n >\n {cells.map((label, i) => {\n const col = i % t.cols;\n const row = Math.floor(i / t.cols);\n const x = t.marginLeftIn + col * (t.labelWIn + t.gutterXIn);\n const y = t.marginTopIn + row * (t.labelHIn + t.gutterYIn);\n const uri = label ? uris.get(label.qrValue) : undefined;\n const wide = layout.wide;\n return (\n <div\n key={i}\n className={\n calibration || !label\n ? \"absolute border border-dashed border-slate-300\"\n : \"absolute border border-slate-200\"\n }\n style={{\n left: pct(x, t.sheetWIn),\n top: pct(y, t.sheetHIn),\n width: pct(t.labelWIn, t.sheetWIn),\n height: pct(t.labelHIn, t.sheetHIn),\n borderRadius: radiusPct,\n overflow: \"hidden\",\n }}\n >\n {calibration ? (\n <div className=\"w-full h-full flex items-center justify-center text-[8px] text-slate-400\">\n {i + 1}\n </div>\n ) : label ? (\n <div\n className={`w-full h-full flex ${wide ? \"flex-row items-center\" : \"flex-col items-center justify-center\"} gap-[2%] p-[3%]`}\n >\n {uri ? (\n <img\n src={uri}\n alt=\"\"\n className=\"block\"\n style={\n wide\n ? { height: \"88%\", aspectRatio: \"1\" }\n : {\n width: `${(layout.qrIn / t.labelWIn) * 100}%`,\n aspectRatio: \"1\",\n }\n }\n />\n ) : (\n <div\n className=\"bg-slate-100 animate-pulse rounded-sm\"\n style={\n wide\n ? { height: \"88%\", aspectRatio: \"1\" }\n : { width: \"70%\", aspectRatio: \"1\" }\n }\n />\n )}\n <div\n className={`min-w-0 overflow-hidden text-black leading-tight ${wide ? \"flex-1 text-left\" : \"text-center w-full\"}`}\n style={{ fontSize: \"min(1.4vw, 10px)\" }}\n >\n {showCaption && (\n <div className=\"font-bold truncate\">\n {label.caption ?? label.qrValue}\n </div>\n )}\n {wide &&\n label.lines\n ?.filter((l) => l.trim())\n .map((l, li) => (\n <div key={li} className=\"truncate text-slate-600\">\n {l}\n </div>\n ))}\n </div>\n </div>\n ) : null}\n </div>\n );\n })}\n </div>\n );\n}\n\n// ─── DocumentPrintPreview ─────────────────────────────────────────────────────\n\n\nexport interface DocumentPrintPreviewProps {\n /** Markdown source, frontmatter settings included. */\n markdown: string;\n /** File name / fallback title when the frontmatter has none. */\n title?: string;\n className?: string;\n /** Called for every degraded or dropped piece (also listed in the panel). */\n onNotice?: (notice: DocumentNotice) => void;\n /**\n * Resolves citation keys the document's frontmatter does not define\n * (e.g. `[@research:<id>]`, `[@file:<id>]`) to CSL-JSON references.\n */\n resolveReferences?: (keys: string[]) => Promise<CslItem[]>;\n /**\n * Draws the PDF pages when the browser cannot show a PDF inline (Chrome\n * with \"Download PDFs\" on, Android, touch devices). Receives a same-origin\n * `blob:` URL of the exact PDF. Without it, such browsers get the Web view.\n */\n renderPages?: (pdfUrl: string) => React.ReactNode;\n /**\n * Prints the PDF's pages when the browser has no inline PDF viewer to print\n * from. Without it, Print prints the Web view.\n */\n printPages?: (pdfUrl: string) => Promise<void>;\n}\n\n/**\n * True when this browser shows a PDF inside a frame with all its pages and can\n * print it from there. Chrome with \"Download PDFs\" on (and Android) reports\n * `pdfViewerEnabled === false` and paints only a file name and an Open button;\n * touch browsers render just the first page.\n */\nfunction hasInlinePdfViewer(): boolean {\n if (typeof navigator === \"undefined\") return true;\n if (navigator.pdfViewerEnabled === false) return false;\n return !(typeof window !== \"undefined\" && window.matchMedia?.(\"(pointer: coarse)\").matches);\n}\n\n/** Prints an HTML document through a hidden same-origin frame. */\nfunction printHtml(html: string): void {\n const frame = document.createElement(\"iframe\");\n frame.setAttribute(\"aria-hidden\", \"true\");\n frame.style.cssText = \"position:fixed;right:0;bottom:0;width:0;height:0;border:0;\";\n document.body.appendChild(frame);\n const win = frame.contentWindow;\n if (!win) {\n frame.remove();\n throw new Error(\"Printing is blocked in this browser.\");\n }\n win.document.open();\n win.document.write(html);\n win.document.close();\n const run = () => {\n win.focus();\n win.print();\n // Mobile print() returns before the system sheet has read the frame.\n setTimeout(() => frame.remove(), 60_000);\n };\n if (win.document.readyState === \"complete\") setTimeout(run, 50);\n else win.addEventListener(\"load\", run, { once: true });\n}\n\nconst EXPORT_BUTTONS: Array<{ format: DocumentFormat; label: string }> = [\n { format: \"pdf\", label: \"PDF\" },\n { format: \"docx\", label: \"Word\" },\n { format: \"epub\", label: \"EPUB\" },\n { format: \"html\", label: \"HTML\" },\n { format: \"markdown\", label: \"Markdown\" },\n];\n\n/**\n * Print preview for a markdown document: the real PDF (the same file the PDF\n * download gives you — pages, header/footer, TOC page numbers), a \"Web\" view\n * of the HTML with a clickable TOC, and one-click export to PDF, Word, EPUB,\n * HTML and Markdown. Everything comes from one parsed tree, so what you see\n * is what every format contains. Engines load lazily on mount.\n */\nexport function DocumentPrintPreview({\n markdown,\n title,\n className,\n onNotice,\n resolveReferences,\n renderPages,\n printPages,\n}: DocumentPrintPreviewProps) {\n const [inlineViewer] = useState(hasInlinePdfViewer);\n const canShowPages = inlineViewer || Boolean(renderPages);\n const [view, setView] = useState<\"pages\" | \"web\">(canShowPages ? \"pages\" : \"web\");\n const [pdf, setPdf] = useState<{ url: string; pages: number } | null>(null);\n const [html, setHtml] = useState<string>(\"\");\n const [notices, setNotices] = useState<DocumentNotice[]>([]);\n const [summary, setSummary] = useState<string>(\"\");\n const [error, setError] = useState<string | null>(null);\n const [busy, setBusy] = useState<DocumentFormat | \"render\" | null>(\"render\");\n const noticeRef = React.useRef(onNotice);\n noticeRef.current = onNotice;\n const frameRef = React.useRef<HTMLIFrameElement>(null);\n\n useEffect(() => {\n let cancelled = false;\n let url: string | null = null;\n setBusy(\"render\");\n const timer = setTimeout(async () => {\n try {\n const doc = await import(\"@ai-matrx/print/document\");\n const exp = await doc.exportDocument(markdown, \"pdf\", { title, resolveReferences });\n if (cancelled) return;\n url = URL.createObjectURL(new Blob([exp.bytes as BlobPart], { type: \"application/pdf\" }));\n const images = await doc.resolveDocumentImages(exp.tree);\n if (cancelled) return;\n setPdf({ url, pages: exp.pageCount ?? 0 });\n setHtml(doc.documentToHtml(exp.tree, { images }));\n const s = exp.tree.settings;\n setSummary(\n [\n doc.PAGE_SIZES[s.page.size].label,\n s.page.orientation === \"landscape\" ? \"Landscape\" : \"Portrait\",\n exp.tree.figures.length ? `${exp.tree.figures.length} figure${exp.tree.figures.length === 1 ? \"\" : \"s\"}` : \"\",\n exp.tree.tables.length ? `${exp.tree.tables.length} table${exp.tree.tables.length === 1 ? \"\" : \"s\"}` : \"\",\n exp.tree.bibliography.length ? `${exp.tree.bibliography.length} reference${exp.tree.bibliography.length === 1 ? \"\" : \"s\"}` : \"\",\n ]\n .filter(Boolean)\n .join(\" · \"),\n );\n setNotices(exp.notices);\n for (const n of exp.notices) noticeRef.current?.(n);\n setError(null);\n } catch (err) {\n if (!cancelled) setError(err instanceof Error ? err.message : String(err));\n } finally {\n if (!cancelled) setBusy(null);\n }\n }, 350);\n return () => {\n cancelled = true;\n clearTimeout(timer);\n if (url) URL.revokeObjectURL(url);\n };\n }, [markdown, title, resolveReferences]);\n\n const download = async (format: DocumentFormat) => {\n setBusy(format);\n try {\n const doc = await import(\"@ai-matrx/print/document\");\n const exp: DocumentExport = await doc.exportDocument(markdown, format, { title, resolveReferences });\n doc.downloadDocumentExport(exp);\n setError(null);\n } catch (err) {\n setError(`${format.toUpperCase()} export failed: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n setBusy(null);\n }\n };\n\n const print = async () => {\n if (!pdf) return;\n try {\n // The inline PDF viewer prints the exact pages shown.\n if (inlineViewer && frameRef.current?.contentWindow) {\n frameRef.current.contentWindow.print();\n return;\n }\n // No inline viewer: the frame holds a placeholder, so print the pages\n // another way — the host's page printer, else the Web view.\n if (printPages) await printPages(pdf.url);\n else printHtml(html);\n } catch (err) {\n setError(`Print failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n };\n\n const btn =\n \"inline-flex h-7 items-center gap-1 rounded-md border border-border bg-background px-2.5 text-xs font-medium text-foreground hover:bg-accent disabled:opacity-50\";\n return (\n <div className={cn(\"flex min-h-0 flex-1 flex-col\", className)} data-testid=\"document-print-preview\">\n <div className=\"flex flex-wrap items-center gap-2 border-b border-border px-3 py-1.5 text-xs\">\n <div className=\"inline-flex overflow-hidden rounded-md border border-border\" role=\"tablist\">\n {(canShowPages ? ([\"pages\", \"web\"] as const) : ([\"web\"] as const)).map((v) => (\n <button\n key={v}\n type=\"button\"\n role=\"tab\"\n aria-selected={view === v}\n onClick={() => setView(v)}\n className={cn(\"px-2.5 py-1 text-xs\", view === v ? \"bg-accent font-medium text-foreground\" : \"text-muted-foreground\")}\n >\n {v === \"pages\" ? \"Pages\" : \"Web\"}\n </button>\n ))}\n </div>\n <span className=\"text-muted-foreground\" data-testid=\"document-print-summary\">\n {busy === \"render\" && !pdf ? \"Laying out pages…\" : `${pdf?.pages ?? 0} page${pdf?.pages === 1 ? \"\" : \"s\"}${summary ? ` · ${summary}` : \"\"}`}\n </span>\n <div className=\"ml-auto flex flex-wrap items-center gap-1.5\">\n <button type=\"button\" className={btn} onClick={() => void print()} disabled={!pdf}>\n Print\n </button>\n {EXPORT_BUTTONS.map((b) => (\n <button key={b.format} type=\"button\" className={btn} onClick={() => void download(b.format)} disabled={busy === b.format}>\n {busy === b.format ? \"Saving…\" : b.label}\n </button>\n ))}\n </div>\n </div>\n {error && <p className=\"border-b border-border bg-destructive/10 px-3 py-1.5 text-xs text-destructive\">{error}</p>}\n {notices.length > 0 && (\n <details className=\"border-b border-border px-3 py-1.5 text-xs\">\n <summary className=\"cursor-pointer text-amber-700 dark:text-amber-400\">\n {notices.length} thing{notices.length === 1 ? \"\" : \"s\"} to fix before printing\n </summary>\n <ul className=\"mt-1 space-y-1\">\n {notices.map((n, i) => (\n <li key={i}>\n <span className=\"text-foreground\">{n.message}</span> <span className=\"text-muted-foreground\">{n.remedy}</span>\n </li>\n ))}\n </ul>\n </details>\n )}\n <div className=\"min-h-0 flex-1 bg-muted/40\">\n {view === \"pages\" ? (\n pdf ? (\n inlineViewer ? (\n <iframe ref={frameRef} title=\"Print preview\" src={pdf.url} className=\"h-full min-h-[480px] w-full border-0\" />\n ) : (\n <div className=\"h-full min-h-[480px] w-full\">{renderPages?.(pdf.url)}</div>\n )\n ) : (\n <p className=\"p-8 text-center text-xs text-muted-foreground\">{error ? \"No preview.\" : \"Laying out pages…\"}</p>\n )\n ) : (\n <iframe title=\"Web preview\" srcDoc={html} sandbox=\"allow-same-origin\" className=\"h-full min-h-[480px] w-full border-0 bg-white\" />\n )}\n </div>\n </div>\n );\n}\n","/**\n * @ai-matrx/print/core — the Block Print contract & print-window composition.\n *\n * Ported VERBATIM from matrx-frontend `lib/block-print/block-print-utils.ts`\n * (2026-08-29 state, including the `number` and `select` PrintSetting types the\n * commerce label lane added). Zero dependencies; touches `window` only at call\n * time, so the module stays import-inert and RSC-safe.\n *\n * BlockPrinter is the only interface a host system needs to know about.\n * Each printable unit exports its own BlockPrinter alongside its render\n * component; host code only ever calls print(data, variantId, settings).\n *\n * `buildFitTextScript` is the ONE auto-shrink script (the flashcards\n * FIT_TEXT pattern, parameterized by selector so the label lane shares it).\n */\n\n// THE ONE HTML escaper (duplication census row 14). Nine copies of\n// `escapeHtml` were live across the fleet and THEY DID NOT ESCAPE THE SAME\n// CHARACTERS — a string safe through one was an attribute break-out through\n// another. It moved to `@ai-matrx/kit/html-escape` with a decided character\n// set: an HTML escaper must not require this package's jspdf + html2canvas\n// graph. Not re-exported from here; import `@ai-matrx/kit/html-escape`.\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport { KATEX_CSS, KATEX_CSS_MARKER } from \"./katex-css.generated\";\n\n// ─── Contract ────────────────────────────────────────────────────────────────\n\nexport interface PrintVariant {\n id: string;\n label: string;\n description?: string;\n}\n\n/**\n * A single user-configurable setting shown in the print dialog.\n * Extend the discriminated union for new control types\n * without breaking existing printers.\n */\nexport type PrintSetting =\n | {\n type: \"boolean\";\n id: string;\n label: string;\n description?: string;\n /** Default value shown when the dialog first opens */\n defaultValue: boolean;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /**\n * A numeric \"from → to\" range control, rendered as two small number inputs.\n * Writes two entries into the resolved PrintSettings map — one under fromId,\n * one under toId. Printers interpret 0 (or missing) as \"no bound\" by convention.\n */\n type: \"range\";\n /** Stable id used as React key — not written to the settings map */\n id: string;\n label: string;\n description?: string;\n /** Settings map key for the lower bound value */\n fromId: string;\n /** Settings map key for the upper bound value */\n toId: string;\n defaultFrom: number;\n defaultTo: number;\n /** Minimum accepted value for each input (inclusive). Default 0. */\n min?: number;\n /** Placeholder shown when the input is empty (0). Default \"start\" / \"end\". */\n fromPlaceholder?: string;\n toPlaceholder?: string;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /** A single numeric input (e.g. \"start at label #\", \"copies\"). */\n type: \"number\";\n id: string;\n label: string;\n description?: string;\n defaultValue: number;\n /** Minimum accepted value (inclusive). Default 0. */\n min?: number;\n /** Maximum accepted value (inclusive). Omit = unbounded. */\n max?: number;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n }\n | {\n /** A one-of-N choice rendered as a compact segmented row. */\n type: \"select\";\n id: string;\n label: string;\n description?: string;\n options: { value: string; label: string }[];\n defaultValue: string;\n /** Which variant IDs this setting applies to. Omit = applies to all. */\n appliesTo?: string[];\n };\n\n/** A resolved map of setting id → current value, passed to print(). */\nexport type PrintSettings = Record<string, boolean | string | number>;\n\n/**\n * The self-contained print capability that each block can export.\n * Host code only ever calls print(data, variantId, settings) — it never inspects internals.\n */\nexport interface BlockPrinter {\n /** Tooltip / aria-label for the print button in the block header */\n label: string;\n\n /**\n * Variants offered to the user before printing.\n * Empty array = no options dialog shown; prints immediately with defaults.\n */\n variants: PrintVariant[];\n\n /**\n * Optional user-configurable settings shown below the variant picker.\n * Omit entirely if the block has no configurable settings.\n */\n settings?: PrintSetting[];\n\n /**\n * Execute the print.\n * data — the block's structured data (serverData or parsed content)\n * variantId — which variant was chosen (undefined = default / no variants)\n * settings — resolved setting values chosen by the user\n *\n * MAY resolve to a PrintOutcome (\"opened\" | \"downloaded\") so the host can\n * tell the user when the popup-blocked download fallback fired; `void`\n * stays legal for printers that don't go through a print window.\n */\n print: (\n data: unknown,\n variantId?: string,\n settings?: PrintSettings,\n ) => void | PrintOutcome | Promise<void | PrintOutcome>;\n}\n\n// ─── Shared Utilities ─────────────────────────────────────────────────────────\n\nconst PRINT_STYLES = `\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;\n font-size: 11pt;\n line-height: 1.6;\n color: #1a1a1a;\n background: #fff;\n max-width: 860px;\n margin: 0 auto;\n padding: 28px 36px;\n }\n\n .print-actions {\n display: flex;\n gap: 10px;\n margin-bottom: 24px;\n padding-bottom: 18px;\n border-bottom: 2px solid #e5e7eb;\n }\n\n .print-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 20px;\n border-radius: 8px;\n font-size: 13px;\n font-family: inherit;\n cursor: pointer;\n border: none;\n font-weight: 600;\n }\n\n .print-btn-primary { background: #2563eb; color: #fff; }\n .print-btn-secondary { background: #f3f4f6; color: #374151; border: 1px solid #d1d5db; }\n\n h1 { font-size: 22pt; font-weight: 700; margin-bottom: 0.5em; }\n h2 { font-size: 16pt; font-weight: 700; margin: 1.2em 0 0.4em; border-bottom: 1px solid #e5e7eb; padding-bottom: 0.2em; }\n h3 { font-size: 13pt; font-weight: 600; margin: 1em 0 0.3em; }\n p { margin-bottom: 0.7em; }\n\n table {\n width: 100%;\n border-collapse: collapse;\n margin: 1em 0;\n font-size: 10pt;\n }\n th {\n background: #1e293b;\n color: #f8fafc;\n font-weight: 600;\n text-align: left;\n padding: 7px 12px;\n border: 1px solid #334155;\n }\n td {\n padding: 6px 12px;\n border: 1px solid #e2e8f0;\n vertical-align: top;\n }\n tr:nth-child(even) td { background: #f8fafc; }\n\n @media print {\n body { max-width: 100%; padding: 0; }\n .print-actions { display: none !important; }\n th { -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n }\n`;\n\n/**\n * Wrap body HTML in a complete, styled print document.\n */\nexport function buildPrintDocument(\n bodyHtml: string,\n title = \"AI Response\",\n extraStyles = \"\",\n): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeHtml(title)}</title>\n <style>\n${PRINT_STYLES}\n${extraStyles}\n </style>\n</head>\n<body>\n <div class=\"print-actions\">\n <button class=\"print-btn print-btn-primary\" onclick=\"window.print()\">Print / Save as PDF</button>\n <button class=\"print-btn print-btn-secondary\" onclick=\"window.close()\">Close</button>\n </div>\n <div class=\"content\">\n${bodyHtml}\n </div>\n</body>\n</html>`;\n}\n\n/**\n * How a print request actually reached the user.\n *\n * \"opened\" — a print window/tab was opened and the document written into it.\n * \"downloaded\" — the popup was blocked, so the document was downloaded as an\n * .html file instead (the contract's fallback). Hosts SHOULD\n * surface this to the user (e.g. a toast saying the print file\n * was downloaded) — a blocked popup plus a silent download is\n * indistinguishable from \"nothing happened\", which is exactly\n * how QA filed it (2026-08-30, F5/F6/F8).\n */\nexport type PrintOutcome = \"opened\" | \"downloaded\";\n\n/**\n * Open a new browser window and write a complete HTML document into it.\n * Falls back to downloading an .html file if the popup is blocked.\n *\n * Returns which path was taken so callers can tell the user when the\n * fallback fired. `windowFeatures` customizes the popup geometry (the\n * flashcards fixed-geometry variants pass a wider window).\n */\n/**\n * Every printed document leaves through here, so this is where typeset math\n * gets its stylesheet: when the page carries KaTeX output and does not already\n * carry the sheet, it is added to <head>. A page with no math is untouched,\n * byte for byte (the flashcard golden master holds every printer to that).\n */\nexport function withMathStyles(htmlDocument: string): string {\n if (!htmlDocument.includes('class=\"katex\"') || htmlDocument.includes(KATEX_CSS_MARKER)) {\n return htmlDocument;\n }\n const style = `<style data-matrx-katex>${KATEX_CSS}</style>`;\n const head = htmlDocument.search(/<\\/head>/i);\n return head >= 0\n ? htmlDocument.slice(0, head) + style + htmlDocument.slice(head)\n : style + htmlDocument;\n}\n\nexport function openPrintWindow(\n rawDocument: string,\n filename = \"print\",\n windowFeatures = \"width=920,height=720,scrollbars=yes\",\n): PrintOutcome {\n const htmlDocument = withMathStyles(rawDocument);\n const win = window.open(\"\", \"_blank\", windowFeatures);\n // Popup blocked — fall back to file download.\n if (!win) return openPrintWindowFallback(htmlDocument, filename);\n win.document.open();\n win.document.write(htmlDocument);\n win.document.close();\n return \"opened\";\n}\n\n/** A print window opened before its document is ready (see `openPendingPrintWindow`). */\nexport interface PendingPrintWindow {\n /** Write the finished document; downloads it when the popup was blocked or closed. */\n write(htmlDocument: string): PrintOutcome;\n}\n\n/**\n * Open the print window NOW — inside the click, where popup blockers allow it —\n * and write the document later, once async preparation (drawing diagrams) is\n * done. The window says \"Preparing…\" meanwhile.\n */\nexport function openPendingPrintWindow(\n filename = \"print\",\n windowFeatures = \"width=920,height=720,scrollbars=yes\",\n): PendingPrintWindow {\n const win = window.open(\"\", \"_blank\", windowFeatures);\n if (win) {\n win.document.open();\n win.document.write(\n '<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Preparing…</title></head><body style=\"font:14px -apple-system,BlinkMacSystemFont,sans-serif;color:#555;padding:2rem\">Preparing the document for print…</body></html>',\n );\n win.document.close();\n }\n return {\n write(rawDocument: string): PrintOutcome {\n const htmlDocument = withMathStyles(rawDocument);\n if (!win || win.closed) return openPrintWindowFallback(htmlDocument, filename);\n win.document.open();\n win.document.write(htmlDocument);\n win.document.close();\n return \"opened\";\n },\n };\n}\n\nfunction openPrintWindowFallback(htmlDocument: string, filename: string): PrintOutcome {\n const blob = new Blob([htmlDocument], { type: \"text/html;charset=utf-8\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = `${filename.replace(/\\s+/g, \"-\").toLowerCase()}.html`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n // Revoke on a macrotask so the browser has started the blob fetch —\n // a synchronous revoke races the download in some engines.\n setTimeout(() => URL.revokeObjectURL(url), 0);\n return \"downloaded\";\n}\n\n/**\n * Shorthand: build the document and open it in one call.\n * Returns the same PrintOutcome as openPrintWindow.\n */\nexport function printHtmlContent(\n bodyHtml: string,\n title = \"AI Response\",\n extraStyles = \"\",\n): PrintOutcome {\n const doc = buildPrintDocument(bodyHtml, title, extraStyles);\n return openPrintWindow(doc, title);\n}\n\n/**\n * Build the auto-shrink script that runs INSIDE a printed window: for every\n * element matching each selector, its font-size is reduced (0.5pt steps, 6pt\n * floor) until it fits its parent element's box. This is the flashcards\n * FIT_TEXT behavior verbatim, parameterized by selector so every lane\n * (flashcards card text, Avery text, label captions) shares the ONE loop.\n *\n * Pass the same selectors the original used to get byte-identical behavior:\n * flashcards passes `[\".card-text .card-text-inner\", \".avery-text-wrap .avery-text\"]`.\n */\nexport function buildFitTextScript(selectors: readonly string[]): string {\n const selectorsJson = JSON.stringify([...selectors]);\n return `\n<script>\n(function() {\n function fitText(el, container) {\n // container is the bounding box we must fit inside\n var maxW = container.clientWidth;\n var maxH = container.clientHeight;\n var size = parseFloat(window.getComputedStyle(el).fontSize);\n var minSize = 6;\n // Give the element a chance to reflow at current size before measuring\n el.style.fontSize = size + \"pt\";\n while (size > minSize && (el.scrollWidth > maxW + 1 || el.scrollHeight > maxH + 1)) {\n size -= 0.5;\n el.style.fontSize = size + \"pt\";\n }\n }\n function runAll() {\n var selectors = ${selectorsJson};\n for (var s = 0; s < selectors.length; s++) {\n var els = document.querySelectorAll(selectors[s]);\n for (var i = 0; i < els.length; i++) {\n fitText(els[i], els[i].parentElement);\n }\n }\n }\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", runAll);\n } else {\n // document.write pages may already be loaded\n setTimeout(runAll, 0);\n }\n})();\n</script>`;\n}\n\n","// GENERATED by scripts/gen-katex-css.mjs from katex@0.18.9 — do not edit.\nexport const KATEX_VERSION = \"0.18.9\";\n/** Leads KATEX_CSS; lets openPrintWindow see the sheet is already present. */\nexport const KATEX_CSS_MARKER = \"/* matrx-katex@0.18.9 */\";\nexport const KATEX_CSS = \"/* matrx-katex@0.18.9 */@font-face{font-display:block;font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_AMS-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Caligraphic-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Fraktur-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-BoldItalic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Main-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-BoldItalic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Math-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:normal;font-weight:700;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Bold.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:italic;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Italic.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:\\\"KaTeX_SansSerif\\\";font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_SansSerif-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Script-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size1-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size2-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size3-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Size4-Regular.ttf) format(\\\"truetype\\\")}@font-face{font-display:block;font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.woff2) format(\\\"woff2\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.woff) format(\\\"woff\\\"),url(https://cdn.jsdelivr.net/npm/katex@0.18.9/dist/fonts/KaTeX_Typewriter-Regular.ttf) format(\\\"truetype\\\")}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;position:relative;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:\\\"0.18.9\\\"}.katex .katex-mathml{border:0;-webkit-clip-path:inset(50%);clip-path:inset(50%);height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.katex-newline{display:block}.katex .katex-base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .katex-base,.katex .katex-strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathboldfrak,.katex .textboldfrak{font-family:KaTeX_Fraktur;font-weight:700}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .mathsfit,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .katex-vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .katex-thinbox{display:inline-flex;flex-direction:row;max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline,.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-rule,.katex .katex-underline .underline-line,.katex .mfrac .frac-line{min-height:1px}.katex .mspace{display:inline-block}.katex .katex-smash{display:inline;line-height:0}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.katex-inner,.katex .llap>.katex-inner,.katex .rlap>.katex-inner{position:absolute}.katex .clap>.katex-fix,.katex .llap>.katex-fix,.katex .rlap>.katex-fix{display:inline-block}.katex .llap>.katex-inner{right:0}.katex .clap>.katex-inner,.katex .rlap>.katex-inner{left:0}.katex .clap>.katex-inner>span{margin-left:-50%;margin-right:50%}.katex .katex-rule{border:0 solid;display:inline-block;position:relative}.katex .katex-hline,.katex .katex-overline .overline-line,.katex .katex-underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .katex-hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.katex-root{margin-left:.2777777778em;margin-right:-.5555555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .katex-sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .katex-sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .katex-sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .katex-sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .katex-sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .katex-sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .katex-sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .katex-sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .katex-sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .katex-sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .katex-sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .katex-sizing.reset-size2.size1{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .katex-sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .katex-sizing.reset-size2.size3{font-size:1.1666666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .katex-sizing.reset-size2.size4{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .katex-sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .katex-sizing.reset-size2.size6{font-size:1.6666666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .katex-sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .katex-sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .katex-sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .katex-sizing.reset-size2.size10{font-size:3.4566666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .katex-sizing.reset-size2.size11{font-size:4.1466666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .katex-sizing.reset-size3.size1{font-size:.7142857143em}.katex .fontsize-ensurer.reset-size3.size2,.katex .katex-sizing.reset-size3.size2{font-size:.8571428571em}.katex .fontsize-ensurer.reset-size3.size3,.katex .katex-sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .katex-sizing.reset-size3.size4{font-size:1.1428571429em}.katex .fontsize-ensurer.reset-size3.size5,.katex .katex-sizing.reset-size3.size5{font-size:1.2857142857em}.katex .fontsize-ensurer.reset-size3.size6,.katex .katex-sizing.reset-size3.size6{font-size:1.4285714286em}.katex .fontsize-ensurer.reset-size3.size7,.katex .katex-sizing.reset-size3.size7{font-size:1.7142857143em}.katex .fontsize-ensurer.reset-size3.size8,.katex .katex-sizing.reset-size3.size8{font-size:2.0571428571em}.katex .fontsize-ensurer.reset-size3.size9,.katex .katex-sizing.reset-size3.size9{font-size:2.4685714286em}.katex .fontsize-ensurer.reset-size3.size10,.katex .katex-sizing.reset-size3.size10{font-size:2.9628571429em}.katex .fontsize-ensurer.reset-size3.size11,.katex .katex-sizing.reset-size3.size11{font-size:3.5542857143em}.katex .fontsize-ensurer.reset-size4.size1,.katex .katex-sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .katex-sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .katex-sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .katex-sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .katex-sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .katex-sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .katex-sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .katex-sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .katex-sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .katex-sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .katex-sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .katex-sizing.reset-size5.size1{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .katex-sizing.reset-size5.size2{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .katex-sizing.reset-size5.size3{font-size:.7777777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .katex-sizing.reset-size5.size4{font-size:.8888888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .katex-sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .katex-sizing.reset-size5.size6{font-size:1.1111111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .katex-sizing.reset-size5.size7{font-size:1.3333333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .katex-sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .katex-sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .katex-sizing.reset-size5.size10{font-size:2.3044444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .katex-sizing.reset-size5.size11{font-size:2.7644444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .katex-sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .katex-sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .katex-sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .katex-sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .katex-sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .katex-sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .katex-sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .katex-sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .katex-sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .katex-sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .katex-sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .katex-sizing.reset-size7.size1{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .katex-sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .katex-sizing.reset-size7.size3{font-size:.5833333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .katex-sizing.reset-size7.size4{font-size:.6666666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .katex-sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .katex-sizing.reset-size7.size6{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .katex-sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .katex-sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .katex-sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .katex-sizing.reset-size7.size10{font-size:1.7283333333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .katex-sizing.reset-size7.size11{font-size:2.0733333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .katex-sizing.reset-size8.size1{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .katex-sizing.reset-size8.size2{font-size:.4166666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .katex-sizing.reset-size8.size3{font-size:.4861111111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .katex-sizing.reset-size8.size4{font-size:.5555555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .katex-sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .katex-sizing.reset-size8.size6{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .katex-sizing.reset-size8.size7{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .katex-sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .katex-sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .katex-sizing.reset-size8.size10{font-size:1.4402777778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .katex-sizing.reset-size8.size11{font-size:1.7277777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .katex-sizing.reset-size9.size1{font-size:.2893518519em}.katex .fontsize-ensurer.reset-size9.size2,.katex .katex-sizing.reset-size9.size2{font-size:.3472222222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .katex-sizing.reset-size9.size3{font-size:.4050925926em}.katex .fontsize-ensurer.reset-size9.size4,.katex .katex-sizing.reset-size9.size4{font-size:.462962963em}.katex .fontsize-ensurer.reset-size9.size5,.katex .katex-sizing.reset-size9.size5{font-size:.5208333333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .katex-sizing.reset-size9.size6{font-size:.5787037037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .katex-sizing.reset-size9.size7{font-size:.6944444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .katex-sizing.reset-size9.size8{font-size:.8333333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .katex-sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .katex-sizing.reset-size9.size10{font-size:1.2002314815em}.katex .fontsize-ensurer.reset-size9.size11,.katex .katex-sizing.reset-size9.size11{font-size:1.4398148148em}.katex .fontsize-ensurer.reset-size10.size1,.katex .katex-sizing.reset-size10.size1{font-size:.2410800386em}.katex .fontsize-ensurer.reset-size10.size2,.katex .katex-sizing.reset-size10.size2{font-size:.2892960463em}.katex .fontsize-ensurer.reset-size10.size3,.katex .katex-sizing.reset-size10.size3{font-size:.337512054em}.katex .fontsize-ensurer.reset-size10.size4,.katex .katex-sizing.reset-size10.size4{font-size:.3857280617em}.katex .fontsize-ensurer.reset-size10.size5,.katex .katex-sizing.reset-size10.size5{font-size:.4339440694em}.katex .fontsize-ensurer.reset-size10.size6,.katex .katex-sizing.reset-size10.size6{font-size:.4821600771em}.katex .fontsize-ensurer.reset-size10.size7,.katex .katex-sizing.reset-size10.size7{font-size:.5785920926em}.katex .fontsize-ensurer.reset-size10.size8,.katex .katex-sizing.reset-size10.size8{font-size:.6943105111em}.katex .fontsize-ensurer.reset-size10.size9,.katex .katex-sizing.reset-size10.size9{font-size:.8331726133em}.katex .fontsize-ensurer.reset-size10.size10,.katex .katex-sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .katex-sizing.reset-size10.size11{font-size:1.1996142719em}.katex .fontsize-ensurer.reset-size11.size1,.katex .katex-sizing.reset-size11.size1{font-size:.2009646302em}.katex .fontsize-ensurer.reset-size11.size2,.katex .katex-sizing.reset-size11.size2{font-size:.2411575563em}.katex .fontsize-ensurer.reset-size11.size3,.katex .katex-sizing.reset-size11.size3{font-size:.2813504823em}.katex .fontsize-ensurer.reset-size11.size4,.katex .katex-sizing.reset-size11.size4{font-size:.3215434084em}.katex .fontsize-ensurer.reset-size11.size5,.katex .katex-sizing.reset-size11.size5{font-size:.3617363344em}.katex .fontsize-ensurer.reset-size11.size6,.katex .katex-sizing.reset-size11.size6{font-size:.4019292605em}.katex .fontsize-ensurer.reset-size11.size7,.katex .katex-sizing.reset-size11.size7{font-size:.4823151125em}.katex .fontsize-ensurer.reset-size11.size8,.katex .katex-sizing.reset-size11.size8{font-size:.578778135em}.katex .fontsize-ensurer.reset-size11.size9,.katex .katex-sizing.reset-size11.size9{font-size:.6945337621em}.katex .fontsize-ensurer.reset-size11.size10,.katex .katex-sizing.reset-size11.size10{font-size:.8336012862em}.katex .fontsize-ensurer.reset-size11.size11,.katex .katex-sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .katex-accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .katex-accent .accent-body{position:relative}.katex .katex-accent .accent-body:not(.accent-full){width:0}.katex .katex-overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex svg{fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .katex-stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .katex-stretchy:after,.katex .katex-stretchy:before{content:\\\"\\\"}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .katex-sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .reflectbox{display:inline-block;transform:scaleX(-1)}.katex .eqn-num:before{content:\\\"(\\\" counter(katexEqnNo) \\\")\\\";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:\\\"(\\\" counter(mmlEqnNo) \\\")\\\";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.katex-tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.katex-tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}\\n\";\n","/**\n * @ai-matrx/print/labels — the label-sheet engine.\n *\n * ONE module carrying the three matrx-frontend files this lane was extracted\n * from, ported behaviour-verbatim (2026-08-29 state):\n * lib/label-print/label-templates.ts → the template registry\n * lib/label-print/qr-labels-printer.ts → qrLabelsPrinter / printQrLabelSheet\n * lib/label-print/qr-labels-pdf.ts → downloadLabelsPdf\n *\n * Warehouse-grade QR labels on standard 8.5\" × 11\" label stock (Avery and\n * compatibles) AND on continuous/die-cut roll stock. ONE generic grid renderer\n * driven by the LabelTemplate registry — geometry is data, never per-stock CSS.\n *\n * Fidelity rules (the reasons this exists):\n * - The print window is a fresh, unauthenticated document: every QR image is\n * inlined as a data URI, never fetched.\n * - Spec-compliant quiet zone: the `qrcode` lib renders 4 modules of white\n * margin INSIDE each data URI (its default; we pass it explicitly).\n * - `print-color-adjust: exact` on code cells so the browser never \"helps\".\n * - Modules are crisp: each QR is rasterized at 300 dpi for its printed size,\n * and `image-rendering: pixelated` stops resampling blur.\n * - Captions auto-shrink to fit their box (FIT_TEXT pattern) — they never\n * overflow onto the neighboring label.\n * - `startAtLabel` inserts LEADING blank cells so a partially-used sheet can\n * be reused; trailing cells pad out the last page.\n *\n * THE IMAGE RULE — the one deliberate exception to \"data URIs only\": a label's\n * optional `imageUrl` is a DURABLE http(s) URL and is emitted as a plain\n * `<img src>`. Anything that is not http(s) is SKIPPED rather than rendered.\n * The QR itself is never a URL; it is always an inlined data URI.\n *\n * SEAM INVERSIONS versus the matrx-frontend originals (the only changes to the\n * ported behaviour):\n * 1. `@/lib/block-print/block-print-utils` → `./core` (identical export names).\n * 2. The deferred `import(\"qrcode\")` + `QRCode.toDataURL(...)` call is now\n * `generateQrDataUrl` from `./qr`, which performs the same lazy import at\n * call time and maps 1:1 — errorCorrectionLevel → ecLevel, margin →\n * quietZoneModules, width → widthPx. The original also passed\n * `color: { dark: \"#000000\", light: \"#ffffff\" }`, which is byte-for-byte the\n * `qrcode` default, so dropping it changes no output.\n * 3. LP_FIT_SCRIPT is KEPT VERBATIM rather than replaced by core's\n * `buildFitTextScript`. The two scripts are NOT behaviourally identical for\n * label cells: core's steps and writes font sizes in **pt** (and pre-assigns\n * `size + \"pt\"` from a px-derived number before measuring) with a **6pt**\n * floor, while the label script steps and writes **px** with a **4px** floor\n * and no pre-assignment. On a 9pt caption inside a 0.28in box those two\n * produce different final sizes, so swapping them would regress captions on\n * the small Avery stocks.\n *\n * Docs: lib/label-print/FEATURE.md (matrx-frontend), NOTES-CHANGELOG.md.\n */\n\nimport {\n openPrintWindow,\n type BlockPrinter,\n type PrintOutcome,\n type PrintSettings,\n} from \"./core\";\nimport { escapeHtml } from \"@ai-matrx/kit/html-escape\";\nimport { generateQrDataUrl, minQrVersion, qrModuleCount } from \"./qr\";\n\n// ─── Template registry ───────────────────────────────────────────────────────\n\n/**\n * Label-sheet template registry — the geometry source of truth for QR label\n * printing on standard 8.5\" × 11\" label stock (Avery and compatibles) and on\n * roll stock.\n *\n * ONE generic grid renderer is driven entirely by these numbers — never a\n * hand-written CSS block per stock. All dimensions in inches, taken from\n * Avery's published templates:\n * - horizontal pitch = labelWIn + gutterXIn; vertical pitch = labelHIn + gutterYIn\n * - a template is valid when marginLeft*2 + cols*labelW + (cols-1)*gutterX = sheetW\n * (same vertically) — `assertTemplateGeometry` checks this in dev.\n */\nexport interface LabelTemplate {\n id: string;\n name: string;\n /** Stock code as printed on the box (e.g. \"Avery 5160\"). */\n stockCode: string;\n sheetWIn: number;\n sheetHIn: number;\n marginTopIn: number;\n marginLeftIn: number;\n labelWIn: number;\n labelHIn: number;\n cols: number;\n rows: number;\n /** Horizontal gap BETWEEN columns (not the outer margin). */\n gutterXIn: number;\n /** Vertical gap BETWEEN rows. */\n gutterYIn: number;\n /** Corner radius; equal to labelWIn/2 for round labels. */\n cornerRadiusIn?: number;\n /** True round label (die-cut circle) — preview/calibration draw a circle. */\n round?: boolean;\n /**\n * Stock family. Absent means \"sheet\" — a multi-up page of labels. \"roll\" is\n * a continuous/die-cut roll where the sheet IS the label: margins 0, a 1×1\n * grid, and `@page` sized to the single label.\n */\n kind?: \"sheet\" | \"roll\";\n}\n\n/**\n * The registry's first row, hoisted to a named const so the \"no template\n * matched\" fallback can reference it without an unchecked index access.\n *\n * 5160 — 1\" × 2⅝\" address, 30-up (3×10). Top/bottom margin 0.5\",\n * side margin 0.1875\", 0.125\" column gutter, zero row gutter.\n */\nconst AVERY_5160: LabelTemplate = {\n id: \"avery-5160\",\n name: 'Address 1\" × 2⅝\" — 30 per sheet',\n stockCode: \"Avery 5160 / 8160\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.5,\n marginLeftIn: 0.1875,\n labelWIn: 2.625,\n labelHIn: 1,\n cols: 3,\n rows: 10,\n gutterXIn: 0.125,\n gutterYIn: 0,\n cornerRadiusIn: 0.1,\n};\n\n/**\n * Seeded templates. Sheet geometry verified against Avery's published template\n * dimensions (avery.com template pages for each SKU):\n *\n * 5160 — see AVERY_5160 above.\n * 5163 — 2\" × 4\" shipping, 10-up (2×5). Top/bottom 0.5\", side 0.15625\",\n * 0.1875\" column gutter, zero row gutter.\n * 5164 — 3⅓\" × 4\" shipping, 6-up (2×3). Same horizontal grid as 5163.\n * 22806 — 2\" × 2\" rounded square, 12-up (3×4). Margins 0.5\" sides /\n * 0.75\" top, 0.75\" column gutter, 0.5\" row gutter — i.e. Avery's\n * published 2.75\" horizontal pitch and 2.5\" vertical pitch. (The\n * matrx-frontend original had 0.375\"/0.375\" here and tiled only 7.5\"\n * of the 8.5\" sheet; see NOTES-CHANGELOG.md.)\n * 22807 — 2\" round, 12-up (3×4). Same grid as 22806, circular die-cut.\n *\n * Roll stock (`kind: \"roll\"`) — the sheet IS the label, so every roll row is a\n * 1×1 grid with zero margins and zero gutters, and its die-cut size is given\n * as width × height in the feed orientation:\n *\n * DK-1202 — Brother die-cut shipping, 62mm × 100mm = 2.4\" × 3.9\".\n * DK-11201 — Brother die-cut address, 29mm × 90mm = 1.14\" × 3.5\".\n * 30252 — DYMO address, 1⅛\" × 3½\".\n * 4x6 — the generic 4\" × 6\" thermal shipping label.\n */\nexport const LABEL_TEMPLATES: readonly LabelTemplate[] = [\n AVERY_5160,\n {\n id: \"avery-5163\",\n name: 'Shipping 2\" × 4\" — 10 per sheet',\n stockCode: \"Avery 5163 / 8163\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.5,\n marginLeftIn: 0.15625,\n labelWIn: 4,\n labelHIn: 2,\n cols: 2,\n rows: 5,\n gutterXIn: 0.1875,\n gutterYIn: 0,\n cornerRadiusIn: 0.125,\n },\n {\n id: \"avery-5164\",\n name: 'Shipping 3⅓\" × 4\" — 6 per sheet',\n stockCode: \"Avery 5164 / 8164\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.5,\n marginLeftIn: 0.15625,\n labelWIn: 4,\n labelHIn: 3.3333333,\n cols: 2,\n rows: 3,\n gutterXIn: 0.1875,\n gutterYIn: 0,\n cornerRadiusIn: 0.125,\n },\n {\n id: \"avery-22806\",\n name: 'Square 2\" × 2\" — 12 per sheet',\n stockCode: \"Avery 22806\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.75,\n marginLeftIn: 0.5,\n labelWIn: 2,\n labelHIn: 2,\n cols: 3,\n rows: 4,\n gutterXIn: 0.75,\n gutterYIn: 0.5,\n cornerRadiusIn: 0.09375,\n },\n {\n id: \"avery-22807\",\n name: 'Round 2\" — 12 per sheet',\n stockCode: \"Avery 22807\",\n sheetWIn: 8.5,\n sheetHIn: 11,\n marginTopIn: 0.75,\n marginLeftIn: 0.5,\n labelWIn: 2,\n labelHIn: 2,\n cols: 3,\n rows: 4,\n gutterXIn: 0.75,\n gutterYIn: 0.5,\n cornerRadiusIn: 1,\n round: true,\n },\n {\n id: \"brother-dk-1202\",\n name: 'Shipping roll 2.4\" × 3.9\" — die-cut',\n stockCode: \"Brother DK-1202\",\n sheetWIn: 2.4,\n sheetHIn: 3.9,\n marginTopIn: 0,\n marginLeftIn: 0,\n labelWIn: 2.4,\n labelHIn: 3.9,\n cols: 1,\n rows: 1,\n gutterXIn: 0,\n gutterYIn: 0,\n kind: \"roll\",\n },\n {\n id: \"brother-dk-11201\",\n name: 'Address roll 1.14\" × 3.5\" — die-cut',\n stockCode: \"Brother DK-11201\",\n sheetWIn: 1.14,\n sheetHIn: 3.5,\n marginTopIn: 0,\n marginLeftIn: 0,\n labelWIn: 1.14,\n labelHIn: 3.5,\n cols: 1,\n rows: 1,\n gutterXIn: 0,\n gutterYIn: 0,\n kind: \"roll\",\n },\n {\n id: \"dymo-30252\",\n name: 'Address roll 1⅛\" × 3½\"',\n stockCode: \"DYMO 30252\",\n sheetWIn: 1.125,\n sheetHIn: 3.5,\n marginTopIn: 0,\n marginLeftIn: 0,\n labelWIn: 1.125,\n labelHIn: 3.5,\n cols: 1,\n rows: 1,\n gutterXIn: 0,\n gutterYIn: 0,\n kind: \"roll\",\n },\n {\n id: \"roll-4x6\",\n name: 'Shipping roll 4\" × 6\"',\n stockCode: \"Thermal 4x6\",\n sheetWIn: 4,\n sheetHIn: 6,\n marginTopIn: 0,\n marginLeftIn: 0,\n labelWIn: 4,\n labelHIn: 6,\n cols: 1,\n rows: 1,\n gutterXIn: 0,\n gutterYIn: 0,\n kind: \"roll\",\n },\n];\n\nexport function getLabelTemplate(id: string): LabelTemplate | undefined {\n return LABEL_TEMPLATES.find((t) => t.id === id);\n}\n\n/**\n * Build a `custom` template for stock not in the registry. Callers supply the\n * full geometry; id is fixed to \"custom\".\n *\n * The geometry spread comes FIRST so an explicitly-present `name: undefined` /\n * `stockCode: undefined` cannot overwrite the resolved default (see\n * NOTES-CHANGELOG.md).\n */\nexport function customLabelTemplate(\n geometry: Omit<LabelTemplate, \"id\" | \"name\" | \"stockCode\"> &\n Partial<Pick<LabelTemplate, \"name\" | \"stockCode\">>,\n): LabelTemplate {\n return {\n ...geometry,\n id: \"custom\",\n name: geometry.name ?? \"Custom stock\",\n stockCode: geometry.stockCode ?? \"custom\",\n };\n}\n\n/** Dev-time sanity check: the grid must tile the sheet exactly (±1/64\"). */\nexport function assertTemplateGeometry(t: LabelTemplate): void {\n const w =\n t.marginLeftIn * 2 + t.cols * t.labelWIn + (t.cols - 1) * t.gutterXIn;\n const h =\n t.marginTopIn * 2 + t.rows * t.labelHIn + (t.rows - 1) * t.gutterYIn;\n const tol = 1 / 64;\n if (Math.abs(w - t.sheetWIn) > tol || Math.abs(h - t.sheetHIn) > tol) {\n console.error(\n `[label-print] Template ${t.id} does not tile its sheet: grid ${w.toFixed(4)}×${h.toFixed(4)}in vs sheet ${t.sheetWIn}×${t.sheetHIn}in`,\n );\n }\n}\n\n// Pure math, no side effects beyond a console.error on drift — the module\n// stays import-inert. `process` is guarded because this ships as an npm\n// package that runs in browsers, where a bare `process.env` read throws.\nif (\n typeof process === \"undefined\" ||\n process.env.NODE_ENV !== \"production\"\n) {\n LABEL_TEMPLATES.forEach(assertTemplateGeometry);\n}\n\n// ─── Data contract ───────────────────────────────────────────────────────────\n\nexport interface QrLabel {\n /** The value encoded in the QR code (SKU, URL, id…). */\n qrValue: string;\n /** Bold headline under/next to the code. Defaults to qrValue when captions are on. */\n caption?: string;\n /** Additional small text lines (wide labels only — there is no room on tiny stock). */\n lines?: string[];\n /**\n * A single short, LARGE, bold token (a garment size, a bin letter, a grade).\n * Rendered in the caption zone ABOVE the caption at ~2× the caption size and\n * auto-shrunk by the fit script.\n */\n badge?: string;\n /**\n * Durable http(s) image URL rendered in the lines zone with\n * `object-fit: contain`. Anything that is not http(s) is skipped — THE IMAGE\n * RULE. Ignored by the PDF lane, which cannot fetch a remote image.\n */\n imageUrl?: string;\n}\n\nexport interface QrLabelPrintData {\n labels: QrLabel[];\n /** Default template; the dialog's variant picker overrides it. */\n templateId: string;\n /** Optional custom-stock geometry used when templateId === \"custom\". */\n customTemplate?: LabelTemplate;\n}\n\nexport type QrEcLevel = \"L\" | \"M\" | \"Q\";\n\nexport interface QrLabelSettings {\n startAtLabel: number; // 1-based first cell to print on\n copiesPerCode: number;\n showCaption: boolean;\n showLines: boolean;\n rangeFrom: number; // 1-based, 0 = no bound\n rangeTo: number;\n ecLevel: QrEcLevel;\n calibrationPage: boolean;\n}\n\nconst DEFAULTS: QrLabelSettings = {\n startAtLabel: 1,\n copiesPerCode: 1,\n showCaption: true,\n showLines: true,\n rangeFrom: 0,\n rangeTo: 0,\n ecLevel: \"M\",\n calibrationPage: false,\n};\n\nfunction resolveSettings(s?: PrintSettings): QrLabelSettings {\n const num = (v: unknown, d: number, min = 0) =>\n typeof v === \"number\" && !Number.isNaN(v) ? Math.max(min, v) : d;\n const ec = s?.ecLevel;\n return {\n startAtLabel: num(s?.startAtLabel, DEFAULTS.startAtLabel, 1),\n copiesPerCode: num(s?.copiesPerCode, DEFAULTS.copiesPerCode, 1),\n showCaption: (s?.showCaption ?? DEFAULTS.showCaption) as boolean,\n showLines: (s?.showLines ?? DEFAULTS.showLines) as boolean,\n rangeFrom: num(s?.rangeFrom, 0),\n rangeTo: num(s?.rangeTo, 0),\n ecLevel: ec === \"L\" || ec === \"Q\" ? ec : \"M\",\n calibrationPage: (s?.calibrationPage ?? false) as boolean,\n };\n}\n\n// ─── QR generation (data URIs, inlined) ──────────────────────────────────────\n\nconst PRINT_DPI = 300;\n/** Quiet zone: QR spec requires ≥4 modules of clear margin around the symbol. */\nconst QUIET_ZONE_MODULES = 4;\n\nexport async function generateQrDataUri(\n value: string,\n ecLevel: QrEcLevel,\n sizeInches: number,\n): Promise<string> {\n // Deferred to call time (user click) — `./qr` lazy-imports `qrcode` inside\n // generateQrDataUrl, so this module never puts it on a shell graph.\n return generateQrDataUrl(value, {\n ecLevel,\n quietZoneModules: QUIET_ZONE_MODULES,\n widthPx: Math.round(sizeInches * PRINT_DPI),\n });\n}\n\n// ─── Cell geometry ───────────────────────────────────────────────────────────\n\ninterface CellLayout {\n /** true = QR left, text right; false = QR on top, caption below. */\n wide: boolean;\n qrIn: number;\n padIn: number;\n /** Height reserved above the caption for a `badge`; 0 when there is none. */\n badgeZoneIn: number;\n}\n\nexport function computeCellLayout(\n t: LabelTemplate,\n showCaption: boolean,\n hasBadge = false,\n): CellLayout {\n const padIn = Math.min(0.08, t.labelHIn * 0.06);\n const wide = t.labelWIn / t.labelHIn >= 1.8;\n const badgeZoneIn = hasBadge ? Math.min(0.4, t.labelHIn * 0.22) : 0;\n if (wide) {\n return { wide, qrIn: t.labelHIn - padIn * 2, padIn, badgeZoneIn };\n }\n // Stacked. Round labels inscribe the content square inside the circle.\n const inset = t.round ? t.labelWIn * 0.15 : 0;\n const captionZone = showCaption ? Math.min(0.28, t.labelHIn * 0.16) : 0;\n const qrIn = Math.min(\n t.labelWIn - padIn * 2 - inset * 2,\n t.labelHIn - padIn * 2 - inset * 2 - captionZone - badgeZoneIn,\n );\n return { wide, qrIn, padIn, badgeZoneIn };\n}\n\n// ─── Generic grid renderer ───────────────────────────────────────────────────\n\nfunction sheetStyles(t: LabelTemplate): string {\n const radius = t.round\n ? `${t.labelWIn / 2}in`\n : t.cornerRadiusIn\n ? `${t.cornerRadiusIn}in`\n : \"0\";\n return `\n @page { size: ${t.sheetWIn}in ${t.sheetHIn}in; margin: 0; }\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n html, body { width: ${t.sheetWIn}in; background: #fff; color: #000;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif; }\n\n .lp-page {\n width: ${t.sheetWIn}in; height: ${t.sheetHIn}in;\n position: relative; overflow: hidden;\n page-break-after: always; break-after: page;\n border-bottom: 1px solid #e2e8f0; /* screen-only separator */\n }\n .lp-page:last-child { page-break-after: auto; break-after: auto; border-bottom: none; }\n\n .lp-cell {\n position: absolute;\n width: ${t.labelWIn}in; height: ${t.labelHIn}in;\n border-radius: ${radius};\n overflow: hidden;\n -webkit-print-color-adjust: exact; print-color-adjust: exact;\n }\n .lp-qr {\n display: block;\n image-rendering: pixelated; /* no resampling blur on crisp modules */\n }\n .lp-cell-wide { display: flex; align-items: center; }\n .lp-cell-stacked { display: flex; flex-direction: column; align-items: center; justify-content: center; }\n\n .lp-text { flex: 1 1 auto; min-width: 0; overflow: hidden; align-self: stretch;\n display: flex; flex-direction: column; justify-content: center; }\n .lp-fit-wrap { overflow: hidden; }\n .lp-caption { font-weight: 700; line-height: 1.15; word-break: break-word; }\n .lp-line { line-height: 1.25; word-break: break-word; }\n .lp-caption-stacked { text-align: center; width: 100%; }\n\n /* Badge — the LARGE token above the caption (garment size, bin letter). */\n .lp-badge { font-weight: 800; line-height: 1.05; word-break: break-word;\n -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n .lp-badge-stacked { text-align: center; width: 100%; }\n\n /* Durable http(s) artwork in the lines zone. */\n .lp-img { display: block; width: 100%; object-fit: contain; }\n\n /* Calibration */\n .lp-cal-cell { border: 0.5pt dashed #94a3b8; background: transparent; }\n .lp-cal-label { position: absolute; inset: 0; display: flex; align-items: center;\n justify-content: center; font-size: 7pt; color: #94a3b8; text-align: center; padding: 4px; }\n .lp-crop { position: absolute; background: #94a3b8;\n -webkit-print-color-adjust: exact; print-color-adjust: exact; }\n\n @media print {\n html, body { height: auto; overflow: visible; }\n .lp-page { border-bottom: none; }\n .screen-only { display: none !important; }\n }\n`;\n}\n\nfunction cellPosition(t: LabelTemplate, index: number): { x: number; y: number } {\n const col = index % t.cols;\n const row = Math.floor(index / t.cols);\n return {\n x: t.marginLeftIn + col * (t.labelWIn + t.gutterXIn),\n y: t.marginTopIn + row * (t.labelHIn + t.gutterYIn),\n };\n}\n\n/**\n * Caption auto-shrink — same pattern as the flashcards FIT_TEXT_SCRIPT.\n *\n * Kept verbatim instead of core's `buildFitTextScript`: this loop measures and\n * writes **px** with a 4px floor, core's writes **pt** with a 6pt floor and\n * pre-assigns the size before measuring. See the module header.\n */\nconst LP_FIT_SCRIPT = `\n<script>\n(function () {\n function fit(el, box) {\n var size = parseFloat(window.getComputedStyle(el).fontSize);\n var min = 4;\n while (size > min && (el.scrollWidth > box.clientWidth + 1 || el.scrollHeight > box.clientHeight + 1)) {\n size -= 0.5;\n el.style.fontSize = size + \"px\";\n }\n }\n function run() {\n var els = document.querySelectorAll(\".lp-fit-wrap .lp-fit\");\n for (var i = 0; i < els.length; i++) fit(els[i], els[i].parentElement);\n }\n if (document.readyState === \"loading\") document.addEventListener(\"DOMContentLoaded\", run);\n else setTimeout(run, 0);\n})();\n</script>`;\n\ninterface RenderedCell {\n html: string; // inner HTML of the cell, \"\" for a blank pad cell\n}\n\n/** THE IMAGE RULE: only a durable http(s) URL is ever rendered. */\nfunction isHttpUrl(value: string | undefined): value is string {\n return typeof value === \"string\" && /^https?:\\/\\//i.test(value.trim());\n}\n\nfunction buildLabelCellHtml(\n label: QrLabel,\n qrDataUri: string,\n t: LabelTemplate,\n layout: CellLayout,\n s: QrLabelSettings,\n): string {\n const qr = qrDataUri\n ? `<img class=\"lp-qr\" src=\"${qrDataUri}\" alt=\"\" style=\"width:${layout.qrIn}in;height:${layout.qrIn}in;\" />`\n : \"\";\n const captionText = s.showCaption ? (label.caption ?? label.qrValue) : \"\";\n const badgeText = label.badge?.trim() ? label.badge.trim() : \"\";\n\n if (layout.wide) {\n const lines =\n s.showLines && label.lines?.length\n ? label.lines\n .filter((l) => l.trim())\n .map(\n (l) =>\n `<div class=\"lp-line\" style=\"font-size:8pt;\">${escapeHtml(l)}</div>`,\n )\n .join(\"\")\n : \"\";\n const caption = captionText\n ? `<div class=\"lp-fit-wrap\" style=\"max-height:${(t.labelHIn * 0.3).toFixed(3)}in;\"><div class=\"lp-fit lp-caption\" style=\"font-size:12pt;\">${escapeHtml(captionText)}</div></div>`\n : \"\";\n const badge = badgeText\n ? `<div class=\"lp-fit-wrap\" style=\"max-height:${layout.badgeZoneIn.toFixed(3)}in;\"><div class=\"lp-fit lp-badge\" style=\"font-size:24pt;\">${escapeHtml(badgeText)}</div></div>`\n : \"\";\n const image = isHttpUrl(label.imageUrl)\n ? `<img class=\"lp-img\" src=\"${escapeHtml(label.imageUrl)}\" alt=\"\" style=\"height:${(t.labelHIn * 0.35).toFixed(3)}in;\" />`\n : \"\";\n return `<div class=\"lp-cell-wide\" style=\"height:100%;padding:${layout.padIn}in;gap:${layout.padIn}in;\">\n ${qr}\n <div class=\"lp-text\">${badge}${caption}${lines}${image}</div>\n </div>`;\n }\n\n const caption = captionText\n ? `<div class=\"lp-fit-wrap lp-caption-stacked\" style=\"height:${Math.min(0.28, t.labelHIn * 0.16).toFixed(3)}in;\"><div class=\"lp-fit lp-caption\" style=\"font-size:9pt;\">${escapeHtml(captionText)}</div></div>`\n : \"\";\n const badge = badgeText\n ? `<div class=\"lp-fit-wrap lp-badge-stacked\" style=\"height:${layout.badgeZoneIn.toFixed(3)}in;\"><div class=\"lp-fit lp-badge\" style=\"font-size:18pt;\">${escapeHtml(badgeText)}</div></div>`\n : \"\";\n return `<div class=\"lp-cell-stacked\" style=\"height:100%;padding:${layout.padIn}in;\">\n ${qr}\n ${badge}\n ${caption}\n </div>`;\n}\n\nfunction renderPages(t: LabelTemplate, cells: RenderedCell[]): string {\n const perPage = t.cols * t.rows;\n const pages: string[] = [];\n for (let p = 0; p < Math.max(1, Math.ceil(cells.length / perPage)); p++) {\n const batch = cells.slice(p * perPage, (p + 1) * perPage);\n const cellHtml = batch\n .map((c, i) => {\n const { x, y } = cellPosition(t, i);\n return `<div class=\"lp-cell\" style=\"left:${x}in;top:${y}in;\">${c.html}</div>`;\n })\n .join(\"\\n\");\n pages.push(`<div class=\"lp-page\">${cellHtml}</div>`);\n }\n return pages.join(\"\\n\");\n}\n\nfunction openSheetWindow(\n t: LabelTemplate,\n bodyHtml: string,\n title: string,\n withFitScript: boolean,\n warning?: string,\n): PrintOutcome {\n const warningHtml = warning\n ? `<div class=\"screen-only lp-warning\" style=\"font-family:sans-serif;font-size:12px;font-weight:600;padding:9px 16px;background:#fef2f2;color:#991b1b;border-bottom:1px solid #fecaca;\">${escapeHtml(warning)}</div>`\n : \"\";\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <title>${escapeHtml(title)} — ${escapeHtml(t.stockCode)}</title>\n <style>${sheetStyles(t)}</style>\n</head>\n<body>\n${warningHtml}\n<div class=\"screen-only\" style=\"font-family:sans-serif;font-size:12px;padding:10px 16px;background:#f8fafc;border-bottom:1px solid #e2e8f0;display:flex;gap:10px;align-items:center;flex-wrap:wrap;\">\n <button onclick=\"window.print()\" style=\"padding:7px 18px;background:#111;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer;\">Print / Save PDF</button>\n <button onclick=\"window.close()\" style=\"padding:7px 14px;background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px;font-size:13px;cursor:pointer;\">Close</button>\n <span style=\"color:#64748b;font-size:11px;\">${escapeHtml(t.stockCode)} · ${escapeHtml(t.name)} · set printer to <strong>100% scale (no \"fit to page\"), no margins, Portrait</strong></span>\n</div>\n${bodyHtml}\n${withFitScript ? LP_FIT_SCRIPT : \"\"}\n</body>\n</html>`;\n return openPrintWindow(html, title);\n}\n\n// ─── Label selection (range → copies → startAt offset) ──────────────────────\n\nexport function expandLabels(labels: QrLabel[], s: QrLabelSettings): QrLabel[] {\n const from = s.rangeFrom > 0 ? s.rangeFrom : 1;\n const to = s.rangeTo > 0 ? Math.min(s.rangeTo, labels.length) : labels.length;\n const subset = labels.slice(from - 1, to);\n if (s.copiesPerCode <= 1) return subset;\n const out: QrLabel[] = [];\n for (const l of subset)\n for (let i = 0; i < s.copiesPerCode; i++) out.push(l);\n return out;\n}\n\n// ─── Calibration test page ───────────────────────────────────────────────────\n\nfunction buildCalibrationBody(t: LabelTemplate): string {\n const perPage = t.cols * t.rows;\n const cells: string[] = [];\n for (let i = 0; i < perPage; i++) {\n const { x, y } = cellPosition(t, i);\n cells.push(\n `<div class=\"lp-cell lp-cal-cell\" style=\"left:${x}in;top:${y}in;\"><div class=\"lp-cal-label\">${i + 1}</div></div>`,\n );\n }\n // Crop marks at the four corners of the label grid area\n const gridR = t.sheetWIn - t.marginLeftIn;\n const gridB = t.sheetHIn - t.marginTopIn;\n const mark = (x: number, y: number, h: boolean) =>\n `<div class=\"lp-crop\" style=\"left:${x}in;top:${y}in;width:${h ? 0.25 : 0.0104}in;height:${h ? 0.0104 : 0.25}in;\"></div>`;\n const marks = [\n mark(t.marginLeftIn - 0.25, t.marginTopIn, true),\n mark(t.marginLeftIn, t.marginTopIn - 0.25, false),\n mark(gridR, t.marginTopIn, true),\n mark(gridR - 0.0104, t.marginTopIn - 0.25, false),\n mark(t.marginLeftIn - 0.25, gridB - 0.0104, true),\n mark(t.marginLeftIn, gridB, false),\n mark(gridR, gridB - 0.0104, true),\n mark(gridR - 0.0104, gridB, false),\n ].join(\"\");\n const banner = `<div style=\"position:absolute;left:0;right:0;top:${Math.max(0, t.marginTopIn - 0.3).toFixed(2)}in;text-align:center;font-size:7pt;color:#94a3b8;\">Calibration — ${escapeHtml(t.stockCode)} · hold against your label sheet: every outline must sit on a label</div>`;\n return `<div class=\"lp-page\">${banner}${marks}${cells.join(\"\\n\")}</div>`;\n}\n\n/** Print the grid outline + crop marks only — verify alignment before burning labels. */\nexport function printCalibrationSheet(template: LabelTemplate): PrintOutcome {\n return openSheetWindow(\n template,\n buildCalibrationBody(template),\n \"Label calibration\",\n false,\n );\n}\n\n// ─── Scannability ────────────────────────────────────────────────────────────\n\nconst MM_PER_IN = 25.4;\n\n/**\n * The printed-module floor a 300 dpi thermal printer and a warehouse scanner\n * can both resolve. Below this a symbol prints, looks fine on screen, and\n * refuses to scan on the dock.\n */\nexport const MIN_SCANNABLE_MODULE_MM = 0.4;\n\nexport interface ScannabilityReport {\n ok: boolean;\n version: number;\n moduleSizeMm: number;\n minModuleSizeMm: number;\n message?: string;\n}\n\n/**\n * Will a payload of `payloadByteLen` bytes actually scan off this stock?\n *\n * The printed side comes from the SAME layout brain the printer uses\n * (`computeCellLayout`), evaluated with captions ON — the smaller, and\n * therefore conservative, QR box. Module size divides that side by the symbol's\n * modules PLUS the two 4-module quiet zones the data URI carries inside it.\n *\n * Never throws: an over-capacity payload comes back as `ok: false` with\n * `version: 0` and the capacity error as the message.\n */\nexport function assertScannable(\n template: LabelTemplate,\n payloadByteLen: number,\n ecLevel: QrEcLevel = DEFAULTS.ecLevel,\n): ScannabilityReport {\n const layout = computeCellLayout(template, true);\n const printedSideMm = layout.qrIn * MM_PER_IN;\n\n let version: number;\n try {\n version = minQrVersion(payloadByteLen, ecLevel);\n } catch (err) {\n return {\n ok: false,\n version: 0,\n moduleSizeMm: 0,\n minModuleSizeMm: MIN_SCANNABLE_MODULE_MM,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n\n const modulesPerSide = qrModuleCount(version) + QUIET_ZONE_MODULES * 2;\n const moduleSizeMm = printedSideMm / modulesPerSide;\n if (moduleSizeMm >= MIN_SCANNABLE_MODULE_MM) {\n return {\n ok: true,\n version,\n moduleSizeMm,\n minModuleSizeMm: MIN_SCANNABLE_MODULE_MM,\n };\n }\n return {\n ok: false,\n version,\n moduleSizeMm,\n minModuleSizeMm: MIN_SCANNABLE_MODULE_MM,\n message:\n `A ${payloadByteLen}-byte payload needs a version ${version} QR, which prints at ` +\n `${moduleSizeMm.toFixed(3)}mm per module on ${template.stockCode} — below the ` +\n `${MIN_SCANNABLE_MODULE_MM}mm floor a scanner can resolve. Shorten the payload ` +\n `(a short resolver URL is the intended fix) or move to larger label stock.`,\n };\n}\n\n/** The batch's worst case: the longest payload decides whether we warn. */\nfunction batchScannabilityWarning(\n t: LabelTemplate,\n labels: readonly QrLabel[],\n ecLevel: QrEcLevel,\n): string | undefined {\n const encoder = new TextEncoder();\n let worst = -1;\n for (const l of labels) {\n if (!l.qrValue) continue;\n const len = encoder.encode(l.qrValue).length;\n if (len > worst) worst = len;\n }\n if (worst < 0) return undefined;\n const report = assertScannable(t, worst, ecLevel);\n return report.ok ? undefined : report.message;\n}\n\n// ─── The printer ─────────────────────────────────────────────────────────────\n\nfunction resolveTemplate(\n data: QrLabelPrintData,\n variantId?: string,\n): LabelTemplate {\n if (variantId && variantId !== \"custom\") {\n const t = getLabelTemplate(variantId);\n if (t) return t;\n }\n if (data.templateId === \"custom\" && data.customTemplate)\n return data.customTemplate;\n return (\n getLabelTemplate(data.templateId) ?? data.customTemplate ?? AVERY_5160\n );\n}\n\n/** Core render path — also used by the demo page's direct print entry point. */\nexport async function printQrLabelSheet(\n data: QrLabelPrintData,\n variantId?: string,\n rawSettings?: PrintSettings,\n): Promise<PrintOutcome> {\n const t = resolveTemplate(data, variantId);\n const s = resolveSettings(rawSettings);\n\n if (s.calibrationPage) {\n return printCalibrationSheet(t);\n }\n\n const labels = expandLabels(data.labels ?? [], s);\n if (!labels.length) {\n return openSheetWindow(\n t,\n `<div class=\"lp-page\"><div style=\"position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#94a3b8;font-size:11pt;\">No labels to print.</div></div>`,\n \"QR labels\",\n false,\n );\n }\n\n const hasBadge = labels.some((l) => (l.badge ?? \"\").trim() !== \"\");\n const layout = computeCellLayout(t, s.showCaption, hasBadge);\n\n // Generate every data URI up front (dedup identical values). A label with no\n // qrValue (a text-only LabelFormat) simply carries no code.\n const uriByValue = new Map<string, string>();\n for (const l of labels) {\n if (l.qrValue && !uriByValue.has(l.qrValue)) {\n uriByValue.set(\n l.qrValue,\n await generateQrDataUri(l.qrValue, s.ecLevel, layout.qrIn),\n );\n }\n }\n\n // Leading blank cells for a partially-used sheet, then labels, then pad.\n const leadingBlanks = Math.max(0, s.startAtLabel - 1) % (t.cols * t.rows);\n const cells: RenderedCell[] = [\n ...Array.from({ length: leadingBlanks }, () => ({ html: \"\" })),\n ...labels.map((l) => ({\n html: buildLabelCellHtml(\n l,\n uriByValue.get(l.qrValue) ?? \"\",\n t,\n layout,\n s,\n ),\n })),\n ];\n const perPage = t.cols * t.rows;\n const tail = cells.length % perPage;\n if (tail > 0)\n for (let i = 0; i < perPage - tail; i++) cells.push({ html: \"\" });\n\n return openSheetWindow(\n t,\n renderPages(t, cells),\n \"QR labels\",\n true,\n batchScannabilityWarning(t, labels, s.ecLevel),\n );\n}\n\nexport const qrLabelsPrinter: BlockPrinter = {\n label: \"Print QR label sheet\",\n variants: LABEL_TEMPLATES.map((t) => ({\n id: t.id,\n label: t.name,\n description: t.stockCode,\n })),\n settings: [\n {\n type: \"number\",\n id: \"startAtLabel\",\n label: \"Start at label #\",\n description: \"Skip already-used positions on a partial sheet\",\n defaultValue: 1,\n min: 1,\n },\n {\n type: \"number\",\n id: \"copiesPerCode\",\n label: \"Copies per code\",\n defaultValue: 1,\n min: 1,\n max: 999,\n },\n {\n type: \"boolean\",\n id: \"showCaption\",\n label: \"Show caption\",\n description: \"Print the caption (or the code value) beside each QR\",\n defaultValue: true,\n },\n {\n type: \"boolean\",\n id: \"showLines\",\n label: \"Show detail lines\",\n description: \"Extra text lines on wide labels\",\n defaultValue: true,\n },\n {\n type: \"range\",\n id: \"labelRange\",\n label: \"Label range\",\n description: \"Print only labels N to M (blank = all)\",\n fromId: \"rangeFrom\",\n toId: \"rangeTo\",\n defaultFrom: 0,\n defaultTo: 0,\n },\n {\n type: \"select\",\n id: \"ecLevel\",\n label: \"Error correction\",\n description: \"Higher survives more damage; M is the warehouse default\",\n options: [\n { value: \"L\", label: \"L\" },\n { value: \"M\", label: \"M\" },\n { value: \"Q\", label: \"Q\" },\n ],\n defaultValue: \"M\",\n },\n {\n type: \"boolean\",\n id: \"calibrationPage\",\n label: \"Calibration test page\",\n description: \"Print outlines + crop marks only to verify alignment\",\n defaultValue: false,\n },\n ],\n print: (data, variantId, settings) =>\n printQrLabelSheet(data as QrLabelPrintData, variantId, settings),\n};\n\n// ─── PDF lane ────────────────────────────────────────────────────────────────\n\n/**\n * downloadLabelsPdf() renders the same registry-driven grid as the printer\n * straight into a jsPDF file, for users who want a file instead of the browser\n * print dialog.\n *\n * jspdf is imported dynamically INSIDE the function body (code-splitting rule 6\n * — click-time machinery never rides a static edge).\n */\n\nconst PT_PER_IN = 72;\n\nfunction resolvePdfSettings(s?: PrintSettings): QrLabelSettings {\n const num = (v: unknown, d: number, min = 0) =>\n typeof v === \"number\" && !Number.isNaN(v) ? Math.max(min, v) : d;\n const ec = s?.ecLevel;\n return {\n startAtLabel: num(s?.startAtLabel, 1, 1),\n copiesPerCode: num(s?.copiesPerCode, 1, 1),\n showCaption: (s?.showCaption ?? true) as boolean,\n showLines: (s?.showLines ?? true) as boolean,\n rangeFrom: num(s?.rangeFrom, 0),\n rangeTo: num(s?.rangeTo, 0),\n ecLevel: ec === \"L\" || ec === \"Q\" ? ec : \"M\",\n calibrationPage: false,\n };\n}\n\n/** jsPDF's splitTextToSize is loosely typed; take the first line safely. */\nfunction firstLine(parts: unknown): string {\n if (Array.isArray(parts)) {\n const head: unknown = parts[0];\n return typeof head === \"string\" ? head : \"\";\n }\n return typeof parts === \"string\" ? parts : \"\";\n}\n\n/**\n * Generate a PDF of QR labels for the given template and hand it to the\n * browser as a download. Same data seam as the printer: `{ labels, templateId }`.\n */\nexport async function downloadLabelsPdf(\n data: QrLabelPrintData,\n templateId?: string,\n rawSettings?: PrintSettings,\n filename = \"qr-labels.pdf\",\n): Promise<void> {\n const t =\n (templateId === \"custom\" || data.templateId === \"custom\"\n ? data.customTemplate\n : undefined) ??\n getLabelTemplate(templateId ?? data.templateId) ??\n AVERY_5160;\n const s = resolvePdfSettings(rawSettings);\n const labels = expandLabels(data.labels ?? [], s);\n if (!labels.length) return;\n\n // Never blocks the download — the PDF lane has no screen-only header to\n // carry the banner, so an unscannable batch screams on the console instead.\n const warning = batchScannabilityWarning(t, labels, s.ecLevel);\n if (warning !== undefined) console.warn(`[label-print] ${warning}`);\n\n const { jsPDF } = await import(\"jspdf\");\n const doc = new jsPDF({\n unit: \"pt\",\n format: [t.sheetWIn * PT_PER_IN, t.sheetHIn * PT_PER_IN],\n });\n\n const hasBadge = labels.some((l) => (l.badge ?? \"\").trim() !== \"\");\n const layout = computeCellLayout(t, s.showCaption, hasBadge);\n const qrPt = layout.qrIn * PT_PER_IN;\n const padPt = layout.padIn * PT_PER_IN;\n const perPage = t.cols * t.rows;\n const leadingBlanks = Math.max(0, s.startAtLabel - 1) % perPage;\n\n const uriByValue = new Map<string, string>();\n for (const l of labels) {\n if (l.qrValue && !uriByValue.has(l.qrValue)) {\n uriByValue.set(\n l.qrValue,\n await generateQrDataUri(l.qrValue, s.ecLevel, layout.qrIn),\n );\n }\n }\n\n for (const [i, label] of labels.entries()) {\n const cellIndex = i + leadingBlanks;\n if (cellIndex > 0 && cellIndex % perPage === 0) doc.addPage();\n const within = cellIndex % perPage;\n const col = within % t.cols;\n const row = Math.floor(within / t.cols);\n const x =\n (t.marginLeftIn + col * (t.labelWIn + t.gutterXIn)) * PT_PER_IN;\n const y = (t.marginTopIn + row * (t.labelHIn + t.gutterYIn)) * PT_PER_IN;\n const caption = s.showCaption ? (label.caption ?? label.qrValue) : \"\";\n const badge = label.badge?.trim() ? label.badge.trim() : \"\";\n const uri = uriByValue.get(label.qrValue) ?? \"\";\n\n if (layout.wide) {\n // QR left, text right\n const qrY = y + (t.labelHIn * PT_PER_IN - qrPt) / 2;\n if (uri) doc.addImage(uri, \"PNG\", x + padPt, qrY, qrPt, qrPt);\n const textX = x + padPt + qrPt + padPt;\n const textMaxW = t.labelWIn * PT_PER_IN - (textX - x) - padPt;\n let cursorY = y + padPt + 12;\n if (badge) {\n doc.setFont(\"helvetica\", \"bold\");\n doc.setFontSize(24);\n cursorY = y + padPt + 22;\n doc.text(firstLine(doc.splitTextToSize(badge, textMaxW)), textX, cursorY);\n cursorY += 16;\n }\n if (caption) {\n doc.setFont(\"helvetica\", \"bold\");\n doc.setFontSize(12);\n doc.text(firstLine(doc.splitTextToSize(caption, textMaxW)), textX, cursorY);\n cursorY += 14;\n }\n if (s.showLines && label.lines?.length) {\n doc.setFont(\"helvetica\", \"normal\");\n doc.setFontSize(8);\n for (const line of label.lines.filter((l) => l.trim())) {\n if (cursorY > y + t.labelHIn * PT_PER_IN - padPt) break;\n doc.text(firstLine(doc.splitTextToSize(line, textMaxW)), textX, cursorY);\n cursorY += 10;\n }\n }\n } else {\n // Stacked, centered\n const cx = x + t.labelWIn * PT_PER_IN / 2;\n const contentH = qrPt + (caption ? 12 : 0) + (badge ? 22 : 0);\n const qrY = y + (t.labelHIn * PT_PER_IN - contentH) / 2;\n if (uri) doc.addImage(uri, \"PNG\", cx - qrPt / 2, qrY, qrPt, qrPt);\n const maxW = (t.labelWIn - layout.padIn * 2) * PT_PER_IN;\n let textY = qrY + qrPt;\n if (badge) {\n doc.setFont(\"helvetica\", \"bold\");\n doc.setFontSize(18);\n textY += 18;\n doc.text(firstLine(doc.splitTextToSize(badge, maxW)), cx, textY, {\n align: \"center\",\n });\n }\n if (caption) {\n doc.setFont(\"helvetica\", \"bold\");\n doc.setFontSize(9);\n doc.text(firstLine(doc.splitTextToSize(caption, maxW)), cx, textY + 10, {\n align: \"center\",\n });\n }\n }\n }\n\n doc.save(filename);\n}\n\n// ─── Label formats (the custom-format model) ─────────────────────────────────\n\n/** Where an element's text comes from: a row column, or a fixed string. */\nexport type LabelField = { field: string } | { literal: string };\n\nexport type LabelElement =\n | { kind: \"qr\"; source: LabelField }\n | {\n kind: \"text\";\n source: LabelField;\n role: \"title\" | \"detail\" | \"badge\";\n /**\n * Cap on how many lines the value may occupy. The value is split on\n * newlines; beyond `maxLines` the extra lines are dropped and the last\n * kept line gains an ellipsis.\n */\n maxLines?: number;\n }\n /** Durable http(s) URL only — THE IMAGE RULE. A non-http value is SKIPPED. */\n | { kind: \"image\"; source: LabelField };\n\nexport interface LabelFormat {\n id: string;\n name: string;\n elements: LabelElement[];\n}\n\nfunction resolveLabelField(\n source: LabelField,\n row: Record<string, string>,\n): string {\n if (\"literal\" in source) return source.literal;\n return row[source.field] ?? \"\";\n}\n\nfunction capToLines(value: string, maxLines: number | undefined): string[] {\n const parts = value\n .split(\"\\n\")\n .map((p) => p.trim())\n .filter((p) => p !== \"\");\n if (maxLines === undefined || maxLines <= 0 || parts.length <= maxLines) {\n return parts;\n }\n const kept = parts.slice(0, maxLines);\n const last = kept[kept.length - 1];\n if (last !== undefined) kept[kept.length - 1] = `${last}…`;\n return kept;\n}\n\n/**\n * Map rows through a format into the printer's label shape.\n *\n * - the qr element's resolved value → `qrValue` (first one wins)\n * - the FIRST title-role text → `caption`\n * - the FIRST badge-role text → `badge`\n * - detail-role texts, in element order → `lines[]`\n * - the first http(s) image element → `imageUrl`\n *\n * A row missing a `field` resolves to \"\" and that element is skipped.\n */\nexport function applyLabelFormat(\n format: LabelFormat,\n rows: Record<string, string>[],\n): { labels: QrLabelPrintData[\"labels\"] } {\n const labels: QrLabel[] = [];\n for (const row of rows) {\n let qrValue = \"\";\n let caption: string | undefined;\n let badge: string | undefined;\n let imageUrl: string | undefined;\n const lines: string[] = [];\n\n for (const el of format.elements) {\n const raw = resolveLabelField(el.source, row).trim();\n if (raw === \"\") continue;\n if (el.kind === \"qr\") {\n if (qrValue === \"\") qrValue = raw;\n continue;\n }\n if (el.kind === \"image\") {\n if (imageUrl === undefined && isHttpUrl(raw)) imageUrl = raw;\n continue;\n }\n const capped = capToLines(raw, el.maxLines);\n if (capped.length === 0) continue;\n if (el.role === \"title\") {\n if (caption === undefined) caption = capped.join(\" \");\n } else if (el.role === \"badge\") {\n if (badge === undefined) badge = capped.join(\" \");\n } else {\n lines.push(...capped);\n }\n }\n\n const label: QrLabel = { qrValue };\n if (caption !== undefined) label.caption = caption;\n if (lines.length > 0) label.lines = lines;\n if (badge !== undefined) label.badge = badge;\n if (imageUrl !== undefined) label.imageUrl = imageUrl;\n labels.push(label);\n }\n return { labels };\n}\n\n/**\n * The shipped formats. Every preset reads the QR payload from the `qrValue`\n * column, so a row is always `{ qrValue, …format-specific columns }`.\n *\n * NOTE on `asset-spec`: the cell renderer auto-shrinks only the caption/badge\n * (`.lp-fit`); detail lines are fixed 8pt, so the title never loses size to a\n * long spec list.\n */\nexport const LABEL_FORMAT_PRESETS: readonly LabelFormat[] = [\n {\n id: \"qr-only\",\n name: \"QR only\",\n elements: [{ kind: \"qr\", source: { field: \"qrValue\" } }],\n },\n {\n id: \"qr-caption\",\n name: \"QR + caption\",\n elements: [\n { kind: \"qr\", source: { field: \"qrValue\" } },\n { kind: \"text\", source: { field: \"caption\" }, role: \"title\" },\n ],\n },\n {\n id: \"garment\",\n name: \"Garment tag\",\n elements: [\n { kind: \"qr\", source: { field: \"qrValue\" } },\n { kind: \"text\", source: { field: \"size\" }, role: \"badge\" },\n { kind: \"text\", source: { field: \"sku\" }, role: \"title\" },\n { kind: \"text\", source: { field: \"color\" }, role: \"detail\" },\n { kind: \"text\", source: { field: \"price\" }, role: \"detail\" },\n ],\n },\n {\n id: \"asset-spec\",\n name: \"Asset spec\",\n elements: [\n { kind: \"text\", source: { field: \"title\" }, role: \"title\" },\n { kind: \"text\", source: { field: \"spec1\" }, role: \"detail\" },\n { kind: \"text\", source: { field: \"spec2\" }, role: \"detail\" },\n { kind: \"text\", source: { field: \"spec3\" }, role: \"detail\" },\n { kind: \"text\", source: { field: \"spec4\" }, role: \"detail\" },\n { kind: \"qr\", source: { field: \"qrValue\" } },\n ],\n },\n];\n","/**\n * @ai-matrx/print/qr — QR GENERATION.\n *\n * New code (no port source). Decoding is NOT here: it lives in\n * `@ai-matrx/kit/qr`. This module only ever turns a value into a symbol, and\n * builds the two payload shapes the platform actually prints:\n *\n * 1. a SHORT RESOLVER URL (`https://origin/l/<code>`) — the default\n * e-commerce payload. The code value is an OPAQUE INPUT: minting,\n * uniqueness, collision handling and lookup are all server-side concerns.\n * Nothing in this file generates, randomizes, or validates the identity of\n * a code beyond \"it is a non-empty string we will percent-encode\".\n * 2. a GS1 DIGITAL LINK (`https://origin/01/<gtin14>[/10/<lot>][/21/<serial>][?17=YYMMDD]`)\n * for supply-chain interoperability — the thing a retailer's scanner\n * understands without knowing anything about AI Matrx.\n *\n * Two hard defaults that are NOT style choices:\n * - Error correction defaults to \"M\", never \"L\". These end up on warehouse\n * labels that get scuffed, wrapped around curved stock and read at an\n * angle; \"L\" is only ever right for a huge, clean, flat code.\n * - The quiet zone is CLAMPED to >= 4 modules (ISO/IEC 18004 §6.3.8 minimum)\n * no matter what the caller passes. A tight quiet zone is the single most\n * common cause of a label that scans on the design monitor and fails at the\n * dock door.\n *\n * The module is import-inert: `qrcode` is only ever pulled in via\n * `await import(\"qrcode\")` inside a function body, so importing this file from\n * a React Server Component (or any tree-shaken bundle that never draws a QR)\n * costs nothing. No module-level mutable state, no React, no DOM at import\n * time.\n */\n\nimport type { QRCodeToDataURLOptions, QRCodeToStringOptions } from \"qrcode\";\n\n// ─── Contract ────────────────────────────────────────────────────────────────\n\nexport type QrEcLevel = \"L\" | \"M\" | \"Q\" | \"H\";\n\nexport interface QrOptions {\n /** Error correction. Default \"M\" — NEVER \"L\" (warehouse labels get scuffed; L is only right for huge clean codes). */\n ecLevel?: QrEcLevel;\n /** Quiet zone in modules. Default 4. CLAMPED to >= 4 at runtime — ISO 18004 spec minimum, not a style choice. */\n quietZoneModules?: number;\n /** Rendered width in pixels (dataURL/canvas lanes). */\n widthPx?: number;\n}\n\n/** ISO/IEC 18004 §6.3.8 minimum quiet zone. Callers cannot go below this. */\nexport const QR_MIN_QUIET_ZONE_MODULES = 4;\n\n/** The default error correction level for every Matrx-printed code. */\nexport const DEFAULT_QR_EC_LEVEL: QrEcLevel = \"M\";\n\n/** Lowest and highest QR symbol versions (ISO/IEC 18004). */\nexport const QR_MIN_VERSION = 1;\nexport const QR_MAX_VERSION = 40;\n\n// ─── Capacity table ──────────────────────────────────────────────────────────\n\n/**\n * ISO/IEC 18004 BYTE-mode data capacity, versions 1–40 × [L, M, Q, H],\n * in bytes (8-bit characters).\n *\n * This is the full spec table, not an approximation. It is cross-checked in\n * `qr.test.ts` against the `qrcode` reference implementation for a spread of\n * versions at all four levels — every entry that test samples must both fit at\n * exactly `capacity` bytes and fail at `capacity + 1`.\n *\n * Anchors worth eyeballing: v1 = L17/M14/Q11/H7 · v2 M=26 · v3 M=42 ·\n * v4 M=62 · v10 M=213 · v40 = L2953/M2331/Q1663/H1273.\n */\nconst BYTE_CAPACITY: readonly (readonly [number, number, number, number])[] = [\n /* v1 */ [17, 14, 11, 7],\n /* v2 */ [32, 26, 20, 14],\n /* v3 */ [53, 42, 32, 24],\n /* v4 */ [78, 62, 46, 34],\n /* v5 */ [106, 84, 60, 44],\n /* v6 */ [134, 106, 74, 58],\n /* v7 */ [154, 122, 86, 64],\n /* v8 */ [192, 152, 108, 84],\n /* v9 */ [230, 180, 130, 98],\n /* v10 */ [271, 213, 151, 119],\n /* v11 */ [321, 251, 177, 137],\n /* v12 */ [367, 287, 203, 155],\n /* v13 */ [425, 331, 241, 177],\n /* v14 */ [458, 362, 258, 194],\n /* v15 */ [520, 412, 292, 220],\n /* v16 */ [586, 450, 322, 250],\n /* v17 */ [644, 504, 364, 280],\n /* v18 */ [718, 560, 394, 310],\n /* v19 */ [792, 624, 442, 338],\n /* v20 */ [858, 666, 482, 382],\n /* v21 */ [929, 711, 509, 403],\n /* v22 */ [1003, 779, 565, 439],\n /* v23 */ [1091, 857, 611, 461],\n /* v24 */ [1171, 911, 661, 511],\n /* v25 */ [1273, 997, 715, 535],\n /* v26 */ [1367, 1059, 751, 593],\n /* v27 */ [1465, 1125, 805, 625],\n /* v28 */ [1528, 1190, 868, 658],\n /* v29 */ [1628, 1264, 908, 698],\n /* v30 */ [1732, 1370, 982, 742],\n /* v31 */ [1840, 1452, 1030, 790],\n /* v32 */ [1952, 1538, 1112, 842],\n /* v33 */ [2068, 1628, 1168, 898],\n /* v34 */ [2188, 1722, 1228, 958],\n /* v35 */ [2303, 1809, 1283, 983],\n /* v36 */ [2431, 1911, 1351, 1051],\n /* v37 */ [2563, 1989, 1423, 1093],\n /* v38 */ [2699, 2099, 1499, 1139],\n /* v39 */ [2809, 2213, 1579, 1219],\n /* v40 */ [2953, 2331, 1663, 1273],\n];\n\nconst EC_COLUMN: Readonly<Record<QrEcLevel, 0 | 1 | 2 | 3>> = { L: 0, M: 1, Q: 2, H: 3 };\n\nfunction assertEcLevel(ecLevel: QrEcLevel): 0 | 1 | 2 | 3 {\n const column = EC_COLUMN[ecLevel];\n if (column === undefined) {\n throw new Error(`Unknown QR error correction level \"${String(ecLevel)}\". Expected one of \"L\", \"M\", \"Q\", \"H\".`);\n }\n return column;\n}\n\nfunction assertVersion(version: number): number {\n if (typeof version !== \"number\" || !Number.isInteger(version) || version < QR_MIN_VERSION || version > QR_MAX_VERSION) {\n throw new Error(\n `QR version must be an integer between ${QR_MIN_VERSION} and ${QR_MAX_VERSION}, got ${String(version)}.`,\n );\n }\n return version;\n}\n\n/**\n * BYTE-mode capacity in bytes for a given version/level.\n * Throws on an out-of-range version or unknown level.\n */\nexport function qrByteCapacity(version: number, ecLevel: QrEcLevel): number {\n const column = assertEcLevel(ecLevel);\n const row = BYTE_CAPACITY[assertVersion(version) - 1];\n if (row === undefined) {\n throw new Error(`No QR capacity row for version ${version}.`);\n }\n return row[column];\n}\n\n/** Modules per side for a QR version: 17 + 4*version. */\nexport function qrModuleCount(version: number): number {\n return 17 + 4 * assertVersion(version);\n}\n\n/**\n * Smallest QR version (1-40) whose BYTE-mode capacity at `ecLevel` fits\n * `payloadByteLen`. Throws a clear Error when nothing fits.\n *\n * NOTE: this is the *byte* length, not the character length — callers holding\n * a JS string should measure it with `new TextEncoder().encode(s).length`, not\n * `s.length`, or every non-ASCII character will silently under-count.\n */\nexport function minQrVersion(payloadByteLen: number, ecLevel: QrEcLevel): number {\n const column = assertEcLevel(ecLevel);\n if (typeof payloadByteLen !== \"number\" || !Number.isInteger(payloadByteLen) || payloadByteLen < 0) {\n throw new Error(`QR payload byte length must be a non-negative integer, got ${String(payloadByteLen)}.`);\n }\n for (let version = QR_MIN_VERSION; version <= QR_MAX_VERSION; version += 1) {\n const row = BYTE_CAPACITY[version - 1];\n if (row !== undefined && row[column] >= payloadByteLen) {\n return version;\n }\n }\n const max = qrByteCapacity(QR_MAX_VERSION, ecLevel);\n throw new Error(\n `QR payload of ${payloadByteLen} bytes does not fit any QR version at error correction level \"${ecLevel}\" ` +\n `(version ${QR_MAX_VERSION} holds ${max} bytes). Shorten the payload — a short resolver URL is the intended ` +\n `fix — or, only for a large clean code, drop to a lower error correction level.`,\n );\n}\n\n// ─── Rendering ───────────────────────────────────────────────────────────────\n\nfunction resolveEcLevel(ecLevel: QrEcLevel | undefined): QrEcLevel {\n if (ecLevel === undefined) return DEFAULT_QR_EC_LEVEL;\n assertEcLevel(ecLevel);\n return ecLevel;\n}\n\n/**\n * `margin` in the `qrcode` library IS the quiet zone measured in modules.\n * Anything below the ISO minimum is silently raised — a caller asking for 0 is\n * asking for a label that will not scan, and we would rather print a slightly\n * smaller symbol than an unreadable one.\n */\nfunction resolveQuietZone(quietZoneModules: number | undefined): number {\n if (typeof quietZoneModules !== \"number\" || !Number.isFinite(quietZoneModules)) {\n return QR_MIN_QUIET_ZONE_MODULES;\n }\n return Math.max(QR_MIN_QUIET_ZONE_MODULES, Math.floor(quietZoneModules));\n}\n\nfunction resolveWidthPx(widthPx: number | undefined): number | undefined {\n if (widthPx === undefined) return undefined;\n if (typeof widthPx !== \"number\" || !Number.isFinite(widthPx) || widthPx <= 0) {\n throw new Error(`QR widthPx must be a positive number, got ${String(widthPx)}.`);\n }\n return Math.round(widthPx);\n}\n\nfunction assertValue(value: string): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(\"QR value must be a non-empty string.\");\n }\n return value;\n}\n\n/** PNG data URL. Lazy-imports \"qrcode\" at call time (import-inert module). */\nexport async function generateQrDataUrl(value: string, opts?: QrOptions): Promise<string> {\n assertValue(value);\n const width = resolveWidthPx(opts?.widthPx);\n const options: QRCodeToDataURLOptions = {\n errorCorrectionLevel: resolveEcLevel(opts?.ecLevel),\n margin: resolveQuietZone(opts?.quietZoneModules),\n };\n // exactOptionalPropertyTypes: omit the key entirely rather than assigning undefined.\n if (width !== undefined) options.width = width;\n\n const QRCode = await import(\"qrcode\");\n return QRCode.toDataURL(value, options);\n}\n\n/** SVG markup string. Same lazy import. */\nexport async function generateQrSvg(value: string, opts?: QrOptions): Promise<string> {\n assertValue(value);\n const width = resolveWidthPx(opts?.widthPx);\n const options: QRCodeToStringOptions = {\n type: \"svg\",\n errorCorrectionLevel: resolveEcLevel(opts?.ecLevel),\n margin: resolveQuietZone(opts?.quietZoneModules),\n };\n if (width !== undefined) options.width = width;\n\n const QRCode = await import(\"qrcode\");\n return QRCode.toString(value, options);\n}\n\n// ─── Payload builders ────────────────────────────────────────────────────────\n\n/**\n * Validate + normalize an origin for a payload builder.\n * Accepts `https://x.com`, `https://x.com/`, `https://x.com/go` — returns it\n * with every trailing slash stripped so callers can always concatenate a\n * leading-slash path without producing `//`.\n */\nfunction normalizeOrigin(origin: string): string {\n if (typeof origin !== \"string\" || origin.trim() === \"\") {\n throw new Error('QR origin is required — pass something like \"https://example.com\".');\n }\n const trimmed = origin.trim();\n let parsed: URL;\n try {\n parsed = new URL(trimmed);\n } catch {\n throw new Error(`QR origin \"${origin}\" is not a valid absolute URL. Expected something like \"https://example.com\".`);\n }\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n throw new Error(`QR origin \"${origin}\" must use http or https, got \"${parsed.protocol}\".`);\n }\n if (parsed.search !== \"\" || parsed.hash !== \"\") {\n throw new Error(`QR origin \"${origin}\" must not carry a query string or fragment.`);\n }\n return trimmed.replace(/\\/+$/, \"\");\n}\n\n/**\n * Short resolver URL — the platform's default e-commerce payload.\n *\n * The CODE VALUE IS AN OPAQUE INPUT: minting and uniqueness are server-side,\n * never generated here. This function's whole job is to put a code the server\n * already owns behind a URL short enough to stay in a low QR version.\n *\n * Result looks like `https://x.com/l/AB12cd`.\n */\nexport function buildShortResolverUrl(opts: { origin: string; code: string; path?: string }): string {\n const origin = normalizeOrigin(opts.origin);\n\n const code = opts.code;\n if (typeof code !== \"string\" || code.trim() === \"\") {\n throw new Error(\"QR resolver code is required — it is minted server-side and passed in here as an opaque value.\");\n }\n\n const rawPath = opts.path === undefined ? \"/l\" : opts.path;\n if (typeof rawPath !== \"string\") {\n throw new Error(`QR resolver path must be a string, got ${String(rawPath)}.`);\n }\n if (rawPath.includes(\"?\") || rawPath.includes(\"#\")) {\n throw new Error(`QR resolver path \"${rawPath}\" must not contain a query string or fragment.`);\n }\n const cleanedPath = rawPath.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n const prefix = cleanedPath === \"\" ? \"\" : `/${cleanedPath}`;\n\n return `${origin}${prefix}/${encodeURIComponent(code)}`;\n}\n\n/**\n * GS1 mod-10 check digit over the payload (i.e. the digits WITHOUT the check\n * digit): weights 3 and 1 alternating from the right. Length-independent, so a\n * GTIN-12 and its zero-padded GTIN-14 form produce the same digit — which is\n * exactly why we validate BEFORE padding.\n */\nfunction gs1CheckDigit(payloadDigits: string): number {\n let sum = 0;\n let weight = 3;\n for (let i = payloadDigits.length - 1; i >= 0; i -= 1) {\n sum += (payloadDigits.charCodeAt(i) - 48) * weight;\n weight = weight === 3 ? 1 : 3;\n }\n return (10 - (sum % 10)) % 10;\n}\n\n/** Validate a GTIN-8/12/13/14 (own check digit) and return it zero-padded to GTIN-14. */\nfunction normalizeGtin(gtin: string): string {\n if (typeof gtin !== \"string\" || gtin.trim() === \"\") {\n throw new Error(\"GTIN is required for a GS1 Digital Link.\");\n }\n const raw = gtin.trim();\n if (!/^[0-9]+$/.test(raw)) {\n throw new Error(`GTIN \"${gtin}\" must contain digits only.`);\n }\n if (raw.length !== 8 && raw.length !== 12 && raw.length !== 13 && raw.length !== 14) {\n throw new Error(`GTIN \"${gtin}\" must be 8, 12, 13, or 14 digits, got ${raw.length}.`);\n }\n const expected = gs1CheckDigit(raw.slice(0, -1));\n const actual = raw.charCodeAt(raw.length - 1) - 48;\n if (expected !== actual) {\n throw new Error(`GTIN \"${raw}\" has an invalid GS1 check digit: expected ${expected}, got ${actual}.`);\n }\n return raw.padStart(14, \"0\");\n}\n\nfunction daysInMonth(year: number, month: number): number {\n return new Date(Date.UTC(year, month, 0)).getUTCDate();\n}\n\n/**\n * AI 17 expiration date — STRICT \"YYMMDD\", 6 digits, nothing else.\n * `DD = 00` is honoured: GS1 defines it as \"last day of the month\".\n */\nfunction normalizeExpiry(expiry: string): string {\n if (typeof expiry !== \"string\" || !/^[0-9]{6}$/.test(expiry.trim())) {\n throw new Error(`GS1 expiry (AI 17) must be exactly 6 digits in YYMMDD form, got \"${String(expiry)}\".`);\n }\n const raw = expiry.trim();\n const year = 2000 + Number(raw.slice(0, 2));\n const month = Number(raw.slice(2, 4));\n const day = Number(raw.slice(4, 6));\n if (month < 1 || month > 12) {\n throw new Error(`GS1 expiry (AI 17) \"${raw}\" has an invalid month \"${raw.slice(2, 4)}\" — expected 01-12.`);\n }\n const max = daysInMonth(year, month);\n // DD=00 is the GS1 \"end of month\" convention and stays legal.\n if (day > max) {\n throw new Error(\n `GS1 expiry (AI 17) \"${raw}\" has an invalid day \"${raw.slice(4, 6)}\" — ${year}-${raw.slice(2, 4)} has ${max} days ` +\n `(00 is legal and means end of month).`,\n );\n }\n return raw;\n}\n\nfunction assertQualifier(value: string, ai: string, name: string): string {\n if (typeof value !== \"string\" || value.trim() === \"\") {\n throw new Error(`GS1 ${name} (AI ${ai}) must be a non-empty string when provided.`);\n }\n return value;\n}\n\n/** GS1 Digital Link builder for supply-chain interoperability. */\nexport function buildGs1DigitalLink(opts: {\n origin: string;\n gtin: string;\n lot?: string;\n serial?: string;\n expiry?: string;\n}): string {\n const origin = normalizeOrigin(opts.origin);\n const gtin14 = normalizeGtin(opts.gtin);\n\n // Path order is fixed by the GS1 Digital Link grammar:\n // /01/{gtin14} then the optional qualifiers /10/{lot} then /21/{serial}.\n let url = `${origin}/01/${gtin14}`;\n if (opts.lot !== undefined) {\n url += `/10/${encodeURIComponent(assertQualifier(opts.lot, \"10\", \"lot\"))}`;\n }\n if (opts.serial !== undefined) {\n url += `/21/${encodeURIComponent(assertQualifier(opts.serial, \"21\", \"serial\"))}`;\n }\n // AI 17 is a DATA ATTRIBUTE, not a path qualifier — it belongs in the query\n // string, never as a /17/ path segment.\n if (opts.expiry !== undefined) {\n url += `?17=${normalizeExpiry(opts.expiry)}`;\n }\n return url;\n}\n"],"mappings":"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","names":["import_html_escape","caption","badge","DrawerPrimitive","renderPages"]}
|