Files
Beyond-KLE/phase1-json-schema.md
T
ewdernaandClaude Opus 5 900dfff65e schema: PHYスキーマをPHYレイヤー.mdに追従させる
size に x2〜h2、key に switchOffset / partGroup / orientation /
perKeyLED / decal / ghost / labels を追加し、部品ノードを component
として定義して root と cluster.children に加えた。

key.metadata は廃止し、labels をトップレベルへ移した。残る見た目の
属性は保持する先の層が決まっていない(P-6)ため、構造化せず
kleCompatibility に不透明なまま置く。

PHYレイヤー.md 側の「スキーマが追従していない」注記を削除。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 14:31:47 +09:00

14 KiB

Phase 1: JSON Schema仕様策定

レイヤー間データフロー

PHY (物理配置) → IDX (キー識別) → ELC (電気配線) + MAP (キーマップ)
                                      ↓                    ↓
                               ファームウェア生成    VIA/Remap出力

1. PHYレイヤー JSON Schema

各フィールドの意味は PHYレイヤー.md を正とする。

基本構造

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://beyond-kle.org/schemas/phy-layer.json",
  "title": "PHY Layer - Physical Key Layout",
  "description": "Physical positioning and sizing of keys",
  "type": "object",
  "required": ["version", "metadata", "profiles", "root"],
  "properties": {
    "version": {
      "type": "string",
      "pattern": "^\\d+\\.\\d+\\.\\d+$",
      "description": "Schema version (semver)"
    },
    "metadata": {
      "type": "object",
      "properties": {
        "name": { "type": "string" },
        "author": { "type": "string" },
        "description": { "type": "string" },
        "created": { "type": "string", "format": "date-time" },
        "modified": { "type": "string", "format": "date-time" }
      }
    },
    "compatibility": {
      "type": "object",
      "description": "Information for external tool compatibility",
      "properties": {
        "kleJson": {
          "type": "object",
          "properties": {
            "source": { "type": "string" },
            "checksum": { "type": "string" },
            "preservedFields": { "type": "array", "items": { "type": "string" } }
          }
        },
        "originalFormat": {
          "type": "string",
          "enum": ["kle", "via", "qmk", "native"]
        }
      }
    },
    "profiles": {
      "type": "array",
      "minItems": 1,
      "items": {
        "$ref": "#/$defs/profile"
      }
    },
    "root": {
      "type": "array",
      "items": {
        "oneOf": [
          { "$ref": "#/$defs/key" },
          { "$ref": "#/$defs/cluster" },
          { "$ref": "#/$defs/component" }
        ]
      }
    }
  },
  "$defs": {
    "transform": {
      "type": "object",
      "description": "Placement relative to the parent: rotate by rotation around the local origin, then translate by (x, y)",
      "required": ["x", "y", "rotation"],
      "properties": {
        "x": { "type": "number" },
        "y": { "type": "number" },
        "rotation": { "type": "number", "description": "Degrees" }
      }
    },
    "profile": {
      "type": "object",
      "required": ["id", "name", "unitSize"],
      "properties": {
        "id": { "type": "string" },
        "name": { "type": "string" },
        "unitSize": {
          "type": "object",
          "required": ["x", "y"],
          "properties": {
            "x": { "type": "number", "minimum": 0 },
            "y": { "type": "number", "minimum": 0 }
          }
        },
        "visualOffset": {
          "type": "object",
          "properties": {
            "x": { "type": "number" },
            "y": { "type": "number" },
            "radius": { "type": "number", "minimum": 0 }
          }
        }
      }
    },
    "key": {
      "type": "object",
      "required": ["type", "id", "profile", "transform", "size"],
      "properties": {
        "type": {
          "type": "string",
          "const": "key"
        },
        "id": {
          "type": "string",
          "description": "Unique key identifier"
        },
        "profile": {
          "type": "string",
          "description": "Reference to profile ID"
        },
        "transform": { "$ref": "#/$defs/transform" },
        "size": {
          "type": "object",
          "description": "Local size. Not affected by the parent transform",
          "required": ["w", "h"],
          "properties": {
            "w": { "type": "number", "minimum": 0 },
            "h": { "type": "number", "minimum": 0 },
            "x2": { "type": "number", "description": "Second rectangle for non-rectangular keys, relative to the local origin" },
            "y2": { "type": "number" },
            "w2": { "type": "number", "minimum": 0 },
            "h2": { "type": "number", "minimum": 0 }
          }
        },
        "switchOffset": {
          "type": "object",
          "description": "Switch position, offset from the center of the (w, h) rectangle. Key local coordinates, unit U. Defaults to 0",
          "required": ["x", "y"],
          "properties": {
            "x": { "type": "number" },
            "y": { "type": "number" }
          }
        },
        "partGroup": {
          "type": "string",
          "description": "Label grouping keys that use the same part. Not interpreted by Beyond KLE"
        },
        "orientation": {
          "type": "integer",
          "enum": [0, 90, 180, 270],
          "default": 0,
          "description": "Switch orientation in the key local coordinates"
        },
        "perKeyLED": {
          "type": "boolean",
          "default": false,
          "description": "Whether a per-key LED is mounted"
        },
        "decal": { "type": "boolean", "default": false },
        "ghost": { "type": "boolean", "default": false },
        "labels": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Reference information. Used for matching on KLE JSON import"
        },
        "kleCompatibility": {
          "type": "object",
          "description": "Opaque KLE fields kept for round-trip output (c, t, p, a, f, f2, fa, l, n, sm, sb, st). The owning layer is undecided; see P-6 in PHYレイヤー.md",
          "additionalProperties": true
        }
      }
    },
    "component": {
      "type": "object",
      "description": "Non-key part placed by the designer (LED, OLED, encoder, screw hole, ...)",
      "required": ["type", "id", "transform"],
      "properties": {
        "type": {
          "type": "string",
          "const": "component"
        },
        "id": { "type": "string" },
        "transform": { "$ref": "#/$defs/transform" },
        "partGroup": {
          "type": "string",
          "description": "Names starting with @ are reserved for editor rendering"
        }
      }
    },
    "cluster": {
      "type": "object",
      "required": ["type", "id", "name", "transform", "children"],
      "properties": {
        "type": {
          "type": "string",
          "const": "cluster"
        },
        "id": { "type": "string" },
        "name": { "type": "string" },
        "transform": { "$ref": "#/$defs/transform" },
        "children": {
          "type": "array",
          "items": {
            "oneOf": [
              { "$ref": "#/$defs/key" },
              { "$ref": "#/$defs/cluster" },
              { "$ref": "#/$defs/component" }
            ]
          }
        }
      }
    }
  }
}

使用例

{
  "version": "1.0.0",
  "metadata": {
    "name": "Split Keyboard Layout",
    "author": "John Doe",
    "description": "Ergonomic split keyboard with rotated thumb clusters",
    "created": "2025-01-01T00:00:00Z"
  },
  "profiles": [
    {
      "id": "standard",
      "name": "Standard Cherry MX",
      "unitSize": { "x": 19.05, "y": 19.05 },
      "visualOffset": { "x": 1, "y": 1, "radius": 1 }
    }
  ],
  "root": [
    {
      "type": "cluster",
      "id": "left_main",
      "name": "Left Main Block",
      "transform": { "x": 0, "y": 0, "rotation": 0 },
      "children": [
        {
          "type": "key",
          "id": "k00",
          "profile": "standard",
          "transform": { "x": 0, "y": 0, "rotation": 0 },
          "size": { "w": 1, "h": 1 },
          "partGroup": "main",
          "perKeyLED": true,
          "labels": ["Q"]
        },
        {
          "type": "key",
          "id": "k01",
          "profile": "standard",
          "transform": { "x": 1, "y": 0, "rotation": 0 },
          "size": { "w": 1, "h": 1 },
          "partGroup": "main",
          "perKeyLED": true,
          "labels": ["W"]
        }
      ]
    },
    {
      "type": "cluster",
      "id": "left_thumb",
      "name": "Left Thumb Cluster",
      "transform": { "x": 2, "y": 3, "rotation": 15 },
      "children": [
        {
          "type": "key",
          "id": "k30",
          "profile": "standard",
          "transform": { "x": 0, "y": 0, "rotation": 0 },
          "size": { "w": 1.25, "h": 1 },
          "partGroup": "thumb",
          "orientation": 180,
          "labels": ["Space"]
        },
        {
          "type": "key",
          "id": "k31",
          "profile": "standard",
          "transform": { "x": 1.25, "y": 0, "rotation": 0 },
          "size": { "w": 1, "h": 1 },
          "partGroup": "thumb",
          "orientation": 180,
          "labels": ["Enter"]
        },
        {
          "type": "component",
          "id": "enc0",
          "transform": { "x": 2.25, "y": 0.5, "rotation": 0 },
          "partGroup": "encoder"
        }
      ]
    },
    {
      "type": "key",
      "id": "k_iso_enter",
      "profile": "standard",
      "transform": { "x": 6, "y": 1, "rotation": 0 },
      "size": { "w": 1.5, "h": 1, "x2": 0.25, "y2": 1, "w2": 1.25, "h2": 1 },
      "switchOffset": { "x": 0, "y": 0.5 },
      "labels": ["Enter"]
    },
    {
      "type": "component",
      "id": "screw0",
      "transform": { "x": 0, "y": 4.5, "rotation": 0 },
      "partGroup": "screw-m2"
    }
  ]
}

2. IDXレイヤー JSON Schema

基本構造

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://beyond-kle.org/schemas/idx-layer.json",
  "title": "IDX Layer - Key Identification",
  "description": "Mapping between physical keys and logical identifiers",
  "type": "object",
  "required": ["version", "phyReference", "keyMap"],
  "properties": {
    "version": { "type": "string" },
    "phyReference": {
      "type": "object",
      "required": ["source", "checksum"],
      "properties": {
        "source": { "type": "string" },
        "checksum": { "type": "string" }
      }
    },
    "keyMap": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/keyMapping"
      }
    }
  },
  "$defs": {
    "keyMapping": {
      "type": "object",
      "required": ["physicalId", "logicalId"],
      "properties": {
        "physicalId": {
          "type": "string",
          "description": "Reference to PHY layer key ID"
        },
        "logicalId": {
          "type": "object",
          "required": ["index"],
          "properties": {
            "index": { "type": "integer", "minimum": 0 },
            "matrix": {
              "type": "object",
              "properties": {
                "row": { "type": "integer", "minimum": 0 },
                "col": { "type": "integer", "minimum": 0 }
              }
            },
            "label": { "type": "string" }
          }
        }
      }
    }
  }
}

3. ELCレイヤー JSON Schema

基本構造

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://beyond-kle.org/schemas/elc-layer.json",
  "title": "ELC Layer - Electrical Configuration",
  "description": "Electrical wiring and pin mapping",
  "type": "object",
  "required": ["version", "idxReference", "microcontroller", "matrix"],
  "properties": {
    "version": { "type": "string" },
    "idxReference": {
      "type": "object",
      "required": ["source", "checksum"],
      "properties": {
        "source": { "type": "string" },
        "checksum": { "type": "string" }
      }
    },
    "microcontroller": {
      "type": "object",
      "required": ["type", "pinMapping"],
      "properties": {
        "type": { "type": "string" },
        "pinMapping": {
          "type": "object",
          "properties": {
            "rows": {
              "type": "array",
              "items": { "type": "string" }
            },
            "cols": {
              "type": "array", 
              "items": { "type": "string" }
            }
          }
        }
      }
    },
    "matrix": {
      "type": "object",
      "required": ["type", "dimensions"],
      "properties": {
        "type": {
          "type": "string",
          "enum": ["standard", "charlieplexing", "direct"]
        },
        "dimensions": {
          "type": "object",
          "required": ["rows", "cols"],
          "properties": {
            "rows": { "type": "integer", "minimum": 1 },
            "cols": { "type": "integer", "minimum": 1 }
          }
        }
      }
    }
  }
}

4. MAPレイヤー JSON Schema

基本構造

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://beyond-kle.org/schemas/map-layer.json", 
  "title": "MAP Layer - Keymap Configuration",
  "description": "Key code assignments and layers",
  "type": "object",
  "required": ["version", "idxReference", "layers"],
  "properties": {
    "version": { "type": "string" },
    "idxReference": {
      "type": "object",
      "required": ["source", "checksum"],
      "properties": {
        "source": { "type": "string" },
        "checksum": { "type": "string" }
      }
    },
    "layers": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/layer"
      }
    }
  },
  "$defs": {
    "layer": {
      "type": "object",
      "required": ["id", "name", "keymap"],
      "properties": {
        "id": { "type": "integer", "minimum": 0 },
        "name": { "type": "string" },
        "keymap": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/keyAssignment"
          }
        }
      }
    },
    "keyAssignment": {
      "type": "object",
      "required": ["keyIndex", "keycode"],
      "properties": {
        "keyIndex": { "type": "integer" },
        "keycode": { "type": "string" }
      }
    }
  }
}

変換フロー設計

PHY → IDX変換

  1. PHYレイヤーから物理キーリストを取得
  2. 各キーに論理IDを割り当て
  3. マトリックス座標の自動生成(行列最適化)

IDX → ELC変換

  1. マトリックス次元を算出
  2. ピン配置の最適化提案
  3. 配線効率の検証

IDX + ELC → MAP変換

  1. キーインデックス配列の生成
  2. デフォルトキーマップの提案
  3. VIA/QMK形式への出力

次のステップ

  1. TypeScriptでの型定義作成
  2. 変換ロジックのプロトタイプ実装
  3. テストケース(60%、Split等)での検証
  4. 既存KLE JSONからの変換機能