seveibar/keyboard-utils

Based on the code provided, here's a concise summary of the electronic component: A keyboard utility library for parsing and generating keyboard layouts using the Keyboard Layout Editor (KLE) JSON format, with support for generating key matrices, reference designators, and diode routing for keyboard PCBs. Key features: - Parses KLE JSON layouts into key coordinate and metadata objects - Supports various keyboard layouts (60%, ANSI, ISO, Ergodox, etc.) - Generates unique reference designators for keys - Provides utility for adding diode routing in key matrices - TypeScript-based with React and tscircuit integration The library allows keyboard designers to easily convert layout designs into structured, machine-readable keyboard specifications.

Version
0.0.19
License
unset
Stars
3

dist/index.js

PCBPCB preview for dist/index.js
SchematicSchematic preview for dist/index.js
import { jsx, jsxs } from 'react/jsx-runtime';
import 'react';
import { createUseComponent } from '@tscircuit/core';

const refDesMap = {
    "~": "K_TILDE",
    "~`": "K_TILDE",
    "!": "K_EXCLAMATION",
    "@": "K_AT",
    "#": "K_HASH",
    $: "K_DOLLAR",
    "%": "K_PERCENT",
    "^": "K_CARET",
    "&": "K_AMPERSAND",
    "*": "K_ASTERISK",
    "(": "K_LPAREN",
    ")": "K_RPAREN",
    "{": "K_LBRACE",
    "}": "K_RBRACE",
    "|": "K_PIPE",
    "[": "K_LSQBRAK",
    "]": "K_RSQBRAK",
    '"': "K_QUOTE",
    "'": "K_SINGLEQUOTE",
    "<": "K_LT",
    ">": "K_GT",
    "?": "K_QUESTION",
    "/": "K_SLASH",
    windows: "K_WINDOWS",
    win: "K_WINDOWS",
    menu: "K_MENU",
    "caps lock": "K_CAPSLOCK",
    ":": "K_COLON",
    ";": "K_SEMICOLON",
    "1": "K_N1",
    "2": "K_N2",
    "3": "K_N3",
    "4": "K_N4",
    "5": "K_N5",
    "6": "K_N6",
    "7": "K_N7",
    "8": "K_N8",
    "9": "K_N9",
    "0": "K_N0",
    "-": "K_MINUS",
    "+": "K_PLUS",
    backspace: "K_BACKSPACE",
    tab: "K_TAB",
    " ": "K_SPACE",
    enter: "K_ENTER",
    shift: "K_SHIFT",
    ctrl: "K_CTRL",
    alt: "K_ALT",
    escape: "K_ESCAPE",
    arrowleft: "K_ARROW_LEFT",
    arrowright: "K_ARROW_RIGHT",
    arrowup: "K_ARROW_UP",
    arrowdown: "K_ARROW_DOWN",
    backquote: "K_BACKQUOTE",
    quote: "K_QUOTE",
    semicolon: "K_SEMICOLON",
    comma: "K_COMMA",
    period: "K_PERIOD",
    slash: "K_SLASH",
    backslash: "K_BACKSLASH",
    bracketleft: "K_BRACKET_LEFT",
    bracketright: "K_BRACKET_RIGHT",
    a: "K_A",
    b: "K_B",
    c: "K_C",
    d: "K_D",
    e: "K_E",
    f: "K_F",
    g: "K_G",
    h: "K_H",
    i: "K_I",
    j: "K_J",
    k: "K_K",
    l: "K_L",
    m: "K_M",
    n: "K_N",
    o: "K_O",
    p: "K_P",
    q: "K_Q",
    r: "K_R",
    s: "K_S",
    t: "K_T",
    u: "K_U",
    v: "K_V",
    w: "K_W",
    x: "K_X",
    y: "K_Y",
    z: "K_Z",
};
const getRefDesForKey = (key) => {
    const normKeyLabels = key.toLowerCase().split("\n");
    for (const normKeyLabel of normKeyLabels) {
        if (normKeyLabel in refDesMap) {
            return refDesMap[normKeyLabel];
        }
    }
    if (key === "") {
        return "K_SPACE";
    }
    if (key.match(/^[a-zA-Z0-9]+$/)) {
        return `K_${key.toUpperCase()}`;
    }
    throw new Error(`No defined refDes for key "${key}"`);
};

/**
 * Assigns logical row/col coordinates to keys based on their physical positions
 * for the ergodox keyboard layout
 */
function assignRowColByPosition(keys) {
    const keysWithNewRowCol = keys.map(key => ({ ...key }));
    // Separate keys by section
    const leftMainKeys = keysWithNewRowCol.filter(key => key.rotation === 0 && key.x < 150);
    const rightMainKeys = keysWithNewRowCol.filter(key => key.rotation === 0 && key.x > 150);
    const thumbClusterKeys = keysWithNewRowCol.filter(key => key.rotation !== 0);
    // Function to assign row/col based on position grid
    function assignRowColByPosition(keys, isRightSide = false) {
        // Create a position-based grid
        const GRID_SIZE = 19.05; // Standard key unit size
        keys.forEach(key => {
            // Calculate grid position based on physical coordinates
            const gridRow = Math.round(-key.y / GRID_SIZE); // Negative y because y increases downward
            const gridCol = Math.round(key.x / GRID_SIZE);
            key.row = gridRow;
            key.col = isRightSide ? gridCol : gridCol; // Keep original columns for now
        });
    }
    // Assign positions for main keys
    assignRowColByPosition(leftMainKeys, false);
    assignRowColByPosition(rightMainKeys, true);
    // Normalize row numbers to start from 0
    const allMainKeys = [...leftMainKeys, ...rightMainKeys];
    const minRow = Math.min(...allMainKeys.map(k => k.row));
    allMainKeys.forEach(key => {
        key.row = key.row - minRow;
    });
    // For ergodox specifically, we want to organize columns better
    // Left side: find the actual column positions
    const leftCols = [...new Set(leftMainKeys.map(k => k.col))].sort((a, b) => a - b);
    const rightCols = [...new Set(rightMainKeys.map(k => k.col))].sort((a, b) => a - b);
    // Remap left side columns to 0-based indexing
    leftMainKeys.forEach(key => {
        key.col = leftCols.indexOf(key.col);
    });
    // Remap right side columns to continue after left side
    const maxLeftCol = Math.max(...leftMainKeys.map(k => k.col));
    rightMainKeys.forEach(key => {
        const rightIndex = rightCols.indexOf(key.col);
        key.col = maxLeftCol + 1 + rightIndex;
    });
    // Handle thumb clusters
    const leftThumbKeys = thumbClusterKeys.filter(key => key.rotation < 0);
    const rightThumbKeys = thumbClusterKeys.filter(key => key.rotation > 0);
    // Sort thumb keys by Y position first, then X
    leftThumbKeys.sort((a, b) => a.y - b.y || a.x - b.x);
    rightThumbKeys.sort((a, b) => a.y - b.y || a.x - b.x);
    // Assign thumb cluster rows (starting after main key rows)
    const maxMainRow = Math.max(...allMainKeys.map(k => k.row));
    leftThumbKeys.forEach((key, index) => {
        key.row = maxMainRow + 1 + Math.floor(index / 3);
        key.col = index % 3;
    });
    const maxLeftThumbCol = Math.max(...leftMainKeys.map(k => k.col));
    rightThumbKeys.forEach((key, index) => {
        key.row = maxMainRow + 1 + Math.floor(index / 3);
        key.col = maxLeftThumbCol + 1 + (index % 3);
    });
    return keysWithNewRowCol;
}

// Define the types for keyboard-layout-editor.com JSON format
// Helper function to parse keyboard-layout-editor JSON
const parseKLELayout = (layout) => {
    // Default key size in millimeters
    const KEY_SIZE = 19.05; // Standard keycap size (19.05mm or 0.75 inches)
    const keys = [];
    let current = {
        x: 0, y: 0, x2: 0, y2: 0,
        width: 1, height: 1, width2: 1, height2: 1,
        rotation_angle: 0, rotation_x: 0, rotation_y: 0,
        nub: false,
        stepped: false, decal: false};
    let cluster = { x: 0, y: 0 };
    const nameCounters = new Map();
    // Process each row
    for (let r = 0; r < layout.length; r++) {
        if (layout[r] instanceof Array) {
            let colIndex = 0;
            // Process each item in the row
            for (let k = 0; k < layout[r].length; k++) {
                const item = layout[r][k];
                if (typeof item === 'string') {
                    // This is a key
                    let refDes = getRefDesForKey(item);
                    if (nameCounters.has(refDes)) {
                        // Edit the original item to include the counter (if it doesn't have it)
                        const ogKey = keys.find((k) => k.name === refDes);
                        if (ogKey) {
                            ogKey.name = `${refDes}${nameCounters.get(refDes)}`;
                        }
                        nameCounters.set(refDes, nameCounters.get(refDes) + 1);
                        refDes = `${refDes}${nameCounters.get(refDes)}`;
                    }
                    else {
                        nameCounters.set(refDes, 1);
                    }
                    const newKey = {
                        name: refDes,
                        x: (current.x + current.width / 2) * KEY_SIZE,
                        y: -(current.y + current.height / 2) * KEY_SIZE,
                        width: current.width * KEY_SIZE,
                        height: current.height * KEY_SIZE,
                        rotation: current.rotation_angle,
                        rotationX: current.rotation_x * KEY_SIZE,
                        rotationY: -current.rotation_y * KEY_SIZE,
                        row: r,
                        col: colIndex,
                    };
                    keys.push(newKey);
                    // Set up for the next key (from KLE logic)
                    current.x += current.width;
                    current.width = current.height = 1;
                    current.x2 = current.y2 = current.width2 = current.height2 = 0;
                    current.nub = current.stepped = current.decal = false;
                    colIndex++;
                }
                else {
                    // This is a property object
                    if (item.r != null) {
                        if (k != 0)
                            throw new Error("'r' can only be used on the first key in a row");
                        current.rotation_angle = item.r;
                    }
                    if (item.rx != null) {
                        if (k != 0)
                            throw new Error("'rx' can only be used on the first key in a row");
                        current.rotation_x = cluster.x = item.rx;
                        // Extend current with cluster (reset position to rotation center)
                        current.x = cluster.x;
                        current.y = cluster.y;
                    }
                    if (item.ry != null) {
                        if (k != 0)
                            throw new Error("'ry' can only be used on the first key in a row");
                        current.rotation_y = cluster.y = item.ry;
                        // Extend current with cluster (reset position to rotation center)
                        current.x = cluster.x;
                        current.y = cluster.y;
                    }
                    if (item.a != null) {
                        item.a;
                    }
                    if (item.x) {
                        current.x += item.x;
                    }
                    if (item.y) {
                        current.y += item.y;
                    }
                    if (item.w) {
                        current.width = current.width2 = item.w;
                    }
                    if (item.h) {
                        current.height = current.height2 = item.h;
                    }
                    // ... other properties would go here
                }
            }
            // End of the row (from KLE logic)
            current.y++;
        }
        // Reset x to rotation_x at end of row (from KLE logic)
        current.x = current.rotation_x;
    }
    // Apply logical row/col assignment based on physical positions
    return assignRowColByPosition(keys);
};

const pinLabels$2 = {
    pin1: ["C"],
    pin2: ["A"],
};
const A_1N4148WS = (props) => {
    return (jsx("chip", { pinLabels: pinLabels$2, symbolName: "diode", supplierPartNumbers: {
            jlcpcb: ["C57759"],
        }, manufacturerPartNumber: "A_1N4148WS", footprint: jsxs("footprint", { children: [jsx("smtpad", { portHints: ["pin1"], pcbX: "-1.1725910000000113mm", pcbY: "0mm", width: "0.9999979999999999mm", height: "0.7500112mm", shape: "rect" }), jsx("smtpad", { portHints: ["pin2"], pcbX: "1.1725910000000113mm", pcbY: "0mm", width: "0.9999979999999999mm", height: "0.7500112mm", shape: "rect" }), jsx("silkscreenpath", { route: [
                        { x: 0.9012427999999773, y: -0.726211400000011 },
                        { x: 0.9012427999999773, y: -0.5199887999999646 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: 0.9012427999999773, y: 0.726211400000011 },
                        { x: 0.9012427999999773, y: 0.5299964000000728 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: -0.8512047999998913, y: 0.726211400000011 },
                        { x: 0.9012427999999773, y: 0.726211400000011 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: -0.8512047999998913, y: -0.726211400000011 },
                        { x: 0.9012427999999773, y: -0.726211400000011 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: -0.44676059999994777, y: 0.726211400000011 },
                        { x: -0.44676059999994777, y: -0.726211400000011 },
                    ] })] }), cadModel: {
            objUrl: "https://modelcdn.tscircuit.com/easyeda_models/download?uuid=973acf8a660c48b1975f1ba1c890421a&pn=C57759",
            rotationOffset: { x: 0, y: 0, z: 0 },
            positionOffset: { x: 0, y: 0, z: 0 },
        }, ...props }));
};

const pinLabels$1 = {
    "1": "pin1",
    "2": "pin2",
};
const pinNames$1 = Object.values(pinLabels$1);
const KeyHotSocket = (props) => {
    return (jsx("pushbutton", { layer: "bottom", ...props, footprint: jsxs("footprint", { children: [jsx("hole", { pcbX: "3.1749999999999545mm", pcbY: "-1.2699999999999818mm", diameter: "2.9999939999999996mm" }), jsx("hole", { pcbX: "-3.1749999999999545mm", pcbY: "1.2700000000000955mm", diameter: "2.9999939999999996mm" }), jsx("smtpad", { portHints: ["2"], pcbX: "6.724904000000038mm", pcbY: "-1.2699999999999818mm", width: "2.8999941999999996mm", height: "2.4999949999999997mm", shape: "rect" }), jsx("smtpad", { portHints: ["1"], pcbX: "-6.724904000000038mm", pcbY: "1.2699999999999818mm", width: "2.8999941999999996mm", height: "2.4999949999999997mm", shape: "rect" }), jsx("silkscreenpath", { route: [
                        { x: -5.461000000000013, y: -0.4611370000000079 },
                        { x: -5.461000000000013, y: -2.9209999999999354 },
                        { x: -5.461000000000013, y: -2.9209999999999354 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: 5.4500017999999955, y: -2.950006799999869 },
                        { x: -5.461000000000013, y: -2.950006799999869 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: 0.18298159999994823, y: 1.0549889999999778 },
                        { x: 5.450027199999909, y: 1.0549889999999778 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: 5.450027199999909, y: 1.0549889999999778 },
                        { x: 5.450027199999909, y: 0.4611370000000079 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: -2.944114000000127, y: 2.9450030000000424 },
                        { x: -5.461000000000013, y: 2.9450030000000424 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: -1.707006999999976, y: 2.9450030000000424 },
                        { x: -2.944114000000127, y: 2.9450030000000424 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: -1.707006999999976, y: 2.9450030000000424 },
                        { x: -1.460311886129034, y: 2.9288336092640748 },
                        { x: -1.2178378661753868, y: 2.8806021044970294 },
                        { x: -0.9837338087377248, y: 2.801133754218995 },
                        { x: -0.762005367588813, y: 2.6917883071702136 },
                        { x: -0.5564464426572613, y: 2.5544367262343712 },
                        { x: -0.37057426423848483, y: 2.3914291751993915 },
                        { x: -0.20756921118118044, y: 2.2055548061152876 },
                        { x: -0.07022039279377168, y: 1.9999940353145576 },
                        { x: 0.03912207440475868, y: 1.7782641246765252 },
                        { x: 0.11858727851722506, y: 1.5441589992735771 },
                        { x: 0.1668155246366041, y: 1.301684331151364 },
                        { x: 0.18298159999994823, y: 1.0549889999999778 },
                    ] })] }), supplierPartNumbers: {
            jlcpcb: ["C41430893"],
        }, cadModel: {
            objUrl: "https://modelcdn.tscircuit.com/easyeda_models/download?uuid=c886ec2b42464573a88fc1f647577a49&pn=C5184526",
            rotationOffset: { x: 0, y: 0, z: 0 },
            positionOffset: { x: 0, y: 0, z: 0 },
        }, pinLabels: pinLabels$1, schPortArrangement: {
            leftSide: {
                direction: "top-to-bottom",
                pins: [1],
            },
            rightSide: {
                direction: "bottom-to-top",
                pins: [2],
            },
        } }));
};
createUseComponent(KeyHotSocket, pinNames$1);

const pinLabels = {
    "1": "pin1",
    "2": "pin2",
};
const pinNames = Object.values(pinLabels);
const KeyShaftForHotSocket = (props) => {
    return (jsx("chip", { ...props, noSchematicRepresentation: true, schWidth: 0.1, schHeight: 0.1, supplierPartNumbers: {
            jlcpcb: ["C400227"],
        }, footprint: jsxs("footprint", { children: [jsx("hole", { pcbX: "0.6349999999999909mm", pcbY: "-3.1149987999999666mm", diameter: "4.1999916mm" }), jsx("silkscreenpath", { route: [
                        { x: -7.165009800000007, y: 4.685011000000031 },
                        { x: 8.435009799999989, y: 4.685011000000031 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: -7.165009800000007, y: -10.915008600000078 },
                        { x: 8.435009799999989, y: -10.915008600000078 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: -7.165009800000007, y: 4.685011000000031 },
                        { x: -7.165009800000007, y: -10.915008600000078 },
                    ] }), jsx("silkscreenpath", { route: [
                        { x: 8.435009799999989, y: 4.685011000000031 },
                        { x: 8.435009799999989, y: -10.915008600000078 },
                    ] })] }), cadModel: {
            objUrl: "https://modelcdn.tscircuit.com/easyeda_models/download?uuid=c00f29e7afb64c29bc388e168980ded2&pn=C400227",
            rotationOffset: { x: 0, y: 0, z: 90 },
            positionOffset: { x: 0, y: 0, z: 0 },
        }, pinLabels: pinLabels, schPinSpacing: 0.75, schPortArrangement: {} }));
};
createUseComponent(KeyShaftForHotSocket, pinNames);

const Key = (props) => {
    return (jsxs("group", { pcbX: props.pcbX, pcbY: props.pcbY, schX: props.schX, schY: props.schY, children: [jsx(KeyHotSocket, { name: props.name, connections: props.connections, pcbRotation: props.pcbRotation }), jsx(KeyShaftForHotSocket, { name: `${props.name}_shaft`, pcbX: 0, pcbY: -0.52, pcbRotation: props.pcbRotation }), jsx("footprint", { children: jsx("silkscreentext", { text: props.name, pcbY: 5 }) })] }));
};

const KeyMatrix = ({ layout, rowToMicroPin, colToMicroPin, name = "KB", pcbX = 0, pcbY = 0, schX = 0, schY = 0, }) => {
    const keys = parseKLELayout(layout);
    const minX = Math.min(...keys.map((k) => k.x));
    const maxX = Math.max(...keys.map((k) => k.x + k.width));
    const minY = Math.min(...keys.map((k) => k.y));
    const maxY = Math.max(...keys.map((k) => k.y + k.height));
    // Group keys by rotation and rotation center
    const keyGroups = new Map();
    keys.forEach(key => {
        const groupKey = `${key.rotation}_${key.rotationX}_${key.rotationY}`;
        if (!keyGroups.has(groupKey)) {
            keyGroups.set(groupKey, []);
        }
        keyGroups.get(groupKey).push(key);
    });
    return (jsx("group", { pcbX: pcbX, pcbY: pcbY, schX: schX, schY: schY, children: Array.from(keyGroups.entries()).map(([groupKey, groupKeys]) => {
            const firstKey = groupKeys[0];
            const hasRotation = firstKey.rotation !== 0;
            if (hasRotation) {
                // For rotated key groups (thumb clusters), position at absolute rotation center
                const keyboardCenterX = (minX + maxX) / 2;
                const keyboardCenterY = (minY + maxY) / 2;
                const rotationCenterX = firstKey.rotationX - keyboardCenterX;
                const rotationCenterY = firstKey.rotationY - keyboardCenterY;
                return (jsx("group", { pcbX: rotationCenterX, pcbY: rotationCenterY, pcbRotation: firstKey.rotation, children: groupKeys.map((key) => {
                        // Position keys relative to their rotation center
                        const relX = key.x - key.rotationX;
                        const relY = key.y - key.rotationY;
                        return (jsxs("group", { pcbX: relX, pcbY: relY, schX: relX / 5, schY: relY / 7, children: [jsx(Key, { name: key.name }), rowToMicroPin?.[key.row] !== undefined && (jsx(A_1N4148WS, { name: `${key.name}_DIO`, connections: {
                                        A: `.${key.name} .pin1`,
                                        C: rowToMicroPin[key.row],
                                    }, pcbX: 0.5, pcbY: -13.5, schY: -1, layer: "bottom" })), colToMicroPin?.[key.col] !== undefined && (jsx("trace", { from: `.${key.name} .pin2`, to: colToMicroPin[key.col] }))] }, key.name));
                    }) }, groupKey));
            }
            else {
                // For non-rotated key groups, position relative to keyboard center
                return (jsx("group", { children: groupKeys.map((key) => {
                        const relX = key.x - (minX + maxX) / 2;
                        const relY = key.y - (minY + maxY) / 2;
                        return (jsxs("group", { pcbX: relX, pcbY: relY, schX: relX / 5, schY: relY / 7, children: [jsx(Key, { name: key.name }), rowToMicroPin?.[key.row] !== undefined && (jsx(A_1N4148WS, { name: `${key.name}_DIO`, connections: {
                                        A: `.${key.name} .pin1`,
                                        C: rowToMicroPin[key.row],
                                    }, pcbX: 0.5, pcbY: -13.5, schY: -1, layer: "bottom" })), colToMicroPin?.[key.col] !== undefined && (jsx("trace", { from: `.${key.name} .pin2`, to: colToMicroPin[key.col] }))] }, key.name));
                    }) }, groupKey));
            }
        }) }));
};

// biome-ignore lint: This file contains keyboard layout data
// From: https://www.keyboard-layout-editor.com/#/
const layouts = {
    default60: [
        ["~\n`", "!\n1", "@\n2", "#\n3", "$\n4", "%\n5", "^\n6", "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", { w: 2 }, "Backspace"],
        [{ w: 1.5 }, "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{\n[", "}\n]", { w: 1.5 }, "|\n\\"],
        [{ w: 1.75 }, "Caps Lock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":\n;", "\"\n'", { w: 2.25 }, "Enter"],
        [{ w: 2.25 }, "Shift", "Z", "X", "C", "V", "B", "N", "M", "<\n,", ">\n.", "?\n/", { w: 2.75 }, "Shift"],
        [{ w: 1.25 }, "Ctrl", { w: 1.25 }, "Win", { w: 1.25 }, "Alt", { a: 7, w: 6.25 }, "", { a: 4, w: 1.25 }, "Alt", { w: 1.25 }, "Win", { w: 1.25 }, "Menu", { w: 1.25 }, "Ctrl"]
    ],
    ansi104: [
        ["Esc", { x: 1 }, "F1", "F2", "F3", "F4", { x: 0.5 }, "F5", "F6", "F7", "F8", { x: 0.5 }, "F9", "F10", "F11", "F12", { x: 0.25 }, "PrtSc", "Scroll Lock", "Pause\nBreak"],
        [{ y: 0.5 }, "~\n`", "!\n1", "@\n2", "#\n3", "$\n4", "%\n5", "^\n6", "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", { w: 2 }, "Backspace", { x: 0.25 }, "Insert", "Home", "PgUp", { x: 0.25 }, "Num Lock", "/", "*", "-"],
        [{ w: 1.5 }, "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{\n[", "}\n]", { w: 1.5 }, "|\n\\", { x: 0.25 }, "Delete", "End", "PgDn", { x: 0.25 }, "7\nHome", "8\n↑", "9\nPgUp", { h: 2 }, "+"],
        [{ w: 1.75 }, "Caps Lock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":\n;", "\"\n'", { w: 2.25 }, "Enter", { x: 3.5 }, "4\n←", "5", "6\n→"],
        [{ w: 2.25 }, "Shift", "Z", "X", "C", "V", "B", "N", "M", "<\n,", ">\n.", "?\n/", { w: 2.75 }, "Shift", { x: 1.25 }, "↑", { x: 1.25 }, "1\nEnd", "2\n↓", "3\nPgDn", { h: 2 }, "Enter"],
        [{ w: 1.25 }, "Ctrl", { w: 1.25 }, "Win", { w: 1.25 }, "Alt", { a: 7, w: 6.25 }, "", { a: 4, w: 1.25 }, "Alt", { w: 1.25 }, "Win", { w: 1.25 }, "Menu", { w: 1.25 }, "Ctrl", { x: 0.25 }, "←", "↓", "→", { x: 0.25, w: 2 }, "0\nIns", ".\nDel"]
    ],
    iso105: [
        ["Esc", { x: 1 }, "F1", "F2", "F3", "F4", { x: 0.5 }, "F5", "F6", "F7", "F8", { x: 0.5 }, "F9", "F10", "F11", "F12", { x: 0.25 }, "PrtSc", "Scroll Lock", "Pause\nBreak"],
        [{ y: 0.5 }, "¬\n`", "!\n1", "\"\n2", "£\n3", "$\n4", "%\n5", "^\n6", "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", { w: 2 }, "Backspace", { x: 0.25 }, "Insert", "Home", "PgUp", { x: 0.25 }, "Num Lock", "/", "*", "-"],
        [{ w: 1.5 }, "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{\n[", "}\n]", { x: 0.25, w: 1.25, h: 2, w2: 1.5, h2: 1, x2: -0.25 }, "Enter", { x: 0.25 }, "Delete", "End", "PgDn", { x: 0.25 }, "7\nHome", "8\n↑", "9\nPgUp", { h: 2 }, "+"],
        [{ w: 1.75 }, "Caps Lock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":\n;", "@\n'", "~\n#", { x: 4.75 }, "4\n←", "5", "6\n→"],
        [{ w: 1.25 }, "Shift", "|\n\\", "Z", "X", "C", "V", "B", "N", "M", "<\n,", ">\n.", "?\n/", { w: 2.75 }, "Shift", { x: 1.25 }, "↑", { x: 1.25 }, "1\nEnd", "2\n↓", "3\nPgDn", { h: 2 }, "Enter"],
        [{ w: 1.25 }, "Ctrl", { w: 1.25 }, "Win", { w: 1.25 }, "Alt", { a: 7, w: 6.25 }, "", { a: 4, w: 1.25 }, "AltGr", { w: 1.25 }, "Win", { w: 1.25 }, "Menu", { w: 1.25 }, "Ctrl", { x: 0.25 }, "←", "↓", "→", { x: 0.25, w: 2 }, "0\nIns", ".\nDel"]
    ],
    iso60: [
        ["¬\n`", "!\n1", "\"\n2", "£\n3", "$\n4", "%\n5", "^\n6", "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", { w: 2 }, "Backspace"],
        [{ w: 1.5 }, "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{\n[", "}\n]", { x: 0.25, w: 1.25, h: 2, w2: 1.5, h2: 1, x2: -0.25 }, "Enter"],
        [{ w: 1.75 }, "Caps Lock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":\n;", "@\n'", "~\n#"],
        [{ w: 1.25 }, "Shift", "|\n\\", "Z", "X", "C", "V", "B", "N", "M", "<\n,", ">\n.", "?\n/", { w: 2.75 }, "Shift"],
        [{ w: 1.25 }, "Ctrl", { w: 1.25 }, "Win", { w: 1.25 }, "Alt", { a: 7, w: 6.25 }, "", { a: 4, w: 1.25 }, "AltGr", { w: 1.25 }, "Win", { w: 1.25 }, "Menu", { w: 1.25 }, "Ctrl"]
    ],
    jd40: [
        ["Esc", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Back<br>Space"],
        [{ w: 1.25 }, "Tab", "A", "S", "D", "F", "G", "H", "J", "K", "L", { w: 1.75 }, "Enter"],
        [{ w: 1.75 }, "Shift", "Z", "X", "C", "V", "B", "N", "M", "<\n.", { w: 1.25 }, "Shift", "Fn"],
        [{ w: 1.25 }, "Hyper", "Super", "Meta", { a: 7, w: 6.25 }, "", { a: 4, w: 1.25 }, "Meta", { w: 1.25 }, "Super"]
    ],
    ergodox: [
        [{ x: 3.5 }, "#\n3", { x: 10.5 }, "*\n8"],
        [{ y: -0.875, x: 2.5 }, "@\n2", { x: 1 }, "$\n4", { x: 8.5 }, "&\n7", { x: 1 }, "(\n9"],
        [{ y: -0.875, x: 5.5 }, "%\n5", { a: 7 }, "", { x: 4.5 }, "", { a: 4 }, "^\n6"],
        [{ y: -0.875, a: 7, w: 1.5 }, "", { a: 4 }, "!\n1", { x: 14.5 }, ")\n0", { a: 7, w: 1.5 }, ""],
        [{ y: -0.375, x: 3.5, a: 4 }, "E", { x: 10.5 }, "I"],
        [{ y: -0.875, x: 2.5 }, "W", { x: 1 }, "R", { x: 8.5 }, "U", { x: 1 }, "O"],
        [{ y: -0.875, x: 5.5 }, "T", { a: 7, h: 1.5 }, "", { x: 4.5, h: 1.5 }, "", { a: 4 }, "Y"],
        [{ y: -0.875, a: 7, w: 1.5 }, "", { a: 4 }, "Q", { x: 14.5 }, "P", { a: 7, w: 1.5 }, ""],
        [{ y: -0.375, x: 3.5, a: 4 }, "D", { x: 10.5 }, "K"],
        [{ y: -0.875, x: 2.5 }, "S", { x: 1 }, "F", { x: 8.5 }, "J", { x: 1 }, "L"],
        [{ y: -0.875, x: 5.5 }, "G", { x: 6.5 }, "H"],
        [{ y: -0.875, a: 7, w: 1.5 }, "", { a: 4 }, "A", { x: 14.5 }, ":\n;", { a: 7, w: 1.5 }, ""],
        [{ y: -0.625, x: 6.5, h: 1.5 }, "", { x: 4.5, h: 1.5 }, ""],
        [{ y: -0.75, x: 3.5, a: 4 }, "C", { x: 10.5 }, "<\n,"],
        [{ y: -0.875, x: 2.5 }, "X", { x: 1 }, "V", { x: 8.5 }, "M", { x: 1 }, ">\n."],
        [{ y: -0.875, x: 5.5 }, "B", { x: 6.5 }, "N"],
        [{ y: -0.875, a: 7, w: 1.5 }, "", { a: 4 }, "Z", { x: 14.5 }, "?\n/", { a: 7, w: 1.5 }, ""],
        [{ y: -0.375, x: 3.5 }, "", { x: 10.5 }, ""],
        [{ y: -0.875, x: 2.5 }, "", { x: 1 }, "", { x: 8.5 }, "", { x: 1 }, ""],
        [{ y: -0.75, x: 0.5 }, "", "", { x: 14.5 }, "", ""],
        [{ r: -45, rx: 6.5, ry: 4.25, y: -1, x: 1 }, "", ""],
        [{ h: 2 }, "", { h: 2 }, "", ""],
        [{ x: 2 }, ""],
        [{ r: 45, rx: 13, y: -1, x: -3 }, "", ""],
        [{ x: -3 }, "", { h: 2 }, "", { h: 2 }, ""],
        [{ x: -3 }, ""]
    ],
    atreus: [
        [{ r: 10, rx: 1, y: -0.1, x: 2 }, "E"],
        [{ y: -0.65, x: 1 }, "W", { x: 1 }, "R"],
        [{ y: -0.75 }, "Q"],
        [{ y: -0.9, x: 4 }, "T"],
        [{ y: -0.7, x: 2 }, "D"],
        [{ y: -0.65, x: 1 }, "S", { x: 1 }, "F"],
        [{ y: -0.75 }, "A"],
        [{ y: -0.9, x: 4 }, "G"],
        [{ y: -0.7, x: 2 }, "C"],
        [{ y: -0.65, x: 1 }, "X", { x: 1 }, "V"],
        [{ y: -0.75 }, "Z"],
        [{ y: -0.9, x: 4 }, "B"],
        [{ y: -0.75, x: 5, h: 1.5 }, "Ctrl"],
        [{ y: -0.95, x: 2 }, "super"],
        [{ y: -0.65, x: 1 }, "Tab", { x: 1 }, "Shift"],
        [{ y: -0.75 }, "Esc"],
        [{ y: -0.9, x: 4 }, "Bksp"],
        [{ r: -10, rx: 7, ry: 0.965, y: -0.2, x: 2 }, "I"],
        [{ y: -0.65, x: 1 }, "U", { x: 1 }, "O"],
        [{ y: -0.75, x: 4 }, "P"],
        [{ y: -0.9 }, "Y"],
        [{ y: -0.7, x: 2 }, "K"],
        [{ y: -0.65, x: 1 }, "J", { x: 1 }, "L"],
        [{ y: -0.75, x: 4 }, ":\n;"],
        [{ y: -0.9 }, "H"],
        [{ y: -0.7, x: 2 }, "<\n,"],
        [{ y: -0.65, x: 1 }, "M", { x: 1 }, ">\n."],
        [{ y: -0.75, x: 4 }, "?\n/"],
        [{ y: -0.9 }, "N"],
        [{ y: -0.75, x: -1, h: 1.5 }, "Alt"],
        [{ y: -0.95, x: 2 }, "_\n-"],
        [{ y: -0.65, x: 1 }, "fn", { x: 1 }, "\"\n'"],
        [{ y: -0.75, x: 4 }, "Enter"],
        [{ y: -0.9 }, "Space"]
    ],
    planck: [
        [{ a: 7 }, "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "Back Space"],
        ["Esc", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'"],
        ["Shift", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "Return"],
        ["", "Ctrl", "Alt", "Super", "&dArr;", { w: 2 }, "", "&uArr;", "&larr;", "&darr;", "&uarr;", "&rarr;"]
    ],
    keycool84: [
        [{ a: 6 }, "Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", { a: 5 }, "PrtSc\nNmLk", "Pause\nScrLk", "Delete\nInsert"],
        [{ a: 4 }, "~\n`", "!\n1", "@\n2", "#\n3", "$\n4", "%\n5", "^\n6", "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", { a: 6, w: 2 }, "Backspace", "Home"],
        [{ a: 4, w: 1.5 }, "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{\n[", "}\n]", { w: 1.5 }, "|\n\\", { a: 6 }, "Page Up"],
        [{ a: 4, w: 1.75 }, "Caps Lock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":\n;", "\"\n'", { a: 6, w: 2.25 }, "Enter", "Page Down"],
        [{ w: 2.25 }, "Shift", { a: 4 }, "Z", "X", "C", "V", "B", "N", "M", "<\n,", ">\n.", "?\n/", { a: 6, w: 1.75 }, "Shift", { a: 7 }, "↑", { a: 6 }, "End"],
        [{ w: 1.25 }, "Ctrl", { w: 1.25 }, "Win", { w: 1.25 }, "Alt", { a: 7, w: 6.25 }, "", { a: 6 }, "Alt", "Fn", "Ctrl", { a: 7 }, "←", "↓", "→"]
    ],
    leopold_fc660m: [
        ["~\n`", "!\n1", "@\n2", "#\n3", "$\n4", "%\n5", "^\n6", "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", { w: 2 }, "Backspace", { x: 0.5 }, "Insert"],
        [{ w: 1.5 }, "Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{\n[", "}\n]", { w: 1.5 }, "|\n\\", { x: 0.5 }, "Delete"],
        [{ w: 1.75 }, "Caps Lock", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":\n;", "\"\n'", { w: 2.25 }, "Enter"],
        [{ w: 2.25 }, "Shift", "Z", "X", "C", "V", "B", "N", "M", "<\n,", ">\n.", "?\n/", { w: 2.25 }, "Shift", "↑"],
        [{ w: 1.25 }, "Ctrl", "Win", { w: 1.25 }, "Alt", { a: 7, w: 6.25 }, "", { a: 4, w: 1.25 }, "Alt", { w: 1.25 }, "Ctrl", { w: 1.25 }, "Menu", "←", "↓", "→"]
    ],
    apple_wireless: [
        [{ y: 0.75, t: "#666666", p: "CHICKLET", a: 7, f: 2, w: 1.0357, h: 0.75 }, "esc", { a: 4, fa: [0, 0, 0, 1], w: 1.0357, h: 0.75 }, "\n\n\nF1", { w: 1.0357, h: 0.75 }, "\n\n\nF2", { w: 1.0357, h: 0.75 }, "\n\n\nF3", { w: 1.0357, h: 0.75 }, "\n\n\nF4", { w: 1.0357, h: 0.75 }, "\n\n\nF5", { w: 1.0357, h: 0.75 }, "\n\n\nF6", { w: 1.0357, h: 0.75 }, "\n\n\nF7\n\n\n\n\n\n<i class='fa fa-backward'></i>", { fa: [0, 0, 0, 1, 0, 0, 0, 0, 0, 1], w: 1.0357, h: 0.75 }, "\n\n\nF8\n\n\n\n\n\n<i class='fa fa-play'></i><i class='fa fa-pause'></i>", { fa: [0, 0, 0, 1], w: 1.0357, h: 0.75 }, "\n\n\nF9\n\n\n\n\n\n<i class='fa fa-forward'></i>", { w: 1.0357, h: 0.75 }, "\n\n\nF10\n\n\n\n\n\n<i class='fa fa-volume-off'></i>", { w: 1.0357, h: 0.75 }, "\n\n\nF11\n\n\n\n\n\n<i class='fa fa-volume-down'></i>", { w: 1.0357, h: 0.75 }, "\n\n\nF12\n\n\n\n\n\n<i class='fa fa-volume-up'></i>", { a: 7, w: 1.0357, h: 0.75 }, "<i class='fa fa-eject'></i>"],
        [{ y: -0.25, a: 5, f: 5 }, "<i class=\"varela varela-tilde\"></i>\n`", "!\n1", "@\n2", "#\n3", "$\n4", "%\n5", "^\n6", "&\n7", "*\n8", "(\n9", ")\n0", "_\n-", "+\n=", { a: 4, f: 2, w: 1.5 }, "\n\n\ndelete"],
        [{ w: 1.5 }, "\ntab", { a: 7, f: 5 }, "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", { a: 5 }, "{\n[", "}\n]", "|\n\\"],
        [{ a: 4, f: 2, fa: [1], w: 1.75 }, "<i class='kb kb-Multimedia-Record'></i>\ncaps lock", { a: 7, f: 5 }, "A", "S", "D", { n: true }, "F", "G", "H", { n: true }, "J", "K", "L", { a: 5 }, ":\n;", "\"\n'", { a: 4, f: 2, fa: [0, 0, 1], w: 1.75 }, "\n\nenter\nreturn"],
        [{ w: 2.25 }, "\nshift", { a: 7, f: 5 }, "Z", "X", "C", "V", "B", "N", "M", { a: 5 }, "<\n,", ">\n.", "?\n/", { a: 4, f: 2, w: 2.25 }, "\n\n\nshift"],
        [{ h: 1.111 }, "\nfn", { h: 1.111 }, "\ncontrol", { fa: [1], h: 1.111 }, "alt\noption", { fa: [1, 0, 5], w: 1.25, h: 1.111 }, "\n\n⌘\ncommand", { a: 7, w: 5, h: 1.111 }, "", { a: 4, fa: [5], w: 1.25, h: 1.111 }, "⌘\ncommand", { fa: [5, 0, 1], h: 1.111 }, "\n\nalt\noption", { x: 1, a: 7, f: 5, h: 0.611 }, "↑"],
        [{ y: -0.5, x: 11.5, h: 0.6111 }, "←", { h: 0.6111 }, "↓", { h: 0.6111 }, "→"]
    ],
};

export { A_1N4148WS, KeyMatrix, getRefDesForKey, layouts, parseKLELayout };