{"$schema":"https:\/\/ui.particle.academy\/schema\/registry-item.json","name":"qr-code","type":"registry:ui","title":"QrCode","description":"A QR code as an SVG, dependency-free - versions 1-40, L\/M\/Q\/H, numeric\/alphanumeric\/byte selected automatically.","package":"react-fancy","dependencies":["tailwind-merge"],"registryDependencies":[],"files":[{"path":"components\/fancy\/qr-code\/QrCode.tsx","content":"import { forwardRef, useMemo } from \"react\";\nimport { cn } from \"..\/lib\/utils\/cn\";\nimport { encodeQr } from \".\/qr-encode\";\nimport type { QrCodeProps } from \".\/QrCode.types\";\n\n\/**\n * A QR code, as an SVG, with no dependencies.\n *\n * ## Why SVG and one path\n *\n * The symbol is drawn as a **single `<path>`** of one-unit squares on a\n * `viewBox` sized in modules, not as a grid of `<rect>` elements. A version 10\n * symbol is 57x57 \u2014 over three thousand modules \u2014 and a rect each would be\n * three thousand DOM nodes for something that never changes. One path is one\n * node, and because the viewBox is in module units the symbol is resolution\n * independent: `size` scales it with no resampling, so it stays crisp at any\n * size and prints at the printer's resolution rather than the screen's.\n *\n * This matters more than it sounds for scanning. A raster QR code scaled to a\n * non-integer multiple of its module size gets blurred module edges, and a\n * scanner reading a blurred edge guesses. Vector output cannot have that\n * problem.\n *\n * ## `value` is any string \u2014 this is not a link component\n *\n * A QR code is a byte container, and most of what belongs in one is not a web\n * address: a serial or asset number, a lot code, a ticket id, a WiFi\n * credential block, a contact card, a `tel:` or `geo:` URI, plain text. Those\n * are the cases where the format earns its place, because they need no network\n * at all \u2014 the scanner reads the value and that IS the payload.\n *\n * ## It is an image of a value, not the value\n *\n * A QR code is unreadable to anyone without a second device pointed at the\n * screen \u2014 which includes every screen-reader user and anyone whose phone is\n * the device showing it. So `label` is required, and the value should ALSO be\n * selectable text beside it. A panel that shows only a code is one some people\n * cannot use at all.\n *\n * @example\n * ```tsx\n * \/\/ Any string. A digits-only value is numeric mode and a third the size.\n * <QrCode value=\"SN-4829-13\" size={192} label=\"Asset tag for pump 14\" \/>\n * <QrCode value=\"482913\" size={192} label=\"Order reference\" \/>\n *\n * \/\/ A payload with no network in it at all.\n * <QrCode value=\"WIFI:T:WPA;S:Guest;P:hunter2;;\" label=\"Join the guest WiFi\" \/>\n *\n * \/\/ A link is one payload among many, not the default case.\n * <QrCode value=\"https:\/\/example.com\/menu\" label=\"Today's menu\" \/>\n *\n * \/\/ On a dark surface, set both colours: a transparent background renders\n * \/\/ dark-on-dark and will not scan.\n * <QrCode value={serial} label=\"Asset tag\" foreground=\"#0b0b0c\" background=\"#ffffff\" \/>\n * ```\n *\/\nexport const QrCode = forwardRef<SVGSVGElement, QrCodeProps>(\n  (\n    {\n      value,\n      size = 192,\n      errorCorrection = \"M\",\n      label,\n      quietZone = 4,\n      foreground = \"currentColor\",\n      background = \"transparent\",\n      mode,\n      version,\n      className,\n      ...props\n    },\n    ref,\n  ) => {\n    \/\/ Encoding a version 40 symbol is real work (Reed-Solomon over 2,956\n    \/\/ codewords, then eight masks scored). Nothing about it depends on `size`\n    \/\/ or the colours, so it is memoised on the inputs that actually change it\n    \/\/ \u2014 otherwise a parent re-render on hover re-encodes the whole symbol.\n    const symbol = useMemo(\n      () => encodeQr(value, { errorCorrection, mode, version }),\n      [value, errorCorrection, mode, version],\n    );\n\n    const extent = symbol.size + quietZone * 2;\n\n    const path = useMemo(() => {\n      const parts: string[] = [];\n      for (let row = 0; row < symbol.size; row += 1) {\n        const line = symbol.modules[row] as boolean[];\n        let col = 0;\n        while (col < symbol.size) {\n          if (!line[col]) {\n            col += 1;\n            continue;\n          }\n          \/\/ Run-length the dark modules along the row: a horizontal run\n          \/\/ becomes one rectangle rather than one per module, which roughly\n          \/\/ halves the path for a typical symbol and costs nothing to read.\n          let run = 1;\n          while (col + run < symbol.size && line[col + run]) {\n            run += 1;\n          }\n          parts.push(`M${col + quietZone} ${row + quietZone}h${run}v1h-${run}z`);\n          col += run;\n        }\n      }\n      return parts.join(\"\");\n    }, [symbol, quietZone]);\n\n    return (\n      <svg\n        ref={ref}\n        width={size}\n        height={size}\n        viewBox={`0 0 ${extent} ${extent}`}\n        role=\"img\"\n        aria-label={label}\n        \/\/ Nearest-neighbour rather than smoothing, for the rasterisers that\n        \/\/ apply it anyway: a blurred module edge is a module a scanner has to\n        \/\/ guess at.\n        shapeRendering=\"crispEdges\"\n        className={cn(\"fa-qr-code\", className)}\n        {...props}\n      >\n        {background !== \"transparent\" && (\n          <rect width={extent} height={extent} fill={background} \/>\n        )}\n        <path d={path} fill={foreground} \/>\n      <\/svg>\n    );\n  },\n);\n\nQrCode.displayName = \"QrCode\";\n","type":"registry:ui","target":"components\/fancy\/qr-code\/QrCode.tsx"},{"path":"components\/fancy\/qr-code\/QrCode.types.ts","content":"import type { SVGAttributes } from \"react\";\n\nimport type { QrErrorCorrection, QrMode } from \".\/qr-encode\";\n\nexport type { QrErrorCorrection, QrMode };\n\n\/**\n * `viewBox` is omitted because the component computes it from the symbol's\n * module count \u2014 overriding it would rescale the modules and break the\n * crispness the vector output exists for.\n *\n * `version` is omitted because SVG has an attribute of that name and ours\n * means the QR symbol version, a number. SVG's is a no-op in SVG 2 and was\n * only ever a document-conformance hint, so nothing is lost by shadowing it,\n * where renaming our prop would cost the one word every QR reference uses.\n *\/\nexport interface QrCodeProps\n  extends Omit<SVGAttributes<SVGSVGElement>, \"children\" | \"viewBox\" | \"version\"> {\n  \/**\n   * What the code carries. **Any string** \u2014 this is not a link component.\n   *\n   * A QR code is a byte container, and most of what goes in one is not a web\n   * address: a serial or asset number, a batch or lot code, a ticket or order\n   * id, a WiFi credential block (`WIFI:T:WPA;S:ssid;P:pass;;`), a contact card\n   * (vCard \/ MeCard), a `tel:` \/ `mailto:` \/ `geo:` URI, a shipment code, a\n   * bare numeric reference, or arbitrary text. Several of those are the cases\n   * where a QR code earns its place, because they work with no network at all.\n   *\n   * The encoder picks the narrowest mode the value allows, so a digits-only\n   * value is a third of the size it would be as text \u2014 see {@link mode}.\n   *\n   * An empty string renders a valid (empty) symbol rather than throwing, so a\n   * controlled surface can render before its value arrives.\n   *\/\n  value: string;\n  \/** Rendered width and height in pixels. Default 192. *\/\n  size?: number;\n  \/**\n   * Error correction level. Default `\"M\"` \u2014 about 15% of the symbol can be\n   * damaged and still read, which is the usual choice for a screen.\n   *\n   * Higher is not better by default: `\"H\"` survives ~30% damage and holds a\n   * third less, so the same value needs a bigger symbol with smaller modules,\n   * and on a phone screen that can read WORSE than `\"M\"` at the same pixel\n   * size. Raise it for print, for a label that will be scuffed, or when a logo\n   * covers part of the symbol.\n   *\/\n  errorCorrection?: QrErrorCorrection;\n  \/**\n   * Accessible name. **Required**, and it must describe WHAT THE CODE IS FOR\n   * rather than the artefact: \"Asset tag for pump 14\" or \"Join the guest WiFi\"\n   * tells a screen-reader user something useful, where \"QR code\" tells them\n   * only that an image they cannot use is present.\n   *\n   * It is not a substitute for the value being available another way. A QR code\n   * is unusable by anyone who cannot point a second device at this screen, so\n   * the value should also appear as selectable text nearby \u2014 the `aria-label`\n   * cannot carry a serial number or a password someone is expected to read off.\n   *\/\n  label: string;\n  \/**\n   * Modules of quiet zone around the symbol. Default 4, which is the\n   * standard's minimum.\n   *\n   * **Do not set this to 0 to save space.** The quiet zone is not padding; it\n   * is how a scanner finds the symbol's edge. A code flush against\n   * surrounding content is the single most common reason a QR code that looks\n   * perfect will not scan.\n   *\/\n  quietZone?: number;\n  \/** Module colour. Default `currentColor`, so it inherits text colour. *\/\n  foreground?: string;\n  \/**\n   * Background colour. Default `\"transparent\"`.\n   *\n   * **A transparent background inherits whatever is behind it**, so on a dark\n   * surface the symbol renders dark-on-dark and most scanners will not read\n   * it. Set an explicit light background, or set `foreground` and background\n   * together, when the surface is not light. Contrast is what gets scanned,\n   * not the dark modules on their own.\n   *\/\n  background?: string;\n  \/**\n   * Force a mode instead of picking the narrowest that fits. Rarely useful;\n   * the default packs digits three to ten bits where byte mode spends 24.\n   *\/\n  mode?: QrMode;\n  \/**\n   * Force a symbol version (1\u201340), fixing the module count regardless of how\n   * little the value needs. Useful when several codes must render at the same\n   * module scale. Throws if the value does not fit.\n   *\/\n  version?: number;\n}\n","type":"registry:ui","target":"components\/fancy\/qr-code\/QrCode.types.ts"},{"path":"components\/fancy\/qr-code\/index.ts","content":"export { QrCode } from \".\/QrCode\";\nexport type { QrCodeProps, QrErrorCorrection, QrMode } from \".\/QrCode.types\";\nexport { encodeQr, selectMode } from \".\/qr-encode\";\nexport type { EncodeOptions, QrSymbol } from \".\/qr-encode\";\n","type":"registry:ui","target":"components\/fancy\/qr-code\/index.ts"},{"path":"components\/fancy\/qr-code\/qr-encode.ts","content":"\/**\n * A QR encoder, dependency-free.\n *\n * ISO\/IEC 18004, versions 1\u201340, error correction L\/M\/Q\/H, with numeric,\n * alphanumeric and byte modes and automatic selection between them. No\n * Kanji mode and no structured append \u2014 neither has a caller here, and an\n * untested branch in an encoder is worse than an absent one because the\n * failure is a code a phone silently refuses to read.\n *\n * Table data and its provenance live in `.\/qr-tables`.\n *\/\nimport {\n  EC_FORMAT_BITS,\n  FORMAT_BITS,\n  MAX_VERSION,\n  MIN_VERSION,\n  alignmentPositions,\n  blockLayout,\n  versionBits,\n  type QrErrorCorrection,\n} from '.\/qr-tables';\n\nexport type { QrErrorCorrection };\n\n\/** Which encoding mode a value is carried in. *\/\nexport type QrMode = 'numeric' | 'alphanumeric' | 'byte';\n\nconst MODE_INDICATOR: Record<QrMode, number> = { numeric: 0b0001, alphanumeric: 0b0010, byte: 0b0100 };\n\n\/** The character set alphanumeric mode can carry, in its own index order. *\/\nconst ALPHANUMERIC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-.\/:';\n\nexport interface QrSymbol {\n  \/** `size * size` modules, row-major. `true` is dark. *\/\n  modules: boolean[][];\n  \/** Modules per side, `version * 4 + 17`. *\/\n  size: number;\n  version: number;\n  errorCorrection: QrErrorCorrection;\n  mode: QrMode;\n  \/** The mask pattern chosen by penalty scoring, 0\u20137. *\/\n  mask: number;\n}\n\n\/\/ --- Galois field GF(256) --------------------------------------------------\n\/\/ Reed-Solomon over the field the standard specifies: primitive polynomial\n\/\/ x^8 + x^4 + x^3 + x^2 + 1 (0x11d), generator 2.\n\nconst EXP = new Uint8Array(256);\nconst LOG = new Uint8Array(256);\n{\n  let x = 1;\n  for (let i = 0; i < 255; i += 1) {\n    EXP[i] = x;\n    LOG[x] = i;\n    x <<= 1;\n    if (x & 0x100) {\n      x ^= 0x11d;\n    }\n  }\n  EXP[255] = EXP[0] as number;\n}\n\nfunction gfMul(a: number, b: number): number {\n  if (a === 0 || b === 0) {\n    return 0;\n  }\n  return EXP[((LOG[a] as number) + (LOG[b] as number)) % 255] as number;\n}\n\n\/** The generator polynomial for `degree` error correction codewords. *\/\nfunction generatorPoly(degree: number): number[] {\n  let poly = [1];\n  for (let i = 0; i < degree; i += 1) {\n    const next = new Array<number>(poly.length + 1).fill(0);\n    for (let j = 0; j < poly.length; j += 1) {\n      next[j] = (next[j] as number) ^ (poly[j] as number);\n      next[j + 1] = (next[j + 1] as number) ^ gfMul(poly[j] as number, EXP[i] as number);\n    }\n    poly = next;\n  }\n  return poly;\n}\n\n\/** Reed-Solomon remainder \u2014 the error correction codewords for one block. *\/\nfunction reedSolomon(data: readonly number[], degree: number): number[] {\n  const generator = generatorPoly(degree);\n  const remainder = new Array<number>(degree).fill(0);\n  for (const byte of data) {\n    const factor = byte ^ (remainder.shift() as number);\n    remainder.push(0);\n    for (let i = 0; i < degree; i += 1) {\n      remainder[i] = (remainder[i] as number) ^ gfMul(generator[i + 1] as number, factor);\n    }\n  }\n  return remainder;\n}\n\n\/\/ --- Mode selection and bit encoding ---------------------------------------\n\n\/**\n * The narrowest mode that can carry `value`.\n *\n * Narrower is denser: numeric packs 3 digits into 10 bits where byte mode\n * would spend 24. A six-digit pairing PIN is a third of the size as numeric,\n * which is the difference between a code a phone reads across a desk and one\n * it does not.\n *\/\nexport function selectMode(value: string): QrMode {\n  if (\/^[0-9]*$\/.test(value)) {\n    return 'numeric';\n  }\n  if ([...value].every((char) => ALPHANUMERIC.includes(char))) {\n    return 'alphanumeric';\n  }\n  return 'byte';\n}\n\n\/** Bits in the character count indicator \u2014 it widens with version. *\/\nfunction countBits(mode: QrMode, version: number): number {\n  const tier = version <= 9 ? 0 : version <= 26 ? 1 : 2;\n  if (mode === 'numeric') {\n    return [10, 12, 14][tier] as number;\n  }\n  if (mode === 'alphanumeric') {\n    return [9, 11, 13][tier] as number;\n  }\n  return [8, 16, 16][tier] as number;\n}\n\nclass BitBuffer {\n  readonly bits: number[] = [];\n\n  push(value: number, length: number): void {\n    for (let i = length - 1; i >= 0; i -= 1) {\n      this.bits.push((value >>> i) & 1);\n    }\n  }\n}\n\n\/** UTF-8 bytes for a string, without TextEncoder \u2014 jsdom and SSR both agree. *\/\nfunction utf8Bytes(value: string): number[] {\n  const out: number[] = [];\n  for (const char of value) {\n    const code = char.codePointAt(0) as number;\n    if (code < 0x80) {\n      out.push(code);\n    } else if (code < 0x800) {\n      out.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));\n    } else if (code < 0x10000) {\n      out.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));\n    } else {\n      out.push(\n        0xf0 | (code >> 18),\n        0x80 | ((code >> 12) & 0x3f),\n        0x80 | ((code >> 6) & 0x3f),\n        0x80 | (code & 0x3f),\n      );\n    }\n  }\n  return out;\n}\n\n\/** The payload's own bits, before terminator and padding. *\/\nfunction encodePayload(value: string, mode: QrMode, version: number): BitBuffer {\n  const buffer = new BitBuffer();\n  buffer.push(MODE_INDICATOR[mode], 4);\n\n  if (mode === 'numeric') {\n    buffer.push(value.length, countBits(mode, version));\n    for (let i = 0; i < value.length; i += 3) {\n      const group = value.slice(i, i + 3);\n      buffer.push(Number.parseInt(group, 10), group.length * 3 + 1);\n    }\n    return buffer;\n  }\n\n  if (mode === 'alphanumeric') {\n    buffer.push(value.length, countBits(mode, version));\n    for (let i = 0; i < value.length; i += 2) {\n      const first = ALPHANUMERIC.indexOf(value[i] as string);\n      if (i + 1 < value.length) {\n        buffer.push(first * 45 + ALPHANUMERIC.indexOf(value[i + 1] as string), 11);\n      } else {\n        buffer.push(first, 6);\n      }\n    }\n    return buffer;\n  }\n\n  const bytes = utf8Bytes(value);\n  buffer.push(bytes.length, countBits(mode, version));\n  for (const byte of bytes) {\n    buffer.push(byte, 8);\n  }\n  return buffer;\n}\n\n\/** How many bits the payload needs at a given version, mode included. *\/\nfunction payloadBits(value: string, mode: QrMode, version: number): number {\n  return encodePayload(value, mode, version).bits.length;\n}\n\n\/**\n * The most characters any mode can carry, at version 40 level L.\n *\n * DERIVED from the block table rather than transcribed, so it cannot disagree\n * with the capacities the encoder actually enforces. (The published figures\n * are 7089 \/ 4296 \/ 2953, and the test asserts these match them.)\n *\/\nexport function maxCharacters(mode: QrMode): number {\n  const bits = blockLayout(MAX_VERSION, 'L').dataCodewords * 8 - 4 - countBits(mode, MAX_VERSION);\n  if (mode === 'numeric') {\n    return Math.floor((bits * 3) \/ 10);\n  }\n  if (mode === 'alphanumeric') {\n    return Math.floor((bits * 2) \/ 11);\n  }\n  return Math.floor(bits \/ 8);\n}\n\n\/** The largest capacity of any mode \u2014 the cheapest possible rejection. *\/\nconst ABSOLUTE_MAX_CHARACTERS = maxCharacters('numeric');\n\n\/**\n * Reject an impossible value in O(1), before anything walks it.\n *\n * `value` is text this code did not author, and it reaches consumers two ways:\n * the package, and as VENDORED SOURCE via `fancy-cli add qr-code` \u2014 which\n * carries no manifest, so nothing watches it. So the question is what a hostile\n * caller gets out of it, and the answer used to be: a lot of work for free.\n *\n * `selectVersion` walks versions 1-40 and re-encoded the payload at each one,\n * so a value too long to fit was encoded FORTY times before being refused.\n * Measured on the built bundle: 342 ms for 100 kB, 2.0 s for 500 kB, linear \u2014\n * so a few megabytes through `<QrCode value={userInput} \/>` blocks the main\n * thread for tens of seconds. The refusal was always correct; it was the COST\n * of the refusal that was the defect.\n *\n * A character count can only ever be <= the byte count, so a string longer than\n * the largest capacity cannot fit in any mode and is refused from `.length`\n * alone, touching none of it.\n *\/\nfunction tooLongToFit(value: string, mode: QrMode | null): boolean {\n  if (value.length > ABSOLUTE_MAX_CHARACTERS) {\n    return true;\n  }\n  return mode !== null && value.length > maxCharacters(mode);\n}\n\n\/** The smallest version that fits, or `null` when nothing does. *\/\nfunction selectVersion(\n  value: string,\n  mode: QrMode,\n  ec: QrErrorCorrection,\n  minVersion: number,\n): number | null {\n  \/\/ The payload's bit length depends on the version ONLY through the width of\n  \/\/ the character-count indicator, which has three tiers. So it is computed at\n  \/\/ most three times rather than once per version \u2014 which also makes the\n  \/\/ legitimate path cheaper for a large value, not just the hostile one.\n  const bitsForTier = new Map<number, number>();\n  for (let version = Math.max(minVersion, MIN_VERSION); version <= MAX_VERSION; version += 1) {\n    const width = countBits(mode, version);\n    let bits = bitsForTier.get(width);\n    if (bits === undefined) {\n      bits = payloadBits(value, mode, version);\n      bitsForTier.set(width, bits);\n    }\n    if (bits <= blockLayout(version, ec).dataCodewords * 8) {\n      return version;\n    }\n  }\n  return null;\n}\n\n\/**\n * Data codewords: payload, terminator, pad to a byte, then alternating pad\n * bytes. Exported for the golden-vector test, which asserts these against the\n * standard's published worked example.\n *\/\nexport function dataCodewords(value: string, mode: QrMode, version: number, ec: QrErrorCorrection): number[] {\n  const capacity = blockLayout(version, ec).dataCodewords * 8;\n  const buffer = encodePayload(value, mode, version);\n\n  \/\/ Terminator: up to four zero bits, truncated if capacity runs out.\n  buffer.push(0, Math.min(4, capacity - buffer.bits.length));\n  \/\/ Then to a byte boundary.\n  buffer.push(0, (8 - (buffer.bits.length % 8)) % 8);\n\n  const codewords: number[] = [];\n  for (let i = 0; i < buffer.bits.length; i += 8) {\n    let byte = 0;\n    for (let bit = 0; bit < 8; bit += 1) {\n      byte = (byte << 1) | (buffer.bits[i + bit] as number);\n    }\n    codewords.push(byte);\n  }\n\n  \/\/ Pad bytes 0b11101100 \/ 0b00010001, alternating, as the standard specifies.\n  const target = capacity \/ 8;\n  for (let i = 0; codewords.length < target; i += 1) {\n    codewords.push(i % 2 === 0 ? 0xec : 0x11);\n  }\n  return codewords;\n}\n\n\/**\n * Split into blocks, compute error correction, and interleave.\n *\n * Interleaving is what makes the error correction work as advertised: a blot\n * on the printed symbol damages a contiguous run of modules, and interleaving\n * spreads that run across every block so no single block exceeds its own\n * correction budget. Concatenating instead would produce a scannable code that\n * fails far sooner than its level claims \u2014 passing a round-trip test and\n * failing on paper.\n *\/\nexport function interleave(codewords: readonly number[], version: number, ec: QrErrorCorrection): number[] {\n  const { ecPerBlock, g1Blocks, g1Size, g2Blocks, g2Size } = blockLayout(version, ec);\n\n  const dataBlocks: number[][] = [];\n  let offset = 0;\n  for (let i = 0; i < g1Blocks; i += 1) {\n    dataBlocks.push(codewords.slice(offset, offset + g1Size));\n    offset += g1Size;\n  }\n  for (let i = 0; i < g2Blocks; i += 1) {\n    dataBlocks.push(codewords.slice(offset, offset + g2Size));\n    offset += g2Size;\n  }\n\n  const ecBlocks = dataBlocks.map((block) => reedSolomon(block, ecPerBlock));\n\n  const result: number[] = [];\n  const widest = Math.max(g1Size, g2Blocks > 0 ? g2Size : 0);\n  for (let i = 0; i < widest; i += 1) {\n    for (const block of dataBlocks) {\n      if (i < block.length) {\n        result.push(block[i] as number);\n      }\n    }\n  }\n  for (let i = 0; i < ecPerBlock; i += 1) {\n    for (const block of ecBlocks) {\n      result.push(block[i] as number);\n    }\n  }\n  return result;\n}\n\n\/\/ --- Matrix construction ---------------------------------------------------\n\nexport type Grid = (boolean | null)[][];\n\n\/**\n * A grid with every function pattern placed and data positions left `null`.\n *\n * Exported for verification. The round-trip decoder in the test needs to know\n * which modules carry data, and reconstructing that knowledge separately would\n * duplicate the layout and share its bugs. Using the same template is safe\n * here because the table-consistency check anchors it to an independent fact:\n * the number of free modules must equal the codeword count in the transcribed\n * block table, so a wrong finder, timing or alignment layout fails there.\n *\/\nexport function reservedModules(version: number): Grid {\n  const size = version * 4 + 17;\n  const grid: Grid = Array.from({ length: size }, () => new Array<boolean | null>(size).fill(null));\n\n  const set = (row: number, col: number, dark: boolean): void => {\n    (grid[row] as (boolean | null)[])[col] = dark;\n  };\n\n  \/\/ Three finder patterns with their separators.\n  for (const [baseRow, baseCol] of [\n    [0, 0],\n    [0, size - 7],\n    [size - 7, 0],\n  ] as const) {\n    for (let row = -1; row <= 7; row += 1) {\n      for (let col = -1; col <= 7; col += 1) {\n        const r = baseRow + row;\n        const c = baseCol + col;\n        if (r < 0 || r >= size || c < 0 || c >= size) {\n          continue;\n        }\n        const ring = Math.max(Math.abs(row - 3), Math.abs(col - 3));\n        set(r, c, ring !== 2 && ring <= 3);\n      }\n    }\n  }\n\n  \/\/ Timing patterns, row and column 6.\n  for (let i = 0; i < size; i += 1) {\n    if ((grid[6] as (boolean | null)[])[i] === null) {\n      set(6, i, i % 2 === 0);\n    }\n    if ((grid[i] as (boolean | null)[])[6] === null) {\n      set(i, 6, i % 2 === 0);\n    }\n  }\n\n  \/\/ Alignment patterns, except where a finder already sits.\n  const positions = alignmentPositions(version);\n  for (const row of positions) {\n    for (const col of positions) {\n      const nearFinder =\n        (row <= 8 && col <= 8) || (row <= 8 && col >= size - 9) || (row >= size - 9 && col <= 8);\n      if (nearFinder) {\n        continue;\n      }\n      for (let dr = -2; dr <= 2; dr += 1) {\n        for (let dc = -2; dc <= 2; dc += 1) {\n          const ring = Math.max(Math.abs(dr), Math.abs(dc));\n          set(row + dr, col + dc, ring !== 1);\n        }\n      }\n    }\n  }\n\n  \/\/ Format information areas, reserved now and written after masking. The\n  \/\/ module at (size - 8, 8) is always dark.\n  for (let i = 0; i < 9; i += 1) {\n    if ((grid[8] as (boolean | null)[])[i] === null) {\n      set(8, i, false);\n    }\n    if ((grid[i] as (boolean | null)[])[8] === null) {\n      set(i, 8, false);\n    }\n  }\n  for (let i = 0; i < 8; i += 1) {\n    set(8, size - 1 - i, false);\n    set(size - 1 - i, 8, false);\n  }\n  set(size - 8, 8, true);\n\n  \/\/ Version information, versions 7 and up.\n  if (version >= 7) {\n    const bits = versionBits(version);\n    for (let i = 0; i < 18; i += 1) {\n      const bit = ((bits >>> i) & 1) === 1;\n      set(Math.floor(i \/ 3), size - 11 + (i % 3), bit);\n      set(size - 11 + (i % 3), Math.floor(i \/ 3), bit);\n    }\n  }\n\n  return grid;\n}\n\n\/** How many modules a version leaves free for data, counted from the grid. *\/\nexport function rawDataModules(version: number): number {\n  const grid = reservedModules(version);\n  let free = 0;\n  for (const row of grid) {\n    for (const cell of row) {\n      if (cell === null) {\n        free += 1;\n      }\n    }\n  }\n  return free;\n}\n\n\/** Place the interleaved codewords in the standard's zigzag order. *\/\nfunction placeData(grid: Grid, codewords: readonly number[]): void {\n  const size = grid.length;\n  let bitIndex = 0;\n  const nextBit = (): boolean => {\n    const byte = codewords[bitIndex >>> 3];\n    \/\/ Past the end is a remainder bit: light, per the standard.\n    const bit = byte === undefined ? 0 : (byte >>> (7 - (bitIndex & 7))) & 1;\n    bitIndex += 1;\n    return bit === 1;\n  };\n\n  let upward = true;\n  for (let right = size - 1; right >= 1; right -= 2) {\n    \/\/ Column 6 is the vertical timing pattern; the pair of columns skips it.\n    const pair = right === 6 ? [5, 4] : [right, right - 1];\n    for (let step = 0; step < size; step += 1) {\n      const row = upward ? size - 1 - step : step;\n      for (const col of pair) {\n        if ((grid[row] as (boolean | null)[])[col] === null) {\n          (grid[row] as (boolean | null)[])[col] = nextBit();\n        }\n      }\n    }\n    if (right === 6) {\n      right -= 1;\n    }\n    upward = !upward;\n  }\n}\n\n\/** The eight mask conditions. `true` means invert this module. *\/\nfunction maskCondition(mask: number, row: number, col: number): boolean {\n  switch (mask) {\n    case 0:\n      return (row + col) % 2 === 0;\n    case 1:\n      return row % 2 === 0;\n    case 2:\n      return col % 3 === 0;\n    case 3:\n      return (row + col) % 3 === 0;\n    case 4:\n      return (Math.floor(row \/ 2) + Math.floor(col \/ 3)) % 2 === 0;\n    case 5:\n      return ((row * col) % 2) + ((row * col) % 3) === 0;\n    case 6:\n      return (((row * col) % 2) + ((row * col) % 3)) % 2 === 0;\n    default:\n      return (((row + col) % 2) + ((row * col) % 3)) % 2 === 0;\n  }\n}\n\n\/**\n * The four penalty rules, scored on a finished candidate.\n *\n * This is the step that is easy to skip and expensive to skip: the rules exist\n * because some masks leave large light or dark areas, or patterns that look\n * like a finder, and a decoder locks onto the wrong feature. A symbol with a\n * badly chosen mask still decodes in software and fails under a phone camera \u2014\n * which is the worst possible place for it to fail, because every test here\n * would pass.\n *\/\nexport function penalty(modules: readonly boolean[][]): number {\n  const size = modules.length;\n  let score = 0;\n\n  \/\/ Rule 1: runs of five or more same-coloured modules in a line.\n  const scoreLine = (run: number): number => (run >= 5 ? run - 2 : 0);\n  for (let i = 0; i < size; i += 1) {\n    for (const horizontal of [true, false]) {\n      let run = 1;\n      for (let j = 1; j < size; j += 1) {\n        const prev = horizontal\n          ? (modules[i] as boolean[])[j - 1]\n          : (modules[j - 1] as boolean[])[i];\n        const cur = horizontal ? (modules[i] as boolean[])[j] : (modules[j] as boolean[])[i];\n        if (cur === prev) {\n          run += 1;\n        } else {\n          score += scoreLine(run);\n          run = 1;\n        }\n      }\n      score += scoreLine(run);\n    }\n  }\n\n  \/\/ Rule 2: every 2x2 block of one colour.\n  for (let row = 0; row < size - 1; row += 1) {\n    for (let col = 0; col < size - 1; col += 1) {\n      const a = (modules[row] as boolean[])[col];\n      if (\n        a === (modules[row] as boolean[])[col + 1] &&\n        a === (modules[row + 1] as boolean[])[col] &&\n        a === (modules[row + 1] as boolean[])[col + 1]\n      ) {\n        score += 3;\n      }\n    }\n  }\n\n  \/\/ Rule 3: the finder-like 1:1:3:1:1 pattern with four light modules either\n  \/\/ side. This is the rule that protects a decoder's pattern detection.\n  const finderish = [true, false, true, true, true, false, true];\n  const light4 = [false, false, false, false];\n  const matches = (line: readonly boolean[], at: number, pattern: readonly boolean[]): boolean =>\n    pattern.every((want, i) => line[at + i] === want);\n  for (let i = 0; i < size; i += 1) {\n    const row = modules[i] as boolean[];\n    const col = modules.map((r) => (r as boolean[])[i] as boolean);\n    for (const line of [row, col]) {\n      for (let j = 0; j + 7 <= size; j += 1) {\n        if (!matches(line, j, finderish)) {\n          continue;\n        }\n        \/\/ Each ELEVEN-module window counts separately, so a pattern with four\n        \/\/ light modules on BOTH sides scores 80 rather than 40.\n        \/\/\n        \/\/ The prose rule (\"add 40 each time the pattern appears with four\n        \/\/ light modules on either side\") is ambiguous about that case, and it\n        \/\/ matters: this score is what picks the mask, and the wrong mask\n        \/\/ produces a symbol that decodes perfectly in software and reads badly\n        \/\/ under a camera. No test here can catch that -- so the convention is\n        \/\/ pinned by a test instead, and this follows the one the reference\n        \/\/ implementations use rather than the reading that happened to come\n        \/\/ first.\n        if (j >= 4 && matches(line, j - 4, light4)) {\n          score += 40;\n        }\n        if (j + 11 <= size && matches(line, j + 7, light4)) {\n          score += 40;\n        }\n      }\n    }\n  }\n\n  \/\/ Rule 4: deviation of the dark-module proportion from 50%.\n  let dark = 0;\n  for (const row of modules) {\n    for (const cell of row) {\n      if (cell) {\n        dark += 1;\n      }\n    }\n  }\n  const percent = (dark * 100) \/ (size * size);\n  score += Math.floor(Math.abs(percent - 50) \/ 5) * 10;\n\n  return score;\n}\n\n\/** Write the 15 format bits into both of their locations. *\/\nfunction applyFormat(modules: boolean[][], ec: QrErrorCorrection, mask: number): void {\n  const size = modules.length;\n  const bits = (FORMAT_BITS[ec][mask] as number) >>> 0;\n  const bit = (i: number): boolean => ((bits >>> i) & 1) === 1;\n\n  \/\/ Bits 0-5 up the left column, bit 6 at (8,7)... the split placement is the\n  \/\/ standard's, not a simplification of it.\n  for (let i = 0; i <= 5; i += 1) {\n    (modules[8] as boolean[])[i] = bit(14 - i);\n  }\n  (modules[8] as boolean[])[7] = bit(8);\n  (modules[8] as boolean[])[8] = bit(7);\n  (modules[7] as boolean[])[8] = bit(6);\n  for (let i = 9; i <= 14; i += 1) {\n    (modules[14 - i] as boolean[])[8] = bit(14 - i);\n  }\n\n  \/\/ The second copy, split across the other two corners: SEVEN modules up the\n  \/\/ column beside the bottom-left finder, then EIGHT along the row beside the\n  \/\/ top-right one.\n  \/\/\n  \/\/ The 7\/8 split is not arbitrary and getting it wrong is quiet. An 8\/7 split\n  \/\/ puts bit 7 at (size - 8, 8) -- which is the dark module, overwritten on\n  \/\/ the next line -- so the symbol still decodes from the FIRST copy and\n  \/\/ carries a corrupt second one. A reader that happens to use the second copy\n  \/\/ refuses the symbol, and nothing in a round-trip test notices, because a\n  \/\/ decoder reading the first copy never looks.\n  for (let i = 0; i <= 6; i += 1) {\n    (modules[size - 1 - i] as boolean[])[8] = bit(i);\n  }\n  for (let i = 7; i <= 14; i += 1) {\n    (modules[8] as boolean[])[size - 15 + i] = bit(i);\n  }\n  (modules[size - 8] as boolean[])[8] = true;\n}\n\nexport interface EncodeOptions {\n  errorCorrection?: QrErrorCorrection;\n  \/** Force a mode instead of selecting the narrowest that fits. *\/\n  mode?: QrMode;\n  \/** Force a version. Throws if the value does not fit. *\/\n  version?: number;\n  \/** Force a mask instead of scoring all eight. For tests and golden vectors. *\/\n  mask?: number;\n}\n\n\/**\n * Encode a string into a QR symbol.\n *\n * @throws RangeError when the value cannot fit, or when a forced version is\n *   too small for it. **Throwing is deliberate**: the alternative is returning\n *   a truncated symbol, which scans cleanly and yields the wrong string \u2014 a\n *   silent corruption on the one surface whose entire job is to carry a value\n *   accurately.\n *\/\nexport function encodeQr(value: string, options: EncodeOptions = {}): QrSymbol {\n  const ec = options.errorCorrection ?? 'M';\n\n  \/\/ Before `selectMode`, which walks the whole string. See `tooLongToFit`:\n  \/\/ this is the difference between refusing a 5 MB value instantly and\n  \/\/ spending tens of seconds on the main thread to reach the same answer.\n  if (tooLongToFit(value, options.mode ?? null)) {\n    throw new RangeError(\n      `Value is too long for a QR code (${value.length} characters; the maximum any mode can carry is ${ABSOLUTE_MAX_CHARACTERS})`,\n    );\n  }\n\n  const mode = options.mode ?? selectMode(value);\n  if (tooLongToFit(value, mode)) {\n    throw new RangeError(\n      `Value is too long for a QR code in ${mode} mode (${value.length} characters; the maximum is ${maxCharacters(mode)})`,\n    );\n  }\n\n  if (mode === 'numeric' && !\/^[0-9]*$\/.test(value)) {\n    throw new RangeError('Numeric mode accepts digits only');\n  }\n  if (mode === 'alphanumeric' && ![...value].every((char) => ALPHANUMERIC.includes(char))) {\n    throw new RangeError('Alphanumeric mode accepts 0-9 A-Z space and $%*+-.\/: only');\n  }\n\n  let version: number;\n  if (options.version !== undefined) {\n    if (options.version < MIN_VERSION || options.version > MAX_VERSION) {\n      throw new RangeError(`Version must be ${MIN_VERSION}-${MAX_VERSION}`);\n    }\n    version = options.version;\n    if (payloadBits(value, mode, version) > blockLayout(version, ec).dataCodewords * 8) {\n      throw new RangeError(`Value does not fit in version ${version} at level ${ec}`);\n    }\n  } else {\n    const chosen = selectVersion(value, mode, ec, MIN_VERSION);\n    if (chosen === null) {\n      throw new RangeError(\n        `Value is too long for a QR code at level ${ec} (${value.length} characters)`,\n      );\n    }\n    version = chosen;\n  }\n\n  const codewords = interleave(dataCodewords(value, mode, version, ec), version, ec);\n  const template = reservedModules(version);\n\n  const candidates: { mask: number; modules: boolean[][] }[] = [];\n  const masksToTry = options.mask === undefined ? [0, 1, 2, 3, 4, 5, 6, 7] : [options.mask];\n  for (const mask of masksToTry) {\n    const grid = template.map((row) => [...row]);\n    placeData(grid, codewords);\n    const modules = grid.map((row, rowIndex) =>\n      row.map((cell, colIndex) => {\n        const value_ = cell as boolean;\n        \/\/ Only data modules are masked; function patterns never are, which is\n        \/\/ why the mask is applied against the TEMPLATE's null positions rather\n        \/\/ than against the finished grid.\n        const isData = (template[rowIndex] as (boolean | null)[])[colIndex] === null;\n        return isData && maskCondition(mask, rowIndex, colIndex) ? !value_ : value_;\n      }),\n    );\n    applyFormat(modules, ec, mask);\n    candidates.push({ mask, modules });\n  }\n\n  const best = candidates.reduce((a, b) => (penalty(a.modules) <= penalty(b.modules) ? a : b));\n\n  return {\n    modules: best.modules,\n    size: version * 4 + 17,\n    version,\n    errorCorrection: ec,\n    mode,\n    mask: best.mask,\n  };\n}\n","type":"registry:ui","target":"components\/fancy\/qr-code\/qr-encode.ts"},{"path":"components\/fancy\/qr-code\/qr-tables.ts","content":"\/**\n * ISO\/IEC 18004 table data for the QR encoder. **Data, not logic** \u2014 kept in\n * its own module so the algorithm beside it can be read without scrolling past\n * 160 rows, and so a correction here is a diff that cannot touch behaviour.\n *\n * There is no formula for these: the block structure per version and error\n * correction level is a table in the standard. They were taken from the\n * published error-correction table at\n * <https:\/\/www.thonky.com\/qr-code-tutorial\/error-correction-table> rather than\n * recalled, and every row is checked by `qr-code.test.tsx` against three\n * equations that must all hold simultaneously:\n *\n *   1. `dataCodewords === g1Blocks * g1Size + g2Blocks * g2Size`\n *   2. `totalCodewords === dataCodewords + ecPerBlock * (g1Blocks + g2Blocks)`\n *   3. `totalCodewords === rawDataModules(version) \/ 8`, where\n *      `rawDataModules` is counted from the matrix THIS code builds.\n *\n * The third is the one worth having: it ties a transcribed table to an\n * independently constructed matrix, so a mistyped row, a wrong alignment\n * pattern position and a wrong function-pattern layout cannot all agree.\n *\/\n\n\/** Error correction level. Higher levels survive more damage and hold less. *\/\nexport type QrErrorCorrection = 'L' | 'M' | 'Q' | 'H';\n\nexport const EC_LEVELS: readonly QrErrorCorrection[] = ['L', 'M', 'Q', 'H'];\n\n\/** The 2-bit indicator each level carries in the format information. *\/\nexport const EC_FORMAT_BITS: Record<QrErrorCorrection, number> = {\n  L: 0b01,\n  M: 0b00,\n  Q: 0b11,\n  H: 0b10,\n};\n\nexport interface BlockLayout {\n  \/** Data codewords in the whole symbol. *\/\n  dataCodewords: number;\n  \/** Error correction codewords per block \u2014 the same for every block. *\/\n  ecPerBlock: number;\n  \/** Blocks in group 1, and how many data codewords each holds. *\/\n  g1Blocks: number;\n  g1Size: number;\n  \/** Blocks in group 2 (always `g1Size + 1` each when present). *\/\n  g2Blocks: number;\n  g2Size: number;\n}\n\n\/**\n * `[dataCodewords, ecPerBlock, g1Blocks, g1Size, g2Blocks, g2Size]` indexed by\n * `[version - 1]` then by EC level, in `L, M, Q, H` order.\n *\/\nconst BLOCK_TABLE: readonly (readonly [number, number, number, number, number, number])[][] = [\n  \/\/ v1\n  [[19, 7, 1, 19, 0, 0], [16, 10, 1, 16, 0, 0], [13, 13, 1, 13, 0, 0], [9, 17, 1, 9, 0, 0]],\n  [[34, 10, 1, 34, 0, 0], [28, 16, 1, 28, 0, 0], [22, 22, 1, 22, 0, 0], [16, 28, 1, 16, 0, 0]],\n  [[55, 15, 1, 55, 0, 0], [44, 26, 1, 44, 0, 0], [34, 18, 2, 17, 0, 0], [26, 22, 2, 13, 0, 0]],\n  [[80, 20, 1, 80, 0, 0], [64, 18, 2, 32, 0, 0], [48, 26, 2, 24, 0, 0], [36, 16, 4, 9, 0, 0]],\n  [[108, 26, 1, 108, 0, 0], [86, 24, 2, 43, 0, 0], [62, 18, 2, 15, 2, 16], [46, 22, 2, 11, 2, 12]],\n  [[136, 18, 2, 68, 0, 0], [108, 16, 4, 27, 0, 0], [76, 24, 4, 19, 0, 0], [60, 28, 4, 15, 0, 0]],\n  [[156, 20, 2, 78, 0, 0], [124, 18, 4, 31, 0, 0], [88, 18, 2, 14, 4, 15], [66, 26, 4, 13, 1, 14]],\n  [[194, 24, 2, 97, 0, 0], [154, 22, 2, 38, 2, 39], [110, 22, 4, 18, 2, 19], [86, 26, 4, 14, 2, 15]],\n  [[232, 30, 2, 116, 0, 0], [182, 22, 3, 36, 2, 37], [132, 20, 4, 16, 4, 17], [100, 24, 4, 12, 4, 13]],\n  \/\/ v10\n  [[274, 18, 2, 68, 2, 69], [216, 26, 4, 43, 1, 44], [154, 24, 6, 19, 2, 20], [122, 28, 6, 15, 2, 16]],\n  [[324, 20, 4, 81, 0, 0], [254, 30, 1, 50, 4, 51], [180, 28, 4, 22, 4, 23], [140, 24, 3, 12, 8, 13]],\n  [[370, 24, 2, 92, 2, 93], [290, 22, 6, 36, 2, 37], [206, 26, 4, 20, 6, 21], [158, 28, 7, 14, 4, 15]],\n  [[428, 26, 4, 107, 0, 0], [334, 22, 8, 37, 1, 38], [244, 24, 8, 20, 4, 21], [180, 22, 12, 11, 4, 12]],\n  [[461, 30, 3, 115, 1, 116], [365, 24, 4, 40, 5, 41], [261, 20, 11, 16, 5, 17], [197, 24, 11, 12, 5, 13]],\n  [[523, 22, 5, 87, 1, 88], [415, 24, 5, 41, 5, 42], [295, 30, 5, 24, 7, 25], [223, 24, 11, 12, 7, 13]],\n  [[589, 24, 5, 98, 1, 99], [453, 28, 7, 45, 3, 46], [325, 24, 15, 19, 2, 20], [253, 30, 3, 15, 13, 16]],\n  [[647, 28, 1, 107, 5, 108], [507, 28, 10, 46, 1, 47], [367, 28, 1, 22, 15, 23], [283, 28, 2, 14, 17, 15]],\n  [[721, 30, 5, 120, 1, 121], [563, 26, 9, 43, 4, 44], [397, 28, 17, 22, 1, 23], [313, 28, 2, 14, 19, 15]],\n  [[795, 28, 3, 113, 4, 114], [627, 26, 3, 44, 11, 45], [445, 26, 17, 21, 4, 22], [341, 26, 9, 13, 16, 14]],\n  \/\/ v20\n  [[861, 28, 3, 107, 5, 108], [669, 26, 3, 41, 13, 42], [485, 30, 15, 24, 5, 25], [385, 28, 15, 15, 10, 16]],\n  [[932, 28, 4, 116, 4, 117], [714, 26, 17, 42, 0, 0], [512, 28, 17, 22, 6, 23], [406, 30, 19, 16, 6, 17]],\n  [[1006, 28, 2, 111, 7, 112], [782, 28, 17, 46, 0, 0], [568, 30, 7, 24, 16, 25], [442, 24, 34, 13, 0, 0]],\n  [[1094, 30, 4, 121, 5, 122], [860, 28, 4, 47, 14, 48], [614, 30, 11, 24, 14, 25], [464, 30, 16, 15, 14, 16]],\n  [[1174, 30, 6, 117, 4, 118], [914, 28, 6, 45, 14, 46], [664, 30, 11, 24, 16, 25], [514, 30, 30, 16, 2, 17]],\n  [[1276, 26, 8, 106, 4, 107], [1000, 28, 8, 47, 13, 48], [718, 30, 7, 24, 22, 25], [538, 30, 22, 15, 13, 16]],\n  [[1370, 28, 10, 114, 2, 115], [1062, 28, 19, 46, 4, 47], [754, 28, 28, 22, 6, 23], [596, 30, 33, 16, 4, 17]],\n  [[1468, 30, 8, 122, 4, 123], [1128, 28, 22, 45, 3, 46], [808, 30, 8, 23, 26, 24], [628, 30, 12, 15, 28, 16]],\n  [[1531, 30, 3, 117, 10, 118], [1193, 28, 3, 45, 23, 46], [871, 30, 4, 24, 31, 25], [661, 30, 11, 15, 31, 16]],\n  [[1631, 30, 7, 116, 7, 117], [1267, 28, 21, 45, 7, 46], [911, 30, 1, 23, 37, 24], [701, 30, 19, 15, 26, 16]],\n  \/\/ v30\n  [[1735, 30, 5, 115, 10, 116], [1373, 28, 19, 47, 10, 48], [985, 30, 15, 24, 25, 25], [745, 30, 23, 15, 25, 16]],\n  [[1843, 30, 13, 115, 3, 116], [1455, 28, 2, 46, 29, 47], [1033, 30, 42, 24, 1, 25], [793, 30, 23, 15, 28, 16]],\n  [[1955, 30, 17, 115, 0, 0], [1541, 28, 10, 46, 23, 47], [1115, 30, 10, 24, 35, 25], [845, 30, 19, 15, 35, 16]],\n  [[2071, 30, 17, 115, 1, 116], [1631, 28, 14, 46, 21, 47], [1171, 30, 29, 24, 19, 25], [901, 30, 11, 15, 46, 16]],\n  [[2191, 30, 13, 115, 6, 116], [1725, 28, 14, 46, 23, 47], [1231, 30, 44, 24, 7, 25], [961, 30, 59, 16, 1, 17]],\n  [[2306, 30, 12, 121, 7, 122], [1812, 28, 12, 47, 26, 48], [1286, 30, 39, 24, 14, 25], [986, 30, 22, 15, 41, 16]],\n  [[2434, 30, 6, 121, 14, 122], [1914, 28, 6, 47, 34, 48], [1354, 30, 46, 24, 10, 25], [1054, 30, 2, 15, 64, 16]],\n  [[2566, 30, 17, 122, 4, 123], [1992, 28, 29, 46, 14, 47], [1426, 30, 49, 24, 10, 25], [1096, 30, 24, 15, 46, 16]],\n  [[2702, 30, 4, 122, 18, 123], [2102, 28, 13, 46, 32, 47], [1502, 30, 48, 24, 14, 25], [1142, 30, 42, 15, 32, 16]],\n  [[2812, 30, 20, 117, 4, 118], [2216, 28, 40, 47, 7, 48], [1582, 30, 43, 24, 22, 25], [1222, 30, 10, 15, 67, 16]],\n  \/\/ v40\n  [[2956, 30, 19, 118, 6, 119], [2334, 28, 18, 47, 31, 48], [1666, 30, 34, 24, 34, 25], [1276, 30, 20, 15, 61, 16]],\n];\n\nexport const MIN_VERSION = 1;\nexport const MAX_VERSION = 40;\n\n\/** Block structure for a version and error correction level. *\/\nexport function blockLayout(version: number, ec: QrErrorCorrection): BlockLayout {\n  const row = BLOCK_TABLE[version - 1]?.[EC_LEVELS.indexOf(ec)];\n  if (!row) {\n    throw new RangeError(`No block layout for version ${version} level ${ec}`);\n  }\n  const [dataCodewords, ecPerBlock, g1Blocks, g1Size, g2Blocks, g2Size] = row;\n  return { dataCodewords, ecPerBlock, g1Blocks, g1Size, g2Blocks, g2Size };\n}\n\n\/**\n * The 15-bit format information for a level and mask, as published.\n *\n * These are a BCH(15,5) code XOR'd with a fixed mask, so they could be\n * computed \u2014 and the first draft did compute them. They are listed instead\n * because the computation and its verification would then share one\n * implementation: a wrong generator polynomial would produce a wrong table and\n * a test that agreed with it. A transcribed table cannot agree with a bug.\n *\n * Source: <https:\/\/www.thonky.com\/qr-code-tutorial\/format-version-tables>.\n * Indexed `[level][mask]`.\n *\/\nexport const FORMAT_BITS: Record<QrErrorCorrection, readonly number[]> = {\n  L: [\n    0b111011111000100, 0b111001011110011, 0b111110110101010, 0b111100010011101,\n    0b110011000101111, 0b110001100011000, 0b110110001000001, 0b110100101110110,\n  ],\n  M: [\n    0b101010000010010, 0b101000100100101, 0b101111001111100, 0b101101101001011,\n    0b100010111111001, 0b100000011001110, 0b100111110010111, 0b100101010100000,\n  ],\n  Q: [\n    0b011010101011111, 0b011000001101000, 0b011111100110001, 0b011101000000110,\n    0b010010010110100, 0b010000110000011, 0b010111011011010, 0b010101111101101,\n  ],\n  H: [\n    0b001011010001001, 0b001001110111110, 0b001110011100111, 0b001100111010000,\n    0b000011101100010, 0b000001001010101, 0b000110100001100, 0b000100000111011,\n  ],\n};\n\n\/**\n * The 18-bit version information, present only for versions 7 and up.\n *\n * A BCH(18,6) code. Computed rather than tabled \u2014 34 rows of 18 bits is more\n * transcription risk than the arithmetic is, and unlike the format bits there\n * are published values for versions 7, 8, 9, 10 and 40 to check it against,\n * which the test does.\n *\/\nexport function versionBits(version: number): number {\n  let remainder = version;\n  for (let i = 0; i < 12; i += 1) {\n    remainder = (remainder << 1) ^ ((remainder >>> 11) * 0b1111100100101);\n  }\n  return ((version << 12) | remainder) >>> 0;\n}\n\n\/**\n * Centre coordinates of the alignment patterns for a version.\n *\n * Derived rather than tabled, and the derivation is checked by the codeword\n * count equation in the test: a wrong position changes how many modules are\n * free for data, so the count would not match the block table.\n *\n * Version 32 is a genuine special case in the standard \u2014 its spacing does not\n * follow the rule the other versions do.\n *\/\nexport function alignmentPositions(version: number): number[] {\n  if (version === 1) {\n    return [];\n  }\n  const count = Math.floor(version \/ 7) + 2;\n  const size = version * 4 + 17;\n  const step = version === 32 ? 26 : Math.ceil((size - 13) \/ (count * 2 - 2)) * 2;\n\n  const positions = [6];\n  for (let pos = size - 7; positions.length < count; pos -= step) {\n    positions.unshift(pos);\n  }\n  return positions.sort((a, b) => a - b);\n}\n","type":"registry:ui","target":"components\/fancy\/qr-code\/qr-tables.ts"},{"path":"components\/fancy\/lib\/utils\/cn.ts","content":"import { twMerge } from \"tailwind-merge\";\n\n\/**\n * A class value: a string, a number, a conditional map, an array of any of\n * those, or nothing. Mirrors what `clsx` accepted, because every component in\n * this package already passes these shapes.\n *\/\nexport type ClassValue =\n  | string\n  | number\n  | bigint\n  | null\n  | undefined\n  | false\n  | ClassDictionary\n  | ClassValue[];\n\ntype ClassDictionary = Record<string, unknown>;\n\n\/**\n * Flatten class values into a space-separated string.\n *\n * This was `clsx` until 2026-08-19. It is roughly twenty lines of logic, it\n * shipped as a RUNTIME dependency to every consumer of this package, and its\n * last commit was 801 days old \u2014 so it fell to the rule that third-party code\n * must be both approved and actively maintained. Owning it costs less than\n * carrying it.\n *\n * `tailwind-merge` deliberately stays: deciding which of two conflicting\n * Tailwind utilities wins requires modelling the entire utility space, which is\n * emphatically not twenty lines, and it is actively maintained.\n *\n * Falsy values are dropped \u2014 including `0` and `\"\"`, which is what `clsx` did\n * and what `tests\/cn.test.ts` pins. A truthy number is kept, because digits are\n * legal class names. `bigint` is admitted because clsx's signature did and\n * the compiler proved it reachable from two call sites here.\n *\/\nfunction flatten(value: ClassValue): string {\n  if (!value) return \"\";\n\n  if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"bigint\") {\n    return String(value);\n  }\n\n  if (Array.isArray(value)) {\n    let out = \"\";\n    for (const item of value) {\n      const part = flatten(item);\n      if (part) out = out ? `${out} ${part}` : part;\n    }\n    return out;\n  }\n\n  let out = \"\";\n  for (const key in value) {\n    if (value[key]) out = out ? `${out} ${key}` : key;\n  }\n  return out;\n}\n\nexport function cn(...inputs: ClassValue[]): string {\n  return twMerge(flatten(inputs));\n}\n","type":"registry:lib","target":"components\/fancy\/lib\/utils\/cn.ts"}]}