{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "qr-code",
  "title": "QR Code",
  "description": "A QR code encoder written from scratch and drawn as crisp SVG with a quiet zone, in square, dotted, framed and logo forms, always dark on light so it scans.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "utils",
    "https://miseui.junsu4621.workers.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/qr-code.tsx",
      "content": "import type * as React from \"react\"\nimport { useId, useMemo, useState } from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\nimport { SPRING_DEFAULT } from \"@/registry/miseui/lib/motion\"\nimport { encodeQr, QrTooLongError, type QrSymbol } from \"@/registry/miseui/ui/qr-code-encode\"\nimport { dotPath, finderPath, logoHole, squarePath } from \"@/registry/miseui/ui/qr-code-shapes\"\nimport type { QrLevel } from \"@/registry/miseui/ui/qr-code-tables\"\nimport { cn } from \"@/registry/miseui/lib/utils\"\n\ntype QrCodeVariant = \"plain\" | \"rounded\" | \"framed\" | \"logo\"\ntype QrCodeEffect = \"reveal\"\n\n/** The standard asks for four modules of light margin; a phone camera needs them to find the edge. */\nconst QUIET_ZONE = 4\nconst DEFAULT_SIZE_PX = 192\n/**\n * Fixed on purpose, in both themes. Readers expect dark on light, and many refuse an inverted symbol,\n * so these two are the one place the theme tokens must not reach.\n */\nconst DARK_MODULE = \"#000000\"\nconst LIGHT_MODULE = \"#ffffff\"\n\ntype QrCodeProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  /** The text or URL to encode, as UTF-8 in byte mode. */\n  value: string\n  /** `plain` square modules, `rounded` dots with soft finders, `framed` a card with a caption, `logo` a centre mark. */\n  variant?: QrCodeVariant\n  /** `reveal` opens the symbol outward once, then removes the clip so nothing stands between it and a camera. */\n  effect?: QrCodeEffect\n  /** Error correction. `logo` always uses H because the mark covers modules. */\n  level?: QrLevel\n  /** Width in px; it shrinks with a narrower container. */\n  size?: number\n  /** What the code is for, read by assistive tech. Say where it leads, not that it is a QR code. */\n  label?: string\n  /** Line under the symbol in `framed`. */\n  caption?: React.ReactNode\n  /** The centre mark of `logo`. Keep it simple: it sits on a white tile. */\n  logo?: React.ReactNode\n}\n\nfunction tryEncode(value: string, level: QrLevel): { symbol?: QrSymbol; error?: string } {\n  try {\n    return { symbol: encodeQr(value, { level }) }\n  } catch (error) {\n    if (error instanceof QrTooLongError) return { error: error.message }\n    throw error\n  }\n}\n\nfunction modulesPath(symbol: QrSymbol, variant: QrCodeVariant) {\n  if (variant === \"rounded\") return dotPath(symbol.modules, QUIET_ZONE) + finderPath(symbol.size, QUIET_ZONE)\n  return squarePath(symbol.modules, QUIET_ZONE, variant === \"logo\" ? logoHole(symbol.size) : undefined)\n}\n\ninterface QrSvgProps {\n  symbol: QrSymbol\n  variant: QrCodeVariant\n  label: string\n  reveal: boolean\n}\n\nfunction QrSvg({ symbol, variant, label, reveal }: QrSvgProps) {\n  const reduceMotion = useReducedMotion() ?? false\n  const clipId = `qr-reveal-${useId().replace(/[^a-zA-Z0-9]/g, \"\")}`\n  const [revealed, setRevealed] = useState(false)\n  const box = symbol.size + QUIET_ZONE * 2\n  const path = useMemo(() => modulesPath(symbol, variant), [symbol, variant])\n  const clipping = reveal && !reduceMotion && !revealed\n  return (\n    <svg\n      role=\"img\"\n      aria-label={label}\n      data-slot=\"qr-code-symbol\"\n      viewBox={`0 0 ${box} ${box}`}\n      shapeRendering={variant === \"rounded\" ? \"geometricPrecision\" : \"crispEdges\"}\n      className=\"block h-auto w-full\"\n    >\n      {clipping ? (\n        <clipPath id={clipId}>\n          <motion.circle\n            cx={box / 2}\n            cy={box / 2}\n            initial={{ r: 0 }}\n            animate={{ r: box * Math.SQRT1_2 }}\n            transition={SPRING_DEFAULT}\n            onAnimationComplete={() => setRevealed(true)}\n          />\n        </clipPath>\n      ) : null}\n      <rect width={box} height={box} fill={LIGHT_MODULE} />\n      <path d={path} fill={DARK_MODULE} fillRule=\"evenodd\" clipPath={clipping ? `url(#${clipId})` : undefined} />\n    </svg>\n  )\n}\n\nfunction QrLogo({ symbol, children }: { symbol: QrSymbol; children: React.ReactNode }) {\n  const share = (logoHole(symbol.size).side / (symbol.size + QUIET_ZONE * 2)) * 100\n  return (\n    <div\n      data-slot=\"qr-code-logo\"\n      aria-hidden=\"true\"\n      style={{ width: `${share}%` }}\n      className=\"absolute top-1/2 left-1/2 grid aspect-square -translate-x-1/2 -translate-y-1/2 place-items-center overflow-hidden text-black *:size-3/4\"\n    >\n      {children}\n    </div>\n  )\n}\n\nfunction QrFrame({ caption, children }: { caption?: React.ReactNode; children: React.ReactNode }) {\n  return (\n    <figure className=\"grid gap-3 rounded-xl border bg-card p-3 text-card-foreground shadow-xs\">\n      {children}\n      {caption ? (\n        <figcaption data-slot=\"qr-code-caption\" className=\"text-center text-[13px] leading-5 text-muted-foreground\">\n          {caption}\n        </figcaption>\n      ) : null}\n    </figure>\n  )\n}\n\nfunction QrCode({ value, variant = \"plain\", effect, level = \"M\", size = DEFAULT_SIZE_PX, label, caption, logo, className, style, ...props }: QrCodeProps) {\n  const resolvedLevel: QrLevel = variant === \"logo\" ? \"H\" : level\n  const { symbol, error } = useMemo(() => tryEncode(value, resolvedLevel), [value, resolvedLevel])\n  const root = { \"data-slot\": \"qr-code\", \"data-variant\": variant, \"data-effect\": effect, style: { ...style, width: size }, ...props }\n  if (!symbol) {\n    return (\n      <div {...root} role=\"alert\" data-state=\"error\" className={cn(\"max-w-full rounded-md border border-dashed p-4 text-[13px] leading-5 text-muted-foreground\", className)}>\n        {error}\n      </div>\n    )\n  }\n  const tile = (\n    <div className=\"relative overflow-hidden rounded-md\">\n      <QrSvg key={value} symbol={symbol} variant={variant} label={label ?? `QR code for ${value}`} reveal={effect === \"reveal\"} />\n      {variant === \"logo\" && logo ? <QrLogo symbol={symbol}>{logo}</QrLogo> : null}\n    </div>\n  )\n  return (\n    <div {...root} className={cn(\"max-w-full\", className)}>\n      {variant === \"framed\" ? <QrFrame caption={caption}>{tile}</QrFrame> : tile}\n    </div>\n  )\n}\n\nexport { QrCode, type QrCodeProps, type QrCodeVariant, type QrCodeEffect, type QrLevel }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/qr-code-encode.ts",
      "content": "import { drawFunctionPatterns, MASKS, placeCodewords, withMask, type QrGrid } from \"@/registry/miseui/ui/qr-code-matrix\"\nimport { maskPenalty } from \"@/registry/miseui/ui/qr-code-penalty\"\nimport { rsGenerator, rsRemainder } from \"@/registry/miseui/ui/qr-code-rs\"\nimport {\n  byteModeCapacity,\n  dataCodewords,\n  EC_BLOCK_COUNT,\n  EC_CODEWORDS_PER_BLOCK,\n  MAX_VERSION,\n  symbolSize,\n  totalCodewords,\n  type QrLevel,\n} from \"@/registry/miseui/ui/qr-code-tables\"\n\nconst BYTE_MODE = 0b0100\nconst PAD_BYTES = [0xec, 0x11]\n\ninterface QrSymbol {\n  version: number\n  level: QrLevel\n  mask: number\n  /** Modules per side, without the quiet zone. */\n  size: number\n  /** Indexed [y][x]; true is a dark module. */\n  modules: QrGrid\n}\n\ninterface QrEncodeOptions {\n  level?: QrLevel\n  /** Fixes the mask instead of scoring all eight. */\n  mask?: number\n  /** Raises the version when a denser grid is needed, for instance to leave room for a logo. */\n  minVersion?: number\n}\n\nclass QrTooLongError extends Error {\n  readonly bytes: number\n  readonly limit: number\n  constructor(bytes: number, limit: number, level: QrLevel) {\n    super(`This text is ${bytes} bytes. A QR code at level ${level} holds ${limit} at most.`)\n    this.name = \"QrTooLongError\"\n    this.bytes = bytes\n    this.limit = limit\n  }\n}\n\nfunction pickVersion(byteLength: number, level: QrLevel, minVersion: number) {\n  for (let version = minVersion; version <= MAX_VERSION; version += 1) {\n    if (byteModeCapacity(version, level) >= byteLength) return version\n  }\n  throw new QrTooLongError(byteLength, byteModeCapacity(MAX_VERSION, level), level)\n}\n\n/** Mode, length, payload, terminator, then padding up to the capacity of this version. */\nfunction dataCodewordsFor(bytes: Uint8Array, version: number, level: QrLevel): number[] {\n  const lengthBits = version <= 9 ? 8 : 16\n  const bits: number[] = []\n  const append = (value: number, count: number) => {\n    for (let bit = count - 1; bit >= 0; bit -= 1) bits.push((value >>> bit) & 1)\n  }\n  append(BYTE_MODE, 4)\n  append(bytes.length, lengthBits)\n  bytes.forEach((byte) => append(byte, 8))\n  const capacity = dataCodewords(version, level)\n  append(0, Math.min(4, capacity * 8 - bits.length))\n  append(0, (8 - (bits.length % 8)) % 8)\n  const filled = Array.from({ length: bits.length / 8 }, (_, index) =>\n    bits.slice(index * 8, index * 8 + 8).reduce((byte, bit) => (byte << 1) | bit, 0),\n  )\n  const padding = Array.from({ length: capacity - filled.length }, (_, index) => PAD_BYTES[index % 2])\n  return [...filled, ...padding]\n}\n\n/** Splits into blocks, appends each block's error correction, then reads the blocks column by column. */\nfunction interleave(data: readonly number[], version: number, level: QrLevel): number[] {\n  const blockCount = EC_BLOCK_COUNT[level][version]\n  const ecLength = EC_CODEWORDS_PER_BLOCK[level][version]\n  const shortCount = blockCount - (totalCodewords(version) % blockCount)\n  const shortLength = Math.floor(totalCodewords(version) / blockCount) - ecLength\n  const generator = rsGenerator(ecLength)\n  const blocks = Array.from({ length: blockCount }, (_, index) => {\n    const start = index * shortLength + Math.max(0, index - shortCount)\n    const length = shortLength + (index < shortCount ? 0 : 1)\n    const body = data.slice(start, start + length)\n    return { body, ec: rsRemainder(body, generator) }\n  })\n  const dataColumns = Array.from({ length: shortLength + 1 }, (_, column) =>\n    blocks.flatMap(({ body }) => (column < body.length ? [body[column]] : [])),\n  )\n  const ecColumns = Array.from({ length: ecLength }, (_, column) => blocks.map(({ ec }) => ec[column]))\n  return [...dataColumns.flat(), ...ecColumns.flat()]\n}\n\nfunction encodeQr(text: string, options: QrEncodeOptions = {}): QrSymbol {\n  const { level = \"M\", mask, minVersion = 1 } = options\n  const bytes = new TextEncoder().encode(text)\n  const version = pickVersion(bytes.length, level, Math.max(1, minVersion))\n  const canvas = drawFunctionPatterns(version)\n  placeCodewords(canvas, interleave(dataCodewordsFor(bytes, version, level), version, level))\n  const candidates = mask === undefined ? MASKS.map((_, index) => index) : [mask]\n  const scored = candidates.map((candidate) => {\n    const { modules } = withMask(canvas, level, candidate)\n    return { mask: candidate, modules, penalty: mask === undefined ? maskPenalty(modules) : 0 }\n  })\n  const best = scored.reduce((winner, entry) => (entry.penalty < winner.penalty ? entry : winner))\n  return { version, level, mask: best.mask, size: symbolSize(version), modules: best.modules }\n}\n\nexport { QrTooLongError, dataCodewordsFor, encodeQr, interleave, type QrEncodeOptions, type QrSymbol }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/qr-code-matrix.ts",
      "content": "import { alignmentPositions, symbolSize, type QrLevel } from \"@/registry/miseui/ui/qr-code-tables\"\n\n/** Indexed [y][x]. The builders fill a local buffer: copying a 177 by 177 grid per module would be absurd. */\ntype QrGrid = boolean[][]\n\nconst FORMAT_LEVEL_BITS: Record<QrLevel, number> = { L: 1, M: 0, Q: 3, H: 2 }\nconst FORMAT_GENERATOR = 0x537\nconst FORMAT_MASK = 0x5412\nconst VERSION_GENERATOR = 0x1f25\n\nconst MASKS: ReadonlyArray<(x: number, y: number) => boolean> = [\n  (x, y) => (x + y) % 2 === 0,\n  (_x, y) => y % 2 === 0,\n  (x) => x % 3 === 0,\n  (x, y) => (x + y) % 3 === 0,\n  (x, y) => (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0,\n  (x, y) => ((x * y) % 2) + ((x * y) % 3) === 0,\n  (x, y) => (((x * y) % 2) + ((x * y) % 3)) % 2 === 0,\n  (x, y) => (((x + y) % 2) + ((x * y) % 3)) % 2 === 0,\n]\n\nfunction bchRemainder(value: number, generator: number, bits: number) {\n  let remainder = value << bits\n  for (let bit = 31 - Math.clz32(remainder); bit >= bits; bit -= 1) {\n    if ((remainder >>> bit) & 1) remainder ^= generator << (bit - bits)\n  }\n  return remainder\n}\n\nfunction formatBits(level: QrLevel, mask: number) {\n  const data = (FORMAT_LEVEL_BITS[level] << 3) | mask\n  return ((data << 10) | bchRemainder(data, FORMAT_GENERATOR, 10)) ^ FORMAT_MASK\n}\n\nfunction versionBits(version: number) {\n  return (version << 12) | bchRemainder(version, VERSION_GENERATOR, 12)\n}\n\nfunction createGrid(size: number): QrGrid {\n  return Array.from({ length: size }, () => Array.from({ length: size }, () => false))\n}\n\ninterface Canvas {\n  modules: QrGrid\n  reserved: QrGrid\n  size: number\n}\n\nfunction setFunctionModule(canvas: Canvas, x: number, y: number, dark: boolean) {\n  canvas.modules[y][x] = dark\n  canvas.reserved[y][x] = true\n}\n\nfunction drawFinder(canvas: Canvas, centreX: number, centreY: number) {\n  for (let dy = -4; dy <= 4; dy += 1) {\n    for (let dx = -4; dx <= 4; dx += 1) {\n      const x = centreX + dx\n      const y = centreY + dy\n      if (x < 0 || y < 0 || x >= canvas.size || y >= canvas.size) continue\n      const ring = Math.max(Math.abs(dx), Math.abs(dy))\n      setFunctionModule(canvas, x, y, ring !== 2 && ring !== 4)\n    }\n  }\n}\n\nfunction drawAlignment(canvas: Canvas, centreX: number, centreY: number) {\n  for (let dy = -2; dy <= 2; dy += 1) {\n    for (let dx = -2; dx <= 2; dx += 1) {\n      setFunctionModule(canvas, centreX + dx, centreY + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1)\n    }\n  }\n}\n\nfunction drawFormat(canvas: Canvas, level: QrLevel, mask: number) {\n  const bits = formatBits(level, mask)\n  const bit = (index: number) => ((bits >>> index) & 1) === 1\n  const { size } = canvas\n  for (let index = 0; index <= 5; index += 1) setFunctionModule(canvas, 8, index, bit(index))\n  setFunctionModule(canvas, 8, 7, bit(6))\n  setFunctionModule(canvas, 8, 8, bit(7))\n  setFunctionModule(canvas, 7, 8, bit(8))\n  for (let index = 9; index < 15; index += 1) setFunctionModule(canvas, 14 - index, 8, bit(index))\n  for (let index = 0; index < 8; index += 1) setFunctionModule(canvas, size - 1 - index, 8, bit(index))\n  for (let index = 8; index < 15; index += 1) setFunctionModule(canvas, 8, size - 15 + index, bit(index))\n  // The always-dark module. It sits in the format strip but carries no bit.\n  setFunctionModule(canvas, 8, size - 8, true)\n}\n\nfunction drawVersion(canvas: Canvas, version: number) {\n  if (version < 7) return\n  const bits = versionBits(version)\n  for (let index = 0; index < 18; index += 1) {\n    const dark = ((bits >>> index) & 1) === 1\n    const long = canvas.size - 11 + (index % 3)\n    const short = Math.floor(index / 3)\n    setFunctionModule(canvas, long, short, dark)\n    setFunctionModule(canvas, short, long, dark)\n  }\n}\n\nfunction drawFunctionPatterns(version: number): Canvas {\n  const size = symbolSize(version)\n  const canvas: Canvas = { modules: createGrid(size), reserved: createGrid(size), size }\n  for (let index = 0; index < size; index += 1) {\n    setFunctionModule(canvas, 6, index, index % 2 === 0)\n    setFunctionModule(canvas, index, 6, index % 2 === 0)\n  }\n  drawFinder(canvas, 3, 3)\n  drawFinder(canvas, size - 4, 3)\n  drawFinder(canvas, 3, size - 4)\n  const positions = alignmentPositions(version)\n  const last = positions.length - 1\n  positions.forEach((y, row) => {\n    positions.forEach((x, column) => {\n      const onFinder = (row === 0 && column === 0) || (row === 0 && column === last) || (row === last && column === 0)\n      if (!onFinder) drawAlignment(canvas, x, y)\n    })\n  })\n  // Placeholder bits: only the reservation matters until a mask has been chosen.\n  drawFormat(canvas, \"L\", 0)\n  drawVersion(canvas, version)\n  return canvas\n}\n\n/** Two columns wide, snaking up and down from the bottom right, stepping over the vertical timing line. */\nfunction placeCodewords(canvas: Canvas, codewords: readonly number[]) {\n  const { size, modules, reserved } = canvas\n  let bitIndex = 0\n  for (let right = size - 1; right >= 1; right -= 2) {\n    if (right === 6) right = 5\n    const upward = ((right + 1) & 2) === 0\n    for (let step = 0; step < size; step += 1) {\n      const y = upward ? size - 1 - step : step\n      for (const x of [right, right - 1]) {\n        if (reserved[y][x]) continue\n        const byte = codewords[bitIndex >>> 3] ?? 0\n        modules[y][x] = ((byte >>> (7 - (bitIndex & 7))) & 1) === 1\n        bitIndex += 1\n      }\n    }\n  }\n}\n\nfunction withMask(canvas: Canvas, level: QrLevel, mask: number): Canvas {\n  const test = MASKS[mask]\n  const modules = canvas.modules.map((row, y) => row.map((dark, x) => (canvas.reserved[y][x] ? dark : dark !== test(x, y))))\n  const masked: Canvas = { ...canvas, modules }\n  drawFormat(masked, level, mask)\n  return masked\n}\n\nexport { MASKS, drawFunctionPatterns, formatBits, placeCodewords, versionBits, withMask, type Canvas, type QrGrid }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/qr-code-penalty.ts",
      "content": "import type { QrGrid } from \"@/registry/miseui/ui/qr-code-matrix\"\n\nconst RUN_WEIGHT = 3\nconst RUN_MINIMUM = 5\nconst BLOCK_WEIGHT = 3\nconst FINDER_LIKE_WEIGHT = 40\nconst BALANCE_WEIGHT = 10\nconst FINDER_LIKE_PATTERNS = [\"10111010000\", \"00001011101\"]\n\nfunction lineString(line: readonly boolean[]) {\n  return line.map((dark) => (dark ? \"1\" : \"0\")).join(\"\")\n}\n\nfunction runPenalty(line: string) {\n  return [...line.matchAll(/0+|1+/g)].reduce((total, [run]) => {\n    return run.length >= RUN_MINIMUM ? total + RUN_WEIGHT + run.length - RUN_MINIMUM : total\n  }, 0)\n}\n\nfunction finderLikePenalty(line: string) {\n  return FINDER_LIKE_PATTERNS.reduce((total, pattern) => {\n    let hits = 0\n    for (let at = line.indexOf(pattern); at !== -1; at = line.indexOf(pattern, at + 1)) hits += 1\n    return total + hits * FINDER_LIKE_WEIGHT\n  }, 0)\n}\n\nfunction blockPenalty(modules: QrGrid) {\n  let total = 0\n  for (let y = 0; y < modules.length - 1; y += 1) {\n    for (let x = 0; x < modules.length - 1; x += 1) {\n      const dark = modules[y][x]\n      if (dark === modules[y][x + 1] && dark === modules[y + 1][x] && dark === modules[y + 1][x + 1]) total += BLOCK_WEIGHT\n    }\n  }\n  return total\n}\n\nfunction balancePenalty(modules: QrGrid) {\n  const total = modules.length * modules.length\n  const dark = modules.reduce((sum, row) => sum + row.filter(Boolean).length, 0)\n  const steps = Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1\n  return Math.max(0, steps) * BALANCE_WEIGHT\n}\n\n/** ISO/IEC 18004 section 7.8.3: the mask with the lowest score is the one a scanner reads most reliably. */\nfunction maskPenalty(modules: QrGrid) {\n  const rows = modules.map(lineString)\n  const columns = modules.map((_, x) => lineString(modules.map((row) => row[x])))\n  const lines = [...rows, ...columns]\n  const perLine = lines.reduce((total, line) => total + runPenalty(line) + finderLikePenalty(line), 0)\n  return perLine + blockPenalty(modules) + balancePenalty(modules)\n}\n\nexport { maskPenalty }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/qr-code-rs.ts",
      "content": "/** The QR field is GF(2^8) reduced by x^8 + x^4 + x^3 + x^2 + 1. */\nconst FIELD_POLYNOMIAL = 0x11d\n\nfunction gfMultiply(a: number, b: number) {\n  let product = 0\n  for (let bit = 7; bit >= 0; bit -= 1) {\n    product = (product << 1) ^ ((product >>> 7) * FIELD_POLYNOMIAL)\n    product ^= ((b >>> bit) & 1) * a\n  }\n  return product\n}\n\n/** Coefficients of (x - 2^0)(x - 2^1)..., highest power first and its leading 1 left out. */\nfunction rsGenerator(degree: number): number[] {\n  const coefficients: number[] = Array.from({ length: degree }, (_, index) => (index === degree - 1 ? 1 : 0))\n  let root = 1\n  for (let round = 0; round < degree; round += 1) {\n    for (let index = 0; index < degree; index += 1) {\n      coefficients[index] = gfMultiply(coefficients[index], root) ^ (coefficients[index + 1] ?? 0)\n    }\n    root = gfMultiply(root, 2)\n  }\n  return coefficients\n}\n\n/** Polynomial long division: what is left over is the error correction block. */\nfunction rsRemainder(data: readonly number[], generator: readonly number[]): number[] {\n  const remainder = generator.map(() => 0)\n  for (const byte of data) {\n    const factor = byte ^ (remainder.shift() ?? 0)\n    remainder.push(0)\n    generator.forEach((coefficient, index) => {\n      remainder[index] ^= gfMultiply(coefficient, factor)\n    })\n  }\n  return remainder\n}\n\nexport { gfMultiply, rsGenerator, rsRemainder }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/qr-code-shapes.ts",
      "content": "import type { QrGrid } from \"@/registry/miseui/ui/qr-code-matrix\"\n\nconst FINDER_SIZE = 7\n/** Share of the side a logo may clear. Above about a quarter of the side, level H starts to lose scans on small versions. */\nconst LOGO_SIDE_SHARE = 0.22\n/** A dot slightly under one module keeps neighbours apart without starving the scanner of dark area. */\nconst DOT_RADIUS = 0.45\nconst FINDER_OUTER_RADIUS = 2\nconst FINDER_INNER_RADIUS = 1.25\nconst FINDER_CORE_RADIUS = 0.75\n\ninterface QrHole {\n  start: number\n  side: number\n}\n\n/** Always odd, so the hole stays centred on a module and the symbol stays symmetrical around it. */\nfunction logoHole(size: number): QrHole {\n  const side = Math.floor(size * LOGO_SIDE_SHARE) | 1\n  return { start: (size - side) / 2, side }\n}\n\nfunction inHole(x: number, y: number, hole: QrHole | undefined) {\n  if (!hole) return false\n  return x >= hole.start && x < hole.start + hole.side && y >= hole.start && y < hole.start + hole.side\n}\n\nfunction isFinderModule(x: number, y: number, size: number) {\n  const left = x < FINDER_SIZE\n  const top = y < FINDER_SIZE\n  const right = x >= size - FINDER_SIZE\n  const bottom = y >= size - FINDER_SIZE\n  return (left && top) || (right && top) || (left && bottom)\n}\n\nfunction run(y: number, from: number, to: number, offset: number) {\n  return `M${from + offset} ${y + offset}h${to - from}v1h${from - to}z`\n}\n\n/** Joins horizontal runs into one path, so antialiasing cannot draw hairlines between neighbouring modules. */\nfunction squarePath(modules: QrGrid, offset: number, hole?: QrHole) {\n  return modules\n    .map((row, y) => {\n      const parts: string[] = []\n      let start = -1\n      for (let x = 0; x <= row.length; x += 1) {\n        const dark = x < row.length && row[x] && !inHole(x, y, hole)\n        if (dark && start === -1) start = x\n        if (!dark && start !== -1) {\n          parts.push(run(y, start, x, offset))\n          start = -1\n        }\n      }\n      return parts.join(\"\")\n    })\n    .join(\"\")\n}\n\nfunction circle(centreX: number, centreY: number, radius: number) {\n  return `M${centreX - radius} ${centreY}a${radius} ${radius} 0 1 0 ${radius * 2} 0a${radius} ${radius} 0 1 0 ${-radius * 2} 0z`\n}\n\nfunction roundedRect(x: number, y: number, side: number, radius: number) {\n  const straight = side - radius * 2\n  const arc = (dx: number, dy: number) => `a${radius} ${radius} 0 0 1 ${dx} ${dy}`\n  return [\n    `M${x + radius} ${y}h${straight}`,\n    arc(radius, radius),\n    `v${straight}`,\n    arc(-radius, radius),\n    `h${-straight}`,\n    arc(-radius, -radius),\n    `v${-straight}`,\n    arc(radius, -radius),\n    \"z\",\n  ].join(\"\")\n}\n\n/** Scanners locate the symbol by these rings, so they stay solid while the data around them turns to dots. */\nfunction finderPath(size: number, offset: number) {\n  const corners = [\n    [0, 0],\n    [size - FINDER_SIZE, 0],\n    [0, size - FINDER_SIZE],\n  ]\n  return corners\n    .map(([x, y]) =>\n      [\n        roundedRect(x + offset, y + offset, FINDER_SIZE, FINDER_OUTER_RADIUS),\n        roundedRect(x + offset + 1, y + offset + 1, FINDER_SIZE - 2, FINDER_INNER_RADIUS),\n        roundedRect(x + offset + 2, y + offset + 2, FINDER_SIZE - 4, FINDER_CORE_RADIUS),\n      ].join(\"\"),\n    )\n    .join(\"\")\n}\n\nfunction dotPath(modules: QrGrid, offset: number) {\n  const size = modules.length\n  return modules\n    .flatMap((row, y) =>\n      row.flatMap((dark, x) => (dark && !isFinderModule(x, y, size) ? [circle(x + offset + 0.5, y + offset + 0.5, DOT_RADIUS)] : [])),\n    )\n    .join(\"\")\n}\n\nexport { DOT_RADIUS, dotPath, finderPath, inHole, isFinderModule, logoHole, squarePath, type QrHole }\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/qr-code-tables.ts",
      "content": "type QrLevel = \"L\" | \"M\" | \"Q\" | \"H\"\n\n/** Index 0 is unused so a version number can index these directly. Values are from ISO/IEC 18004 table 9. */\nconst EC_CODEWORDS_PER_BLOCK: Record<QrLevel, readonly number[]> = {\n  L: [0, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],\n  M: [0, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28],\n  Q: [0, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],\n  H: [0, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],\n}\n\nconst EC_BLOCK_COUNT: Record<QrLevel, readonly number[]> = {\n  L: [0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25],\n  M: [0, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49],\n  Q: [0, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68],\n  H: [0, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81],\n}\n\nconst MAX_VERSION = 40\n\nfunction symbolSize(version: number) {\n  return version * 4 + 17\n}\n\n/** Modules left for data and error correction once the function patterns are drawn, remainder bits included. */\nfunction rawDataModules(version: number) {\n  const base = (16 * version + 128) * version + 64\n  if (version < 2) return base\n  const alignCount = Math.floor(version / 7) + 2\n  const withoutAlignment = base - ((25 * alignCount - 10) * alignCount - 55)\n  return version >= 7 ? withoutAlignment - 36 : withoutAlignment\n}\n\nfunction totalCodewords(version: number) {\n  return Math.floor(rawDataModules(version) / 8)\n}\n\nfunction dataCodewords(version: number, level: QrLevel) {\n  return totalCodewords(version) - EC_CODEWORDS_PER_BLOCK[level][version] * EC_BLOCK_COUNT[level][version]\n}\n\n/** Byte mode costs a 4 bit mode and an 8 or 16 bit length, which is what makes the limit depend on the version. */\nfunction byteModeCapacity(version: number, level: QrLevel) {\n  const lengthBits = version <= 9 ? 8 : 16\n  return Math.floor((dataCodewords(version, level) * 8 - 4 - lengthBits) / 8)\n}\n\n/** Centre coordinates of the alignment patterns on both axes. */\nfunction alignmentPositions(version: number): number[] {\n  if (version === 1) return []\n  const count = Math.floor(version / 7) + 2\n  const last = symbolSize(version) - 7\n  // Version 32 is the one irregular spacing in the standard.\n  const step = version === 32 ? 26 : Math.ceil((last - 6) / (count - 1) / 2) * 2\n  return [6, ...Array.from({ length: count - 1 }, (_, index) => last - (count - 2 - index) * step)]\n}\n\nexport {\n  EC_BLOCK_COUNT,\n  EC_CODEWORDS_PER_BLOCK,\n  MAX_VERSION,\n  alignmentPositions,\n  byteModeCapacity,\n  dataCodewords,\n  symbolSize,\n  totalCodewords,\n  type QrLevel,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "display"
  ],
  "type": "registry:ui"
}