{
  "name": "QRCode",
  "category": "Data Display",
  "subcategory": "Status & Indicators",
  "description": "Renders a QR code SVG for a string payload.",
  "thumbnail": "/preview-cards/qr-code.png",
  "hasDocsPage": false,
  "nugetPackage": "Lumeo",
  "files": [
    "UI/QRCode/QRCode.razor",
    "UI/QRCode/QRCodeEncoder.cs"
  ],
  "dependencies": [],
  "packageDependencies": [],
  "cssVars": [
    "--radius"
  ],
  "api": {
    "name": "QRCode",
    "category": "Data Display",
    "subcategory": "Status & Indicators",
    "description": "Renders a QR code SVG for a string payload.",
    "nugetPackage": "Lumeo",
    "files": [
      "UI/QRCode/QRCode.razor",
      "UI/QRCode/QRCodeEncoder.cs"
    ],
    "namespace": "Lumeo",
    "inheritsFrom": null,
    "implements": [],
    "parameters": [
      {
        "name": "Value",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Size",
        "type": "int",
        "default": "200",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ErrorCorrectionLevel",
        "type": "QRErrorLevel",
        "default": "QRErrorLevel.Medium",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ForegroundColor",
        "type": "string",
        "default": "\"currentColor\"",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "BackgroundColor",
        "type": "string",
        "default": "\"transparent\"",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "IncludeMargin",
        "type": "bool",
        "default": "true",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ImageSrc",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "ImageSize",
        "type": "int",
        "default": "40",
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "Class",
        "type": "string?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": false
      },
      {
        "name": "AdditionalAttributes",
        "type": "Dictionary<string, object>?",
        "default": null,
        "description": null,
        "isCascading": false,
        "captureUnmatched": true
      }
    ],
    "events": [],
    "enums": [
      {
        "name": "QRErrorLevel",
        "values": [
          "Low",
          "Medium",
          "Quartile",
          "High"
        ],
        "description": null
      }
    ],
    "records": [],
    "cssVars": [
      "--radius"
    ],
    "examples": [],
    "subComponents": {},
    "parseFailed": false,
    "parseError": null
  },
  "$schema": "https://lumeo.nativ.sh/registry-component-schema.json",
  "key": "qr-code",
  "docsUrl": "https://lumeo.nativ.sh/components/qr-code",
  "sourceUrl": [
    {
      "file": "UI/QRCode/QRCode.razor",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/QRCode/QRCode.razor"
    },
    {
      "file": "UI/QRCode/QRCodeEncoder.cs",
      "url": "https://github.com/Brain2k-0005/Lumeo/tree/master/src/Lumeo/UI/QRCode/QRCodeEncoder.cs"
    }
  ],
  "source": [
    {
      "path": "UI/QRCode/QRCode.razor",
      "content": "@namespace Lumeo\n\n<div class=\"@ContainerClass\" style=\"@ContainerStyle\" @attributes=\"AdditionalAttributes\">\n    <svg viewBox=\"@ViewBox\" xmlns=\"http://www.w3.org/2000/svg\" style=\"display: block; width: 100%; height: 100%;\">\n        <rect x=\"0\" y=\"0\" width=\"@_totalSize\" height=\"@_totalSize\" fill=\"@BackgroundColor\" />\n        @if (_darkModules is not null)\n        {\n            @foreach (var (x, y) in _darkModules)\n            {\n                <rect x=\"@x\" y=\"@y\" width=\"1\" height=\"1\" fill=\"@ForegroundColor\" />\n            }\n        }\n        @if (!string.IsNullOrEmpty(ImageSrc) && _moduleCount > 0)\n        {\n            int center = _totalSize / 2;\n            int imgX = center - ImageSize / 2;\n            int imgY = center - ImageSize / 2;\n            <image href=\"@ImageSrc\" x=\"@imgX\" y=\"@imgY\" width=\"@ImageSize\" height=\"@ImageSize\"\n                   preserveAspectRatio=\"xMidYMid meet\" style=\"border-radius: 4px;\" />\n        }\n    </svg>\n</div>\n\n@code {\n    [Parameter] public string? Value { get; set; }\n    [Parameter] public int Size { get; set; } = 200;\n    [Parameter] public QRErrorLevel ErrorCorrectionLevel { get; set; } = QRErrorLevel.Medium;\n    [Parameter] public string ForegroundColor { get; set; } = \"currentColor\";\n    [Parameter] public string BackgroundColor { get; set; } = \"transparent\";\n    [Parameter] public bool IncludeMargin { get; set; } = true;\n    [Parameter] public string? ImageSrc { get; set; }\n    [Parameter] public int ImageSize { get; set; } = 40;\n    [Parameter] public string? Class { get; set; }\n    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }\n\n    private IReadOnlyList<(int x, int y)>? _darkModules;\n    private int _moduleCount;\n    private int _margin;\n    private int _totalSize;\n\n    private string ContainerClass => $\"inline-block overflow-hidden rounded-xl {Class}\".Trim();\n    private string ContainerStyle => $\"width: {Size}px; height: {Size}px;\";\n    private string ViewBox => $\"0 0 {_totalSize} {_totalSize}\";\n\n    protected override void OnParametersSet() => Rebuild();\n\n    private void Rebuild()\n    {\n        if (string.IsNullOrEmpty(Value))\n        {\n            _darkModules = null;\n            _moduleCount = 0;\n            _margin = 0;\n            _totalSize = 0;\n            return;\n        }\n\n        try\n        {\n            var ecl = ErrorCorrectionLevel switch\n            {\n                QRErrorLevel.Low => QRCoder.QRCodeGenerator.ECCLevel.L,\n                QRErrorLevel.Quartile => QRCoder.QRCodeGenerator.ECCLevel.Q,\n                QRErrorLevel.High => QRCoder.QRCodeGenerator.ECCLevel.H,\n                _ => QRCoder.QRCodeGenerator.ECCLevel.M,\n            };\n\n            using var gen = new QRCoder.QRCodeGenerator();\n            using var data = gen.CreateQrCode(Value, ecl);\n            var pixels = data.ModuleMatrix;\n            _moduleCount = pixels.Count;\n            _margin = IncludeMargin ? 4 : 0;\n            _totalSize = _moduleCount + _margin * 2;\n\n            var modules = new List<(int x, int y)>();\n            for (int row = 0; row < _moduleCount; row++)\n            for (int col = 0; col < _moduleCount; col++)\n                if (pixels[row][col])\n                    modules.Add((col + _margin, row + _margin));\n\n            _darkModules = modules;\n        }\n        catch\n        {\n            _darkModules = null;\n            _moduleCount = 0;\n            _margin = 0;\n            _totalSize = 0;\n        }\n    }\n\n    public enum QRErrorLevel { Low, Medium, Quartile, High }\n}\n"
    },
    {
      "path": "UI/QRCode/QRCodeEncoder.cs",
      "content": "using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace Lumeo;\n\n/// <summary>\n/// Pure C# QR code encoder. Supports byte mode, versions 1–10, all EC levels, all 8 masks.\n/// </summary>\ninternal static class QRCodeEncoder\n{\n    // ── Error Correction Level ───────────────────────────────────────────────\n\n    public enum ErrorCorrectionLevel { Low = 0, Medium = 1, Quartile = 2, High = 3 }\n\n    // ── Public entry point ───────────────────────────────────────────────────\n\n    /// <summary>\n    /// Encodes <paramref name=\"text\"/> and returns a bool[,] matrix where true = dark module.\n    /// Quiet zone (4 modules) is NOT included; add it in the caller if needed.\n    /// </summary>\n    public static bool[,] Encode(string text, ErrorCorrectionLevel ecl)\n    {\n        var data = Encoding.UTF8.GetBytes(text);\n        int version = ChooseVersion(data.Length, ecl);\n        var codewords = BuildCodewords(data, version, ecl);\n        var ecCodewords = AddErrorCorrection(codewords, version, ecl);\n        var matrix = BuildMatrix(ecCodewords, version, ecl);\n        return matrix;\n    }\n\n    // ── Version selection ────────────────────────────────────────────────────\n\n    // Max data bytes per version/EC level (versions 1–10, EC Low/Med/Qua/High)\n    private static readonly int[,] DataCapacity =\n    {\n        //  L    M    Q    H\n        {  17,  14,  11,   7 }, // v1\n        {  32,  26,  20,  14 }, // v2\n        {  53,  42,  32,  24 }, // v3\n        {  78,  62,  46,  34 }, // v4\n        { 106,  84,  60,  44 }, // v5\n        { 134, 106,  74,  58 }, // v6\n        { 154, 122,  86,  64 }, // v7\n        { 192, 152, 108,  84 }, // v8\n        { 230, 180, 130, 98  }, // v9\n        { 271, 213, 151, 119 }, // v10\n    };\n\n    private static int ChooseVersion(int byteLength, ErrorCorrectionLevel ecl)\n    {\n        int col = (int)ecl;\n        for (int v = 0; v < DataCapacity.GetLength(0); v++)\n            if (byteLength <= DataCapacity[v, col])\n                return v + 1;\n        throw new InvalidOperationException($\"Data too long ({byteLength} bytes) for versions 1–10.\");\n    }\n\n    // ── Codeword building ────────────────────────────────────────────────────\n\n    // Total codewords per version (1–10)\n    private static readonly int[] TotalCodewords = { 26, 44, 70, 100, 134, 172, 196, 242, 292, 346 };\n\n    // EC codewords per block per version/EC level\n    private static readonly int[,] EcPerBlock =\n    {\n        //  L   M   Q   H\n        {  7,  10, 13, 17 }, // v1\n        { 10,  16, 22, 28 }, // v2\n        { 15,  26, 18, 22 }, // v3\n        { 20,  18, 26, 16 }, // v4\n        { 26,  24, 18, 22 }, // v5\n        { 18,  16, 24, 28 }, // v6\n        { 20,  18, 18, 26 }, // v7\n        { 24,  22, 22, 26 }, // v8\n        { 30,  22, 20, 24 }, // v9\n        { 18,  26, 24, 28 }, // v10\n    };\n\n    // Number of EC blocks per version/EC level\n    private static readonly int[,] NumBlocks =\n    {\n        //  L  M  Q  H\n        {  1,  1,  1,  1 }, // v1\n        {  1,  1,  1,  1 }, // v2\n        {  1,  1,  2,  2 }, // v3\n        {  1,  2,  2,  4 }, // v4\n        {  1,  2,  4,  2 }, // v5 — note: some blocks may have +1 data codeword\n        {  2,  4,  4,  4 }, // v6\n        {  2,  4,  2,  4 }, // v7\n        {  2,  2,  4,  4 }, // v8\n        {  2,  3,  4,  4 }, // v9\n        {  4,  4,  6,  6 }, // v10\n    };\n\n    private static byte[] BuildCodewords(byte[] data, int version, ErrorCorrectionLevel ecl)\n    {\n        int totalCw = TotalCodewords[version - 1];\n        int ecCwPerBlock = EcPerBlock[version - 1, (int)ecl];\n        int blocks = NumBlocks[version - 1, (int)ecl];\n        int totalEcCw = ecCwPerBlock * blocks;\n        int dataCw = totalCw - totalEcCw;\n\n        // Bit buffer\n        var bits = new List<bool>();\n\n        // Mode indicator: 0100 = byte mode\n        AppendBits(bits, 0b0100, 4);\n\n        // Character count indicator\n        int ccBits = version <= 9 ? 8 : 16;\n        AppendBits(bits, data.Length, ccBits);\n\n        // Data bytes\n        foreach (byte b in data)\n            AppendBits(bits, b, 8);\n\n        // Terminator\n        int remaining = dataCw * 8 - bits.Count;\n        for (int i = 0; i < Math.Min(4, remaining); i++) bits.Add(false);\n\n        // Byte-align\n        while (bits.Count % 8 != 0) bits.Add(false);\n\n        // Padding codewords\n        bool toggle = true;\n        while (bits.Count < dataCw * 8)\n        {\n            AppendBits(bits, toggle ? 0xEC : 0x11, 8);\n            toggle = !toggle;\n        }\n\n        // Convert to bytes\n        var result = new byte[dataCw];\n        for (int i = 0; i < dataCw; i++)\n        {\n            int val = 0;\n            for (int b = 0; b < 8; b++)\n                if (bits[i * 8 + b]) val |= 1 << (7 - b);\n            result[i] = (byte)val;\n        }\n        return result;\n    }\n\n    private static void AppendBits(List<bool> bits, int value, int length)\n    {\n        for (int i = length - 1; i >= 0; i--)\n            bits.Add(((value >> i) & 1) == 1);\n    }\n\n    // ── Reed-Solomon error correction ────────────────────────────────────────\n\n    private static byte[] AddErrorCorrection(byte[] data, int version, ErrorCorrectionLevel ecl)\n    {\n        int ecCwPerBlock = EcPerBlock[version - 1, (int)ecl];\n        int blocks = NumBlocks[version - 1, (int)ecl];\n        int totalCw = TotalCodewords[version - 1];\n        int totalEcCw = ecCwPerBlock * blocks;\n        int totalDataCw = totalCw - totalEcCw;\n\n        // Split data into blocks\n        int shortBlockLen = totalDataCw / blocks;\n        int numLongBlocks = totalDataCw % blocks;\n\n        var dataBlocks = new byte[blocks][];\n        int idx = 0;\n        for (int b = 0; b < blocks; b++)\n        {\n            int len = shortBlockLen + (b >= blocks - numLongBlocks ? 1 : 0);\n            dataBlocks[b] = new byte[len];\n            Array.Copy(data, idx, dataBlocks[b], 0, len);\n            idx += len;\n        }\n\n        // Compute EC for each block\n        var ecBlocks = new byte[blocks][];\n        for (int b = 0; b < blocks; b++)\n            ecBlocks[b] = ReedSolomon(dataBlocks[b], ecCwPerBlock);\n\n        // Interleave data codewords\n        var result = new byte[totalCw];\n        int ri = 0;\n        for (int col = 0; col < shortBlockLen + (numLongBlocks > 0 ? 1 : 0); col++)\n            for (int b = 0; b < blocks; b++)\n                if (col < dataBlocks[b].Length)\n                    result[ri++] = dataBlocks[b][col];\n\n        // Interleave EC codewords\n        for (int col = 0; col < ecCwPerBlock; col++)\n            for (int b = 0; b < blocks; b++)\n                result[ri++] = ecBlocks[b][col];\n\n        return result;\n    }\n\n    // GF(256) tables\n    private static readonly byte[] GfExp = new byte[512];\n    private static readonly byte[] GfLog = new byte[256];\n\n    static QRCodeEncoder()\n    {\n        int x = 1;\n        for (int i = 0; i < 255; i++)\n        {\n            GfExp[i] = (byte)x;\n            GfLog[x] = (byte)i;\n            x <<= 1;\n            if ((x & 0x100) != 0) x ^= 0x11D;\n        }\n        for (int i = 255; i < 512; i++)\n            GfExp[i] = GfExp[i - 255];\n    }\n\n    private static byte GfMul(byte a, byte b)\n    {\n        if (a == 0 || b == 0) return 0;\n        return GfExp[GfLog[a] + GfLog[b]];\n    }\n\n    private static byte[] RsGeneratorPoly(int degree)\n    {\n        var poly = new byte[degree + 1];\n        poly[degree] = 1;\n        for (int i = 0; i < degree; i++)\n        {\n            byte alpha = GfExp[i];\n            for (int j = 0; j < degree; j++)\n                poly[j] = (byte)(GfMul(poly[j], alpha) ^ poly[j + 1]);\n            poly[degree] = GfMul(poly[degree], alpha);\n        }\n        return poly;\n    }\n\n    private static byte[] ReedSolomon(byte[] data, int ecLen)\n    {\n        var gen = RsGeneratorPoly(ecLen);\n        var remainder = new byte[ecLen];\n        foreach (byte b in data)\n        {\n            byte coeff = (byte)(b ^ remainder[0]);\n            Array.Copy(remainder, 1, remainder, 0, ecLen - 1);\n            remainder[ecLen - 1] = 0;\n            if (coeff != 0)\n                for (int i = 0; i < ecLen; i++)\n                    remainder[i] ^= GfMul(gen[i], coeff);\n        }\n        return remainder;\n    }\n\n    // ── Matrix building ──────────────────────────────────────────────────────\n\n    private static bool[,] BuildMatrix(byte[] codewords, int version, ErrorCorrectionLevel ecl)\n    {\n        int size = version * 4 + 17;\n        var matrix = new bool[size, size];\n        var reserved = new bool[size, size]; // modules that are non-data\n\n        PlaceFinders(matrix, reserved, size);\n        PlaceTimings(matrix, reserved, size);\n        PlaceAlignments(matrix, reserved, version);\n        PlaceDarkModule(matrix, reserved, version);\n        ReserveFormatAreas(reserved, size);\n\n        // Place data bits with best mask\n        int bestMask = -1;\n        int bestPenalty = int.MaxValue;\n        bool[,]? bestMatrix = null;\n\n        for (int mask = 0; mask < 8; mask++)\n        {\n            var m = (bool[,])matrix.Clone();\n            PlaceData(m, reserved, codewords, size);\n            ApplyMask(m, reserved, mask, size);\n            PlaceFormatInfo(m, ecl, mask, size);\n            int penalty = ComputePenalty(m, size);\n            if (penalty < bestPenalty)\n            {\n                bestPenalty = penalty;\n                bestMask = mask;\n                bestMatrix = (bool[,])m.Clone();\n            }\n        }\n\n        return bestMatrix!;\n    }\n\n    // ── Finder patterns ──────────────────────────────────────────────────────\n\n    private static void PlaceFinders(bool[,] m, bool[,] r, int size)\n    {\n        // Top-left, top-right, bottom-left\n        PlaceFinder(m, r, 0, 0);\n        PlaceFinder(m, r, 0, size - 7);\n        PlaceFinder(m, r, size - 7, 0);\n\n        // Separators\n        for (int i = 0; i <= 7; i++)\n        {\n            SetReserved(m, r, 7, i, false);\n            SetReserved(m, r, i, 7, false);\n            SetReserved(m, r, 7, size - 8 + i, false);\n            SetReserved(m, r, i, size - 8, false);\n            SetReserved(m, r, size - 8, i, false);\n            SetReserved(m, r, size - 8 + i, 7, false);\n        }\n    }\n\n    private static void PlaceFinder(bool[,] m, bool[,] r, int row, int col)\n    {\n        for (int dr = 0; dr < 7; dr++)\n            for (int dc = 0; dc < 7; dc++)\n            {\n                bool dark = dr == 0 || dr == 6 || dc == 0 || dc == 6\n                            || (dr >= 2 && dr <= 4 && dc >= 2 && dc <= 4);\n                SetReserved(m, r, row + dr, col + dc, dark);\n            }\n    }\n\n    private static void SetReserved(bool[,] m, bool[,] r, int row, int col, bool dark)\n    {\n        m[row, col] = dark;\n        r[row, col] = true;\n    }\n\n    // ── Timing patterns ──────────────────────────────────────────────────────\n\n    private static void PlaceTimings(bool[,] m, bool[,] r, int size)\n    {\n        for (int i = 8; i < size - 8; i++)\n        {\n            bool dark = i % 2 == 0;\n            SetReserved(m, r, 6, i, dark);\n            SetReserved(m, r, i, 6, dark);\n        }\n    }\n\n    // ── Alignment patterns ───────────────────────────────────────────────────\n\n    // Centers for versions 1–10 (version 1 has none)\n    private static readonly int[][] AlignmentPositions =\n    {\n        Array.Empty<int>(), // v1\n        new[] { 6, 18 }, // v2\n        new[] { 6, 22 }, // v3\n        new[] { 6, 26 }, // v4\n        new[] { 6, 30 }, // v5\n        new[] { 6, 34 }, // v6\n        new[] { 6, 22, 38 }, // v7\n        new[] { 6, 24, 42 }, // v8\n        new[] { 6, 26, 46 }, // v9\n        new[] { 6, 28, 50 }, // v10\n    };\n\n    private static void PlaceAlignments(bool[,] m, bool[,] r, int version)\n    {\n        var positions = AlignmentPositions[version - 1];\n        foreach (int row in positions)\n            foreach (int col in positions)\n            {\n                if (r[row, col]) continue; // overlaps finder\n                for (int dr = -2; dr <= 2; dr++)\n                    for (int dc = -2; dc <= 2; dc++)\n                    {\n                        bool dark = dr == -2 || dr == 2 || dc == -2 || dc == 2 || (dr == 0 && dc == 0);\n                        SetReserved(m, r, row + dr, col + dc, dark);\n                    }\n            }\n    }\n\n    // ── Dark module ──────────────────────────────────────────────────────────\n\n    private static void PlaceDarkModule(bool[,] m, bool[,] r, int version)\n    {\n        SetReserved(m, r, 4 * version + 9, 8, true);\n    }\n\n    // ── Format information areas ─────────────────────────────────────────────\n\n    private static void ReserveFormatAreas(bool[,] r, int size)\n    {\n        // Top-left horizontal + vertical\n        for (int i = 0; i <= 8; i++)\n        {\n            r[8, i] = true;\n            r[i, 8] = true;\n        }\n        // Top-right\n        for (int i = size - 8; i < size; i++) r[8, i] = true;\n        // Bottom-left\n        for (int i = size - 7; i < size; i++) r[i, 8] = true;\n    }\n\n    // Format info bits lookup (ECL × 3 bits + mask × 3 bits, precomputed)\n    private static int FormatInfo(ErrorCorrectionLevel ecl, int mask)\n    {\n        // ECL encoding: L=01, M=00, Q=11, H=10\n        int[] eclBits = { 0b01, 0b00, 0b11, 0b10 };\n        int data = (eclBits[(int)ecl] << 3) | mask;\n        // BCH error correction for format\n        int remainder = data;\n        for (int i = 0; i < 10; i++)\n            if ((remainder & (1 << (9 - i))) != 0)\n                remainder ^= (0x537 << (3 - i));\n        // Actually compute correctly: generator = 10100110111\n        int gen = 0b10100110111;\n        remainder = data << 10;\n        for (int i = 14; i >= 10; i--)\n            if ((remainder & (1 << i)) != 0)\n                remainder ^= gen << (i - 10);\n        int formatBits = ((data << 10) | remainder) ^ 0b101010000010010;\n        return formatBits;\n    }\n\n    private static void PlaceFormatInfo(bool[,] m, ErrorCorrectionLevel ecl, int mask, int size)\n    {\n        int bits = FormatInfo(ecl, mask);\n\n        // Top-left horizontal (col 0–5, skip 6 timing, col 7–8)\n        int[] hPos = { 0, 1, 2, 3, 4, 5, 7, 8 };\n        for (int i = 0; i < 8; i++)\n            m[8, hPos[i]] = ((bits >> (14 - i)) & 1) == 1;\n\n        // Top-left vertical (row 8–7, skip 6, row 5–0)\n        int[] vPos = { 7, 5, 4, 3, 2, 1, 0 };\n        m[8, 8] = ((bits >> 7) & 1) == 1;\n        for (int i = 0; i < 7; i++)\n            m[vPos[i], 8] = ((bits >> (6 - i)) & 1) == 1;\n\n        // Top-right\n        for (int i = 0; i < 8; i++)\n            m[8, size - 1 - i] = ((bits >> i) & 1) == 1;\n\n        // Bottom-left\n        for (int i = 0; i < 7; i++)\n            m[size - 7 + i, 8] = ((bits >> (14 - i)) & 1) == 1;\n    }\n\n    // ── Data placement ───────────────────────────────────────────────────────\n\n    private static void PlaceData(bool[,] m, bool[,] r, byte[] codewords, int size)\n    {\n        int bitIdx = 0;\n        int totalBits = codewords.Length * 8;\n\n        // Zigzag columns from right to left, skipping column 6 (timing)\n        for (int right = size - 1; right >= 1; right -= 2)\n        {\n            if (right == 6) right = 5; // skip timing column\n            bool upward = ((size - 1 - right) / 2) % 2 == 0;\n\n            for (int vert = 0; vert < size; vert++)\n            {\n                int row = upward ? size - 1 - vert : vert;\n                for (int dc = 0; dc < 2; dc++)\n                {\n                    int col = right - dc;\n                    if (r[row, col]) continue;\n                    if (bitIdx < totalBits)\n                    {\n                        int byteIdx = bitIdx / 8;\n                        int bitPos = 7 - (bitIdx % 8);\n                        m[row, col] = ((codewords[byteIdx] >> bitPos) & 1) == 1;\n                        bitIdx++;\n                    }\n                    else\n                    {\n                        m[row, col] = false;\n                    }\n                }\n            }\n        }\n    }\n\n    // ── Masking ──────────────────────────────────────────────────────────────\n\n    private static void ApplyMask(bool[,] m, bool[,] r, int mask, int size)\n    {\n        for (int row = 0; row < size; row++)\n            for (int col = 0; col < size; col++)\n            {\n                if (r[row, col]) continue;\n                bool flip = mask switch\n                {\n                    0 => (row + col) % 2 == 0,\n                    1 => row % 2 == 0,\n                    2 => col % 3 == 0,\n                    3 => (row + col) % 3 == 0,\n                    4 => (row / 2 + col / 3) % 2 == 0,\n                    5 => (row * col) % 2 + (row * col) % 3 == 0,\n                    6 => ((row * col) % 2 + (row * col) % 3) % 2 == 0,\n                    7 => ((row + col) % 2 + (row * col) % 3) % 2 == 0,\n                    _ => false\n                };\n                if (flip) m[row, col] = !m[row, col];\n            }\n    }\n\n    // ── Penalty scoring ──────────────────────────────────────────────────────\n\n    private static int ComputePenalty(bool[,] m, int size)\n    {\n        int penalty = 0;\n\n        // Rule 1: 5+ consecutive same-color modules in a row or column\n        for (int r = 0; r < size; r++)\n        {\n            int runH = 1, runV = 1;\n            for (int c = 1; c < size; c++)\n            {\n                if (m[r, c] == m[r, c - 1]) { runH++; if (runH == 5) penalty += 3; else if (runH > 5) penalty++; }\n                else runH = 1;\n                if (m[c, r] == m[c - 1, r]) { runV++; if (runV == 5) penalty += 3; else if (runV > 5) penalty++; }\n                else runV = 1;\n            }\n        }\n\n        // Rule 2: 2x2 blocks\n        for (int r = 0; r < size - 1; r++)\n            for (int c = 0; c < size - 1; c++)\n                if (m[r, c] == m[r, c + 1] && m[r, c] == m[r + 1, c] && m[r, c] == m[r + 1, c + 1])\n                    penalty += 3;\n\n        // Rule 3: finder-like patterns\n        int[] pattern1 = { 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0 };\n        int[] pattern2 = { 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1 };\n        for (int r = 0; r < size; r++)\n            for (int c = 0; c + 10 < size; c++)\n            {\n                bool h1 = true, h2 = true, v1 = true, v2 = true;\n                for (int i = 0; i <= 10; i++)\n                {\n                    bool hm = m[r, c + i], vm = m[c + i, r];\n                    if (hm != (pattern1[i] == 1)) h1 = false;\n                    if (hm != (pattern2[i] == 1)) h2 = false;\n                    if (vm != (pattern1[i] == 1)) v1 = false;\n                    if (vm != (pattern2[i] == 1)) v2 = false;\n                }\n                if (h1 || h2) penalty += 40;\n                if (v1 || v2) penalty += 40;\n            }\n\n        // Rule 4: dark proportion\n        int dark = 0;\n        for (int r = 0; r < size; r++)\n            for (int c = 0; c < size; c++)\n                if (m[r, c]) dark++;\n        int total = size * size;\n        int pct = dark * 100 / total;\n        int prev5 = (pct / 5) * 5;\n        int next5 = prev5 + 5;\n        penalty += Math.Min(Math.Abs(prev5 - 50) / 5, Math.Abs(next5 - 50) / 5) * 10;\n\n        return penalty;\n    }\n}\n"
    }
  ],
  "slots": [],
  "serviceDependencies": [],
  "cascadingDependencies": [],
  "relatedComponents": [],
  "keyboardInteractions": [],
  "tests": [
    "tests/Lumeo.Docs.Tests/Catalog/CatalogCardTests.cs",
    "tests/Lumeo.Tests/Components/QRCode/QRCodeTests.cs"
  ],
  "mdSummary": "# QRCode\n\nRenders a QR code SVG for a string payload.\n\n**Package:** `Lumeo` · **Category:** Data Display/Status & Indicators\n**Docs:** https://lumeo.nativ.sh/components/qr-code\n\n## Parameters\n| Owner | Name | Type | Default | Description |\n|---|---|---|---|---|\n| QRCode | Value | `string?` | `` |  |\n| QRCode | Size | `int` | `200` |  |\n| QRCode | ErrorCorrectionLevel | `QRErrorLevel` | `QRErrorLevel.Medium` |  |\n| QRCode | ForegroundColor | `string` | `\"currentColor\"` |  |\n| QRCode | BackgroundColor | `string` | `\"transparent\"` |  |\n| QRCode | IncludeMargin | `bool` | `true` |  |\n| QRCode | ImageSrc | `string?` | `` |  |\n| QRCode | ImageSize | `int` | `40` |  |\n| QRCode | Class | `string?` | `` |  |\n| QRCode | AdditionalAttributes | `Dictionary<string, object>?` | `` |  |\n\n## Slots\n- (none)\n\n## Events\n- (none)\n\n## Sub-components\n- (none)\n\n## Cascading values it expects\n- (none)\n\n## Services it injects\n- (none)\n\n## Keyboard\n—\n\n## Example\n```razor\n<QRCode />\n```\n\n## CSS variables\n`--radius`\n\n## Files\n- `UI/QRCode/QRCode.razor`\n- `UI/QRCode/QRCodeEncoder.cs`\n"
}