{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://abyss.keyboard-hub.com/schemas/keyboard-layout/v1.json",
  "title": "KeyboardLayout",
  "description": "A concrete keyboard layout: physical key positions, active modules, and optional keymap capabilities.",
  "type": "object",
  "required": ["$schema", "name", "positions", "modules", "features"],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "const": "https://abyss.keyboard-hub.com/schemas/keyboard-layout/v1.json",
      "description": "Canonical JSON Schema URI for KeyboardLayout v1."
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "description": "Stable layout identifier, e.g. ANSI, ISO, HHKB, or default."
    },
    "displayName": {
      "type": "string",
      "minLength": 1,
      "description": "Optional user-facing layout name."
    },
    "layout": {
      "type": "string",
      "enum": ["us", "jis"],
      "description": "Optional keycode preview layout used when the keymap JSON does not define one."
    },
    "positions": {
      "type": "array",
      "minItems": 1,
      "description": "Physical key positions in binding order.",
      "items": { "$ref": "#/definitions/KeyPosition" }
    },
    "matrix": {
      "$ref": "#/definitions/Matrix",
      "description": "Optional keyboard matrix dimensions used to format firmware exports. The key bindings remain ordered by positions."
    },
    "modules": {
      "type": "array",
      "description": "Hardware modules that are part of this layout.",
      "items": {
        "$ref": "https://abyss.keyboard-hub.com/schemas/module-definition/v1.json"
      },
      "default": []
    },
    "features": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "combo": {
          "anyOf": [
            { "$ref": "#/definitions/ComboFeature" },
            { "type": "null" }
          ],
          "description": "Set to null when this layout is not capable of combos."
        },
        "macro": {
          "anyOf": [
            { "$ref": "#/definitions/MacroFeature" },
            { "type": "null" }
          ],
          "description": "Set to null when this layout is not capable of macros."
        }
      }
    },
    "firmwareDialect": {
      "$ref": "#/definitions/LayoutDialect",
      "description": "Optional source firmware layout identifiers and option metadata.",
      "default": {}
    }
  },
  "definitions": {
    "Matrix": {
      "type": "object",
      "required": ["rows", "cols"],
      "additionalProperties": false,
      "properties": {
        "rows": {
          "type": "integer",
          "minimum": 1,
          "maximum": 400,
          "description": "Number of matrix rows."
        },
        "cols": {
          "type": "integer",
          "minimum": 1,
          "maximum": 400,
          "description": "Number of matrix columns."
        }
      }
    },
    "KeyPosition": {
      "type": "object",
      "required": ["x", "y"],
      "additionalProperties": false,
      "properties": {
        "x": { "type": "number", "description": "X position in key units." },
        "y": { "type": "number", "description": "Y position in key units." },
        "w": { "type": "number", "exclusiveMinimum": 0, "default": 1 },
        "h": { "type": "number", "exclusiveMinimum": 0, "default": 1 },
        "r": {
          "type": "number",
          "description": "Rotation angle in centidegrees (degrees * 100)."
        },
        "rx": { "type": "number", "description": "Rotation origin X." },
        "ry": { "type": "number", "description": "Rotation origin Y." },
        "color": { "type": "string", "minLength": 1 },
        "shape": {
          "type": "string",
          "enum": ["normal", "iso-enter"],
          "default": "normal",
          "description": "Optional keycap shape hint. Omit or use normal for rectangular keys; use iso-enter for ISO Enter."
        }
      },
      "description": "Physical key geometry in key units."
    },
    "LayoutDialect": {
      "type": "object",
      "description": "Firmware-specific layout metadata. Custom dialect keys such as cormoran/zmk+custom-studio-protocol may contain any object.",
      "additionalProperties": {
        "type": "object",
        "additionalProperties": true
      },
      "properties": {
        "zmk": { "$ref": "#/definitions/LayoutDialectZmk" },
        "via": { "$ref": "#/definitions/LayoutDialectVia" },
        "vial": { "$ref": "#/definitions/LayoutDialectVial" }
      }
    },
    "LayoutDialectZmk": {
      "type": "object",
      "additionalProperties": false,
      "required": ["layoutName"],
      "properties": {
        "layoutId": { "type": "string", "minLength": 1 },
        "layoutName": { "type": "string", "minLength": 1 }
      }
    },
    "LayoutDialectVia": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Optional VIA keyboard definition name override."
        },
        "vendorId": {
          "type": "string",
          "minLength": 1,
          "description": "Optional VIA/QMK USB vendor ID, e.g. 0x5957."
        },
        "productId": {
          "type": "string",
          "minLength": 1,
          "description": "Optional VIA/QMK USB product ID, e.g. 0x0400."
        },
        "layoutName": { "type": "string", "minLength": 1 },
        "optionKeys": {
          "type": "array",
          "items": { "type": "string", "minLength": 1 }
        },
        "options": {
          "type": "array",
          "items": { "type": "integer", "minimum": 0 }
        },
        "matrixPositions": {
          "type": "array",
          "description": "Matrix row/column for each key in positions order.",
          "items": { "$ref": "#/definitions/MatrixPosition" }
        },
        "skippedIndices": {
          "type": "array",
          "description": "Original VIA layouts.keymap key indices skipped by the selected layout option values.",
          "items": { "type": "integer", "minimum": 0 }
        },
        "optionLayouts": {
          "type": "array",
          "description": "VIA layout-option alternatives that are not necessarily active in positions. Each entry stores the physical keys that exist for one layouts.labels option value.",
          "items": {
            "type": "object",
            "required": [
              "optionIndex",
              "optionValue",
              "positions",
              "matrixPositions"
            ],
            "additionalProperties": false,
            "properties": {
              "optionIndex": { "type": "integer", "minimum": 0 },
              "optionValue": { "type": "integer", "minimum": 0 },
              "positions": {
                "type": "array",
                "items": { "$ref": "#/definitions/KeyPosition" }
              },
              "matrixPositions": {
                "type": "array",
                "items": { "$ref": "#/definitions/MatrixPosition" }
              },
              "keymapIndices": {
                "type": "array",
                "description": "Original VIA layouts.keymap key index for each option key in positions order.",
                "items": { "type": "integer", "minimum": 0 }
              }
            }
          }
        },
        "labels": {
          "type": "array",
          "description": "Raw VIA layouts.labels option metadata. String entries are toggles; string arrays are select controls whose first entry is the control label.",
          "items": {
            "oneOf": [
              { "type": "string", "minLength": 1 },
              {
                "type": "array",
                "minItems": 1,
                "items": { "type": "string", "minLength": 1 }
              }
            ]
          }
        },
        "lighting": {
          "description": "Optional raw VIA lighting value.",
          "type": ["string", "object", "array", "number", "boolean", "null"]
        },
        "menus": {
          "description": "Optional raw VIA menus value.",
          "type": ["string", "object", "array", "number", "boolean", "null"]
        },
        "keycodes": {
          "description": "Optional raw VIA keycodes value.",
          "type": ["string", "object", "array", "number", "boolean", "null"]
        },
        "customKeycodes": {
          "description": "Optional raw VIA customKeycodes value.",
          "type": ["string", "object", "array", "number", "boolean", "null"]
        }
      }
    },
    "LayoutDialectVial": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "layoutName": { "type": "string", "minLength": 1 },
        "optionKeys": {
          "type": "array",
          "items": { "type": "string", "minLength": 1 }
        },
        "options": {
          "type": "array",
          "items": { "type": "integer", "minimum": 0 }
        },
        "labels": {
          "description": "Raw Vial layout label metadata.",
          "type": ["object", "array", "string", "number", "boolean", "null"]
        }
      }
    },
    "ComboFeature": {
      "type": "object",
      "required": ["maxCombo"],
      "additionalProperties": false,
      "properties": {
        "maxCombo": {
          "type": "integer",
          "minimum": 0,
          "description": "Maximum number of combos supported by keymaps for this layout."
        }
      }
    },
    "MacroFeature": {
      "type": "object",
      "required": ["maxMacro"],
      "additionalProperties": false,
      "properties": {
        "maxMacro": {
          "type": "integer",
          "minimum": 0,
          "description": "Maximum number of macros supported by keymaps for this layout."
        }
      }
    },
    "MatrixPosition": {
      "type": "object",
      "additionalProperties": false,
      "required": ["row", "column"],
      "properties": {
        "row": { "type": "integer", "minimum": 0 },
        "column": { "type": "integer", "minimum": 0 }
      }
    }
  }
}
