排他キーも別々の要素として並べる。出力はバリアントを選択した構成を単位とし、
対象外のキーを除いた並びの位置をそのままキー番号にする。同じ配線の排他キーは
選択後に同時には残らないため、番号を共有させる必要がない。
保存したorderは選択で書き換えず、番号の互換性は出力レイアウトごとに扱う。
6d0eba9 で「IDXは番号を共有できる」と書いたが、共有をやめる形に変える。
コミュニティレイアウトの並びを取捨で得られるかは、この方式では未検証のため
IDXのP-1として残す。
Journal: journal/2026-09-22-idx-variant-output.md
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DKW58HEg8zAQoLrkWByPfD
708 lines
21 KiB
Markdown
708 lines
21 KiB
Markdown
# Phase 1: JSON Schema仕様策定
|
|
|
|
## レイヤー間データフロー
|
|
|
|
```txt
|
|
PHY (物理配置)
|
|
├─→ ELC (電気配線)
|
|
├─→ IDX (キー識別)
|
|
└─→ MAP (キーマップ)
|
|
```
|
|
|
|
ELC・IDX・MAPはどれもPHYを参照する。ELCはIDXを参照せず、MAPもIDXを参照しない。
|
|
ファームウェアやVIA/Remap向けの出力は、各レイヤーの情報を統合して生成する。
|
|
他レイヤーからキーや部品を指す参照には、PHYのノードIDを使う。
|
|
|
|
## 1. PHYレイヤー JSON Schema
|
|
|
|
各フィールドの意味は `PHYレイヤー.md` を正とする。
|
|
|
|
### 基本構造
|
|
|
|
```json
|
|
{
|
|
"$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" }
|
|
}
|
|
},
|
|
"kleCompatibility": {
|
|
"type": "object",
|
|
"description": "File-level opaque KLE fields kept for round-trip output (switchMount, switchBrand, switchType, backcolor, radii, background, css). The owning layer is undecided; see P-6 in PHYレイヤー.md",
|
|
"additionalProperties": true
|
|
},
|
|
"profiles": {
|
|
"type": "array",
|
|
"minItems": 1,
|
|
"items": {
|
|
"$ref": "#/$defs/profile"
|
|
}
|
|
},
|
|
"root": {
|
|
"type": "array",
|
|
"items": {
|
|
"oneOf": [
|
|
{ "$ref": "#/$defs/key" },
|
|
{ "$ref": "#/$defs/cluster" },
|
|
{ "$ref": "#/$defs/component" },
|
|
{ "$ref": "#/$defs/variantGroup" }
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"$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",
|
|
"description": "Millimeters per 1U",
|
|
"required": ["x", "y"],
|
|
"properties": {
|
|
"x": { "type": "number", "minimum": 0 },
|
|
"y": { "type": "number", "minimum": 0 }
|
|
}
|
|
},
|
|
"visualOffset": {
|
|
"type": "object",
|
|
"description": "Key top rectangle, in millimeters: inward distance from each edge of the outline, plus corner radius",
|
|
"required": ["top", "right", "bottom", "left", "radius"],
|
|
"properties": {
|
|
"top": { "type": "number" },
|
|
"right": { "type": "number" },
|
|
"bottom": { "type": "number" },
|
|
"left": { "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"
|
|
}
|
|
}
|
|
},
|
|
"variantGroup": {
|
|
"type": "object",
|
|
"description": "Mutually exclusive layout alternatives. Children are variants only",
|
|
"required": ["type", "id", "name", "transform", "default", "children"],
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"const": "variantGroup"
|
|
},
|
|
"id": { "type": "string" },
|
|
"name": {
|
|
"type": "string",
|
|
"description": "Heading shown for the choice in the editor"
|
|
},
|
|
"transform": { "$ref": "#/$defs/transform" },
|
|
"default": {
|
|
"type": "string",
|
|
"description": "id of the variant used when a single layout must be chosen"
|
|
},
|
|
"children": {
|
|
"type": "array",
|
|
"minItems": 2,
|
|
"description": "Display order",
|
|
"items": { "$ref": "#/$defs/variant" }
|
|
}
|
|
}
|
|
},
|
|
"variant": {
|
|
"type": "object",
|
|
"description": "One alternative. Appears only directly under a variantGroup. Carries no transform",
|
|
"required": ["type", "id", "name", "children"],
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"const": "variant"
|
|
},
|
|
"id": { "type": "string" },
|
|
"name": { "type": "string" },
|
|
"children": {
|
|
"type": "array",
|
|
"description": "May be empty",
|
|
"items": {
|
|
"oneOf": [
|
|
{ "$ref": "#/$defs/key" },
|
|
{ "$ref": "#/$defs/cluster" },
|
|
{ "$ref": "#/$defs/component" },
|
|
{ "$ref": "#/$defs/variantGroup" }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"cluster": {
|
|
"type": "object",
|
|
"required": ["type", "id", "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" },
|
|
{ "$ref": "#/$defs/variantGroup" }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 使用例
|
|
|
|
```json
|
|
{
|
|
"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": { "top": 3, "right": 3, "bottom": 3, "left": 3, "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": "variantGroup",
|
|
"id": "vg_enter",
|
|
"name": "Enter",
|
|
"transform": { "x": 6, "y": 0, "rotation": 0 },
|
|
"default": "v_ansi",
|
|
"children": [
|
|
{
|
|
"type": "variant",
|
|
"id": "v_ansi",
|
|
"name": "ANSI",
|
|
"children": [
|
|
{
|
|
"type": "key",
|
|
"id": "k_ansi_bslash",
|
|
"profile": "standard",
|
|
"transform": { "x": 1, "y": 0, "rotation": 0 },
|
|
"size": { "w": 1.5, "h": 1 },
|
|
"labels": ["|", "\\"]
|
|
},
|
|
{
|
|
"type": "key",
|
|
"id": "k_ansi_enter",
|
|
"profile": "standard",
|
|
"transform": { "x": 0.25, "y": 1, "rotation": 0 },
|
|
"size": { "w": 2.25, "h": 1 },
|
|
"labels": ["Enter"]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "variant",
|
|
"id": "v_iso",
|
|
"name": "ISO",
|
|
"children": [
|
|
{
|
|
"type": "key",
|
|
"id": "k_iso_enter",
|
|
"profile": "standard",
|
|
"transform": { "x": 1, "y": 0, "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": "key",
|
|
"id": "k_iso_hash",
|
|
"profile": "standard",
|
|
"transform": { "x": 0.25, "y": 1, "rotation": 0 },
|
|
"size": { "w": 1, "h": 1 },
|
|
"labels": ["~", "#"]
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"type": "component",
|
|
"id": "screw0",
|
|
"transform": { "x": 0, "y": 4.5, "rotation": 0 },
|
|
"partGroup": "screw-m2"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## 2. IDXレイヤー JSON Schema
|
|
|
|
各フィールドの意味は `IDXレイヤー.md` を正とする。
|
|
|
|
全バリアントの対象キーIDを、重複なく1つの並びに持つ。同じ配線を使う排他キーも別々の要素とする。
|
|
出力時は選択したバリアントで使わないキーを除き、残った並びの位置を0から連続するキー番号とする。
|
|
保存した `order` は変更しない。
|
|
|
|
### 基本構造
|
|
|
|
```json
|
|
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://beyond-kle.org/schemas/idx-layer.json",
|
|
"title": "IDX Layer - Key Identification",
|
|
"description": "Ordered PHY key IDs across all variants. Output key numbers are contiguous positions after filtering by variant selection",
|
|
"type": "object",
|
|
"required": ["version", "phyReference", "order"],
|
|
"properties": {
|
|
"version": { "type": "string" },
|
|
"phyReference": {
|
|
"type": "object",
|
|
"required": ["source", "checksum"],
|
|
"properties": {
|
|
"source": { "type": "string" },
|
|
"checksum": { "type": "string" }
|
|
}
|
|
},
|
|
"order": {
|
|
"type": "array",
|
|
"uniqueItems": true,
|
|
"description": "Each PHY key has its own entry, including mutually exclusive keys wired to the same terminals. Variant selection does not modify this list",
|
|
"items": {
|
|
"type": "string",
|
|
"description": "Reference to PHY layer key ID"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## 3. ELCレイヤー JSON Schema
|
|
|
|
各フィールドの意味は `ELCレイヤー.md` を正とする。
|
|
|
|
### 基本構造
|
|
|
|
```json
|
|
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://beyond-kle.org/schemas/elc-layer.json",
|
|
"title": "ELC Layer - Electrical Wiring",
|
|
"description": "Wiring between provider pins and key terminals",
|
|
"type": "object",
|
|
"required": ["version", "phyReference", "providers", "keys"],
|
|
"properties": {
|
|
"version": { "type": "string" },
|
|
"phyReference": {
|
|
"type": "object",
|
|
"required": ["source", "checksum"],
|
|
"properties": {
|
|
"source": { "type": "string" },
|
|
"checksum": { "type": "string" }
|
|
}
|
|
},
|
|
"providers": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/provider" }
|
|
},
|
|
"keys": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/keyWiring" }
|
|
},
|
|
"ledChains": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/ledChain" }
|
|
}
|
|
},
|
|
"$defs": {
|
|
"pinRef": {
|
|
"type": "string",
|
|
"pattern": "^(VCC|GND|[^.]+\\.[^.]+)$",
|
|
"description": "\"<provider id>.<pin name>\", or the reserved power rails VCC / GND"
|
|
},
|
|
"provider": {
|
|
"oneOf": [
|
|
{ "$ref": "#/$defs/mcuProvider" },
|
|
{ "$ref": "#/$defs/analogMuxProvider" }
|
|
]
|
|
},
|
|
"mcuProvider": {
|
|
"type": "object",
|
|
"required": ["id", "kind", "part"],
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"kind": { "const": "mcu" },
|
|
"part": {
|
|
"type": "string",
|
|
"description": "Part number. Used to interpret pin names"
|
|
}
|
|
}
|
|
},
|
|
"analogMuxProvider": {
|
|
"type": "object",
|
|
"required": ["id", "kind", "channels", "select", "common"],
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"kind": { "const": "analog-mux" },
|
|
"channels": {
|
|
"type": "integer",
|
|
"minimum": 1,
|
|
"description": "Number of logical pins provided. Pin names are \"0\"..\"channels-1\""
|
|
},
|
|
"select": {
|
|
"type": "array",
|
|
"items": { "$ref": "#/$defs/pinRef" },
|
|
"description": "Select pins, least significant bit first"
|
|
},
|
|
"common": { "$ref": "#/$defs/pinRef" },
|
|
"enable": { "$ref": "#/$defs/pinRef" }
|
|
}
|
|
},
|
|
"keyWiring": {
|
|
"oneOf": [
|
|
{ "$ref": "#/$defs/contactKey" },
|
|
{ "$ref": "#/$defs/analogKey" }
|
|
]
|
|
},
|
|
"contactKey": {
|
|
"type": "object",
|
|
"required": ["key", "detect", "terminals"],
|
|
"properties": {
|
|
"key": {
|
|
"type": "string",
|
|
"description": "Reference to PHY layer key ID"
|
|
},
|
|
"detect": { "const": "contact" },
|
|
"terminals": {
|
|
"type": "object",
|
|
"description": "Non-polar. a and b carry no ordering meaning",
|
|
"required": ["a", "b"],
|
|
"properties": {
|
|
"a": { "$ref": "#/$defs/pinRef" },
|
|
"b": { "$ref": "#/$defs/pinRef" }
|
|
}
|
|
},
|
|
"diode": {
|
|
"type": "string",
|
|
"enum": ["a-to-b", "b-to-a"],
|
|
"description": "Anode side comes first. Omitted means no diode"
|
|
}
|
|
}
|
|
},
|
|
"analogKey": {
|
|
"type": "object",
|
|
"required": ["key", "detect", "terminals"],
|
|
"properties": {
|
|
"key": {
|
|
"type": "string",
|
|
"description": "Reference to PHY layer key ID"
|
|
},
|
|
"detect": { "const": "analog" },
|
|
"terminals": {
|
|
"type": "object",
|
|
"required": ["out", "vcc", "gnd"],
|
|
"properties": {
|
|
"out": { "$ref": "#/$defs/pinRef" },
|
|
"vcc": { "$ref": "#/$defs/pinRef" },
|
|
"gnd": { "$ref": "#/$defs/pinRef" }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"ledChain": {
|
|
"type": "object",
|
|
"required": ["id", "data", "order"],
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"data": { "$ref": "#/$defs/pinRef" },
|
|
"order": {
|
|
"type": "array",
|
|
"items": { "type": "string" },
|
|
"description": "PHY node IDs. The position in the array is the LED number"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## 4. MAPレイヤー JSON Schema
|
|
|
|
キーの参照にはPHYのキーIDを使う。キー番号は参照しない。
|
|
配列としての並べ替えは、ファームウェア出力時にバリアント選択後のIDXの並び順で行う。
|
|
キー番号と接続先の対応表も同じ並びで生成し、MAPの保存データは変更しない。
|
|
|
|
### 基本構造
|
|
|
|
```json
|
|
{
|
|
"$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", "phyReference", "layers"],
|
|
"properties": {
|
|
"version": { "type": "string" },
|
|
"phyReference": {
|
|
"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": ["keyId", "keycode"],
|
|
"properties": {
|
|
"keyId": {
|
|
"type": "string",
|
|
"description": "Reference to PHY layer key ID"
|
|
},
|
|
"keycode": { "type": "string" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## 変換フロー設計
|
|
|
|
### PHY → IDX変換
|
|
|
|
1. PHYレイヤーから全バリアントのキーのリストを取得
|
|
2. 番号を振る対象キーを1つの `order` に重複なく並べる。同じ配線の排他キーも別々の要素にする
|
|
|
|
### PHY → ELC変換
|
|
|
|
1. ピン提供者を置き、各提供者が消費する上位ピンを指定
|
|
2. 各キーの端子の接続先を指定
|
|
3. LEDチェーンの並びを指定
|
|
|
|
### IDX + ELC + MAP → ファームウェア出力
|
|
|
|
1. PHYのVariantGroupごとにバリアントを選び、共通キーと、祖先を含めて選択されたバリアントのキーを特定
|
|
2. IDXの `order` から対象外のキーを除き、残った順序のまま出力用のキー番号を0から連続して割り当て
|
|
3. ELCの配線から、ターゲットが要求する row / col を割り当て、手順2の並びでキー番号との対応表を生成
|
|
4. 同じ並びでMAPのキーコードを配列へ並べ替え、VIA/QMK形式へ出力
|
|
|
|
保存したPHY・IDX・ELC・MAPは書き換えない。ELCのLEDチェーンの並び・番号は、このキーの取捨では変わらない。
|
|
|
|
## 次のステップ
|
|
|
|
1. **型定義の作成**(言語は `implementation-plan.md` で選定)
|
|
2. **変換ロジックのプロトタイプ実装**
|
|
3. **テストケース(60%、Split等)での検証**
|
|
4. **既存KLE JSONからの変換機能**
|
|
|
|
## 未確認事項
|
|
|
|
(なし)
|