{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://abyss.keyboard-hub.com/schemas/keymap/v1.json",
  "title": "KeyboardHubKeymap",
  "description": "KeyboardHub common keymap format v1. Based on HID Usage Tables; converts to ZMK, QMK/VIA, KLE, and related firmware/export targets without firmware-specific branches.",
  "type": "object",
  "required": ["$schema", "keyboard", "name", "layers"],
  "additionalProperties": false,
  "properties": {
    "$schema": {
      "type": "string",
      "const": "https://abyss.keyboard-hub.com/schemas/keymap/v1.json",
      "description": "Canonical JSON Schema URI for KeyboardHubKeymap v1."
    },
    "keyboard": {
      "type": "string",
      "minLength": 2,
      "maxLength": 80,
      "pattern": "^[a-z0-9]+(?:[-_][a-z0-9]+)*$",
      "description": "Keyboard slug matching keyboards.slug in the database."
    },
    "name": { "type": "string", "minLength": 1, "maxLength": 100 },
    "description": { "type": "string", "maxLength": 500 },
    "layout": {
      "type": "string",
      "enum": ["us", "jis"],
      "description": "Optional keycode preview layout. When both the keymap and layout JSON define this, the keymap value takes precedence."
    },
    "locale": {
      "type": "string",
      "enum": ["en", "ja", "ja-kana"],
      "description": "Optional keycode preview locale used when rendering key legends."
    },
    "keycapPreset": {
      "type": "string",
      "enum": ["flat", "minimal", "cherry", "dsa", "roundedTop", "deep3d"],
      "description": "Optional keycap shape preset used when rendering this keymap preview."
    },
    "fontFamily": {
      "type": "string",
      "minLength": 1,
      "maxLength": 240,
      "pattern": "^[A-Za-z0-9 ._,'\"-]+$",
      "description": "Optional CSS font-family value used when rendering this keymap preview on keymap detail pages. Google Fonts entries may be listed in fallback order before system fallbacks."
    },
    "modulePreset": {
      "type": "string",
      "enum": ["flat", "soft3d", "deep3d", "blueprint"],
      "description": "Optional module visual preset used when rendering this keymap preview."
    },
    "layers": {
      "type": "array",
      "minItems": 1,
      "maxItems": 32,
      "items": { "$ref": "#/definitions/Layer" }
    },
    "modules": {
      "type": "array",
      "description": "Global functional settings for active modules. Layer modules override these values by matching name.",
      "items": { "$ref": "#/definitions/Module" },
      "default": []
    },
    "combos": {
      "type": "array",
      "description": "Optional combo triggers. Each combo is a set of physical key positions plus the binding to trigger.",
      "items": { "$ref": "#/definitions/Combo" },
      "default": []
    },
    "macros": {
      "type": "array",
      "description": "Optional named macros containing action steps that can be triggered by MacroBinding.",
      "items": { "$ref": "#/definitions/Macro" },
      "default": []
    }
  },
  "definitions": {
    "Layer": {
      "type": "object",
      "required": ["name", "bindings"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "maxLength": 50,
          "description": "Human-readable layer name, e.g. 'Base', 'Symbol', 'Nav'."
        },
        "bindings": {
          "type": "array",
          "description": "One binding per physical key, in the order defined by the keyboard layout.",
          "items": {
            "$ref": "https://abyss.keyboard-hub.com/schemas/keybindings/v1.json"
          }
        },
        "modules": {
          "type": "array",
          "description": "Layer-specific functional module settings. Values override top-level modules by matching name.",
          "items": { "$ref": "#/definitions/Module" },
          "default": []
        }
      }
    },
    "Module": {
      "oneOf": [
        { "$ref": "#/definitions/TrackballModule" },
        { "$ref": "#/definitions/RotaryEncoderModule" },
        { "$ref": "#/definitions/TouchPadModule" },
        { "$ref": "#/definitions/CustomModule" }
      ]
    },
    "TrackballModule": {
      "type": "object",
      "required": ["name", "type"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Stable module instance identifier matching the layout module definition."
        },
        "type": { "const": "trackball" },
        "firmwareDialect": {
          "$ref": "#/definitions/ModuleDialect",
          "description": "Optional source firmware metadata.",
          "default": {}
        },
        "scaleX": {
          "type": "number",
          "description": "Pointer sensitivity or smoothness along the X axis."
        },
        "scaleY": {
          "type": "number",
          "description": "Pointer sensitivity or smoothness along the Y axis."
        },
        "rotation": {
          "type": "number",
          "description": "Pointer coordinate rotation in degrees."
        },
        "invertX": {
          "type": "boolean",
          "description": "Invert pointer movement on the X axis."
        },
        "invertY": {
          "type": "boolean",
          "description": "Invert pointer movement on the Y axis."
        },
        "tempLayer": {
          "type": "integer",
          "minimum": 0,
          "maximum": 31,
          "description": "Layer temporarily activated while this pointer module is in use. Must also reference an existing keymap layer."
        },
        "activeLayers": {
          "type": "array",
          "items": { "type": "integer", "minimum": 0, "maximum": 31 },
          "description": "Layers where this pointer module is active. Omitted or empty means all layers. Each value must also reference an existing keymap layer."
        }
      }
    },
    "RotaryEncoderModule": {
      "type": "object",
      "required": ["name", "type"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Stable module instance identifier matching the layout module definition."
        },
        "type": { "const": "rotary_encoder" },
        "firmwareDialect": {
          "$ref": "#/definitions/ModuleDialect",
          "description": "Optional source firmware metadata.",
          "default": {}
        },
        "rotation": {
          "type": "number",
          "description": "Encoder orientation in degrees."
        },
        "stepsPerDetent": {
          "type": "integer",
          "minimum": 1,
          "description": "Firmware step count emitted for one tactile detent."
        },
        "press": {
          "type": "boolean",
          "description": "Whether the encoder also supports a press input."
        },
        "clockwise": {
          "$ref": "https://abyss.keyboard-hub.com/schemas/keybindings/v1.json",
          "description": "Binding emitted by clockwise rotation."
        },
        "counterClockwise": {
          "$ref": "https://abyss.keyboard-hub.com/schemas/keybindings/v1.json",
          "description": "Binding emitted by counter-clockwise rotation."
        }
      }
    },
    "TouchPadModule": {
      "type": "object",
      "required": ["name", "type"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Stable module instance identifier matching the layout module definition."
        },
        "type": { "const": "touch_pad" },
        "firmwareDialect": {
          "$ref": "#/definitions/ModuleDialect",
          "description": "Optional source firmware metadata.",
          "default": {}
        },
        "scaleX": {
          "type": "number",
          "description": "Pointer sensitivity or smoothness along the X axis."
        },
        "scaleY": {
          "type": "number",
          "description": "Pointer sensitivity or smoothness along the Y axis."
        },
        "rotation": {
          "type": "number",
          "description": "Pointer coordinate rotation in degrees."
        },
        "tap": {
          "type": "boolean",
          "description": "Whether tap-to-click behavior is supported."
        },
        "invertX": {
          "type": "boolean",
          "description": "Invert pointer movement on the X axis."
        },
        "invertY": {
          "type": "boolean",
          "description": "Invert pointer movement on the Y axis."
        },
        "tempLayer": {
          "type": "integer",
          "minimum": 0,
          "maximum": 31,
          "description": "Layer temporarily activated while this pointer module is in use. Must also reference an existing keymap layer."
        },
        "activeLayers": {
          "type": "array",
          "items": { "type": "integer", "minimum": 0, "maximum": 31 },
          "description": "Layers where this pointer module is active. Omitted or empty means all layers. Each value must also reference an existing keymap layer."
        }
      }
    },
    "CustomModule": {
      "type": "object",
      "required": ["name", "type", "values"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Stable module instance identifier matching the layout module definition."
        },
        "type": { "const": "custom" },
        "firmwareDialect": {
          "$ref": "#/definitions/ModuleDialect",
          "description": "Optional source firmware metadata.",
          "default": {}
        },
        "values": {
          "type": "array",
          "items": { "$ref": "#/definitions/CustomValue" }
        }
      }
    },
    "CustomValue": {
      "type": "object",
      "required": ["name", "value"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Custom attribute name from the layout module definition."
        },
        "value": {
          "description": "Custom attribute value.",
          "anyOf": [
            { "type": "number" },
            { "type": "integer" },
            { "type": "boolean" },
            { "type": "string" },
            {
              "$ref": "https://abyss.keyboard-hub.com/schemas/keybindings/v1.json"
            }
          ]
        }
      }
    },
    "ModuleDialect": {
      "type": "object",
      "description": "Firmware-specific module metadata. Custom dialect keys may contain any object.",
      "additionalProperties": {
        "type": "object",
        "additionalProperties": true
      },
      "properties": {
        "zmk": { "type": "object", "additionalProperties": true },
        "via": { "type": "object", "additionalProperties": true },
        "vial": { "type": "object", "additionalProperties": true }
      }
    },
    "Combo": {
      "type": "object",
      "description": "A set of physical key positions that triggers a binding.",
      "required": ["positions", "binding"],
      "additionalProperties": false,
      "properties": {
        "positions": {
          "type": "array",
          "minItems": 2,
          "uniqueItems": true,
          "items": { "type": "integer", "minimum": 0 },
          "description": "Physical key indexes in the selected layout's positions array."
        },
        "binding": {
          "$ref": "https://abyss.keyboard-hub.com/schemas/keybindings/v1.json",
          "description": "Binding triggered by pressing the combo."
        }
      }
    },
    "Macro": {
      "type": "object",
      "description": "Named sequence of macro action steps. Macro names are referenced by MacroBinding.",
      "required": ["name", "bindings"],
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Stable macro name unique within this keymap."
        },
        "bindings": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "https://abyss.keyboard-hub.com/schemas/keybindings/v1.json#/definitions/MacroStepBinding"
          },
          "description": "Ordered action steps emitted by this macro."
        }
      }
    }
  }
}
