From 2eb2867c1387dbad432d62eaf514aee070986d49 Mon Sep 17 00:00:00 2001 From: EwdErna Date: Mon, 21 Sep 2026 08:52:27 +0900 Subject: [PATCH] =?UTF-8?q?IDX:=20=E3=82=AD=E3=83=BC=E7=95=AA=E5=8F=B7?= =?UTF-8?q?=E3=81=AE=E6=AD=A3=E6=9C=AC=E3=81=A8=E3=81=97=E3=81=A6IDX?= =?UTF-8?q?=E3=83=AC=E3=82=A4=E3=83=A4=E3=83=BC=E3=82=92=E7=A2=BA=E5=AE=9A?= =?UTF-8?q?=E3=81=97=E3=80=81MAP=E3=81=AE=E5=8F=82=E7=85=A7=E3=82=92PHY?= =?UTF-8?q?=E3=81=B8=E7=A7=BB=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit キー番号はPHYの幾何からもELCの配線からも導出できない決めごとなので、IDXが正本を持つ。 公開した基板のキー番号は他人のキーマップが依存する公開インターフェースであり、 出力のたびに生成し直さず固定する。 「最も重要な抽象化層」「最も欠けている層」という位置づけは誤りだったので落とした。 QMKのLAYOUTマクロの引数順、ZMKのmatrix transformのmap、KMKのcoord_mappingとして 既に存在していて、形式化されていないだけである。 あわせて2点。MAPはキー番号ではなくPHYのキーIDを参照する(IDXへの挿入でMAPがずれないため)。 IDXの保存形式は order: string[] とし、ELCのLEDChain.orderと同じく並びの位置を番号とする。 phase1-json-schema.md の P-1 を解消。 Journal: journal/2026-09-21-idx-necessity.md Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 2 + IDXレイヤー.md | 97 +++++++++++++++++++++++ journal/2026-09-21-idx-necessity.md | 115 ++++++++++++++++++++++++++++ keyboard-design-layers.md | 30 ++++---- keyboard-making-workflow.md | 9 ++- phase1-json-schema.md | 64 +++++++--------- 6 files changed, 263 insertions(+), 54 deletions(-) create mode 100644 IDXレイヤー.md create mode 100644 journal/2026-09-21-idx-necessity.md diff --git a/CLAUDE.md b/CLAUDE.md index a76476f..01d82bb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,6 +12,8 @@ PHY(物理配置) / ELC(電気配線) / IDX(キー識別) / MAP(キーマップ) |---|---| | `keyboard-design-layers.md` | レイヤー構造とツール分割方針(最上位) | | `PHYレイヤー.md` | PHY層の仕様 | +| `ELCレイヤー.md` | ELC層の仕様 | +| `IDXレイヤー.md` | IDX層の仕様 | | `phase1-json-schema.md` | 各層のJSON Schema | | `keyboard-making-workflow.md` | 既存の自作キーボード制作フローとの対応 | | `implementation-plan.md` | 実装方針の決定事項・検証用サンプル | diff --git a/IDXレイヤー.md b/IDXレイヤー.md new file mode 100644 index 0000000..df5ffd1 --- /dev/null +++ b/IDXレイヤー.md @@ -0,0 +1,97 @@ +# IDXレイヤー + +## 概要 + +IDXレイヤーは、PHYのキーに「キー番号」を与える。キー番号は、キーマップがキーを指すための識別子である。 + +キー番号は他のどの情報からも導出できない。設計者が決める並び順そのものであり、 +そのためIDXが正本として持つ。ELCのrow/colが「配線から決まるので出力時に導出する」のと、 +理由が裏返しの関係にある。 + +IDXが持つのは、番号を振る対象のキーと、その並び順だけとする。 + +## キー番号は公開インターフェース + +基板を公開すると、他人が書いたキーマップがそのキー番号に依存する。 +番号を振り直すと、既存のキーマップが別のキーを指す。 + +そのためキー番号は、出力のたびに生成し直す派生情報として扱わず、固定した正本として保存する。 + +## 幾何から導出しない + +PHYの座標・ツリー順をキー番号に使わない。 + +クラスターは幾何の編集単位であって論理的な行ではない。親指キーをまとめて回転させるために +クラスターへ出し入れすると、ツリー順が変わる。ツリー順が番号なら、そこでキーマップがずれる。 + +座標から行を判定する方法は、カラムスタッガードや曲線配列で破綻する。これはKLEの問題点として +`keyboard-design-layers.md` に挙げたものと同じ性質の話である。 + +## 配線から導出しない + +ELCのマトリクス位置をキー番号に使わない。 + +- マトリクスの並びとキー番号の並びは一致しない。分割キーボードでは、マトリクスは片方の半身の行を + 並べてからもう片方の行を並べるが、キー番号は視覚的な行ごとに左右を交互に取るのが通例である +- マトリクスには欠番がある。キー番号は実在するキーに対して連続だが、マトリクスは疎である +- 基板リビジョンでピンを差し替えたときに、キー番号が動いてはならない + +row / col 自体がELCの正本ではなく出力時の派生情報である。派生情報から番号を導くと二重に不安定になる。 + +## 出力先が要求する形 + +3つの処理系はいずれも「マトリクス位置の順序付きリスト」を要求し、その並びの位置をキー番号とする。 +どれも人手で書かれており、他の情報から導出されていない。Firmware Generatorはこれらを出力する。 + +| 処理系 | キー番号を決めているもの | +| --- | --- | +| QMK | `LAYOUT` マクロの引数順 / `keyboard.json` の `layouts..layout[]` の配列順 | +| ZMK | matrix transform の `map` の並び順 | +| KMK | `coord_mapping` の並び順 | + +## 他レイヤーとの関係 + +IDXはPHYのキーIDだけを参照する。ELCを参照しない。 + +MAPはIDXを参照しない。MAPもキーの参照にPHYのキーIDを使う。 +キーマップを配列として出力するとき、IDXの並び順で並べ替える。 +これにより、IDXにキーを挿入して番号がずれても、MAPは書き換えずに済む。 + +MAPが番号を持たないキーにキーコードを割り当てていた場合、そのキーは出力に現れない。 +IDXの `order` と突き合わせて検出できる。 + +## 持たないもの + +- マトリクス座標 (row / col)。ELCの配線から出力時に割り当てる +- LEDの識別番号。ELCが正本を持つ +- キーコード (MAPが持つ) +- キーの物理的な位置とサイズ (PHYが持つ) + +## 保存形式 + +```ts +type IDX = { + order: string[] // PHYのキーID。並びの位置がキー番号 +} +``` + +並びの位置を番号とする形は、ELCの `LEDChain.order` と同じ形である。 +欠番・重複・番号の飛びが構造として起こらない。 + +`order` に挙げたキーだけが番号を持つ。PHYの部品ノードと `decal` のキーは対象にならない。 + +## 検討済み・非採用 + +- PHYのツリー順をキー番号とする (journal/2026-09-21-idx-necessity.md) +- ELCのマトリクス順からキー番号を導出する (journal/2026-09-21-idx-necessity.md) +- `keyId` と `index` の対を並べる形 (journal/2026-09-21-idx-necessity.md) +- MAPがキー番号を参照する (journal/2026-09-21-idx-necessity.md) + +## 未確認事項 + +### P-1 レイアウトバリアントの扱い + +- 位置づけ: 未解決の問い。本文は単一レイアウトだけを前提にしている +- 現状の認識: QMKの `LAYOUT_60_ansi` / `LAYOUT_60_iso` のように、1つの基板が複数のキー番号空間を持つ場合がある。 + 1つのPHY+ELCに複数のIDXがぶら下がる形になりそうだが、PHYが代替キーをどう持つかが今のPHY仕様にない +- 格上げ条件: PHYの代替キーの表現とあわせて、マルチレイアウトの扱いをユーザーと決める diff --git a/journal/2026-09-21-idx-necessity.md b/journal/2026-09-21-idx-necessity.md new file mode 100644 index 0000000..ca7babf --- /dev/null +++ b/journal/2026-09-21-idx-necessity.md @@ -0,0 +1,115 @@ +# IDXレイヤーは必要か (schema P-1解消) + +## 結論 + +必要。ただし「最も重要な抽象化層」「エコシステムで最も欠けている層」という位置づけは誤りで、 +ドキュメントから落とす。 + +IDXの中身は「どのキーに、どの順で番号を振るか」という順列だけ。情報ではなく決めごとで、 +PHYからもELCからも導出できない。導出できないから正本として置く。 + +あわせて2点決めた。 + +- MAPはキー番号ではなくPHYのキーIDを参照する +- IDXの保存形式は `order: string[]` (並びの位置が番号) + +## 発端 + +ユーザーの指摘: 「PHYのidやELCの接続がそのままキー番号になってしまうのでは? +わざわざIDXを振る意味が薄れてきている」。 + +薄く見える原因は特定できた。IDXが持つと思われていたものが順次出ていったため。 + +| 元々IDXが持つとされたもの | 現在の置き場 | コミット | +| --- | --- | --- | +| row / col | ELCの配線から出力時に導出 | 5bba75d | +| LEDの識別番号 | ELC (チェーン順序) | eb96ff0 | + +引き算の結果、キー番号との対応だけが残った。薄いという認識自体は正しい。 + +## 案の比較 + +| 案 | キー番号の出どころ | 判定 | +| --- | --- | --- | +| IDXが正本として持つ | 設計者が決める並び順 | 採用 | +| PHYのツリー順 | 幾何の編集順 | reparentで番号が動きキーマップがずれる | +| ELCのマトリクス順 | row/col | 分割で並びが一致しない。欠番がある。再配線で動く | +| そもそも番号を持たない | (MAPがPHYのキーIDを参照) | 公開インターフェースが固定できない | + +最後の案が一番惜しかった。row/colを出力時に割り当てる判断と揃えるなら、キー番号も +Firmware Generatorが生成する派生情報にできそうに見える。しかし番号が再生成のたびに動くと、 +公開した基板に対して他人が書いたキーマップが別のキーを指す。公開インターフェースは固定が要る。 + +結果として、MAPの参照だけをPHYのキーIDに移し、番号の並びはIDXに残す形にした。 +番号がずれてもMAPは無傷で、番号は固定される。 + +## 根拠: 3つの処理系はどれもこの順列を手書きしている + +- QMK: `LAYOUT` マクロの引数順 / `keyboard.json` の `layouts..layout[]` の配列順 +- ZMK: matrix transform の `map` の並び順 +- KMK: `coord_mapping` の並び順 + +QMK Configuratorの説明: +> The Configurator's API uses the layout macro and the JSON file we've given it to create a visual +> representation of the keyboard that has each visual object tied to a specific key, in sequence. +> When a user selects the top-left key in the Configurator, and assigns Num Lock to it, the +> Configurator builds a keymap file with `KC_NUM` as the first key, and so on as the keymap is built. + +> The names themselves actually don't matter, as long as they match between the top section, which +> receives the keycodes from the keymap, and the bottom half which designates where each key is in the matrix. + +ZMKの matrix transform `map`: +> It should have one item per logical position in the keymap. Each item should list the physical row +> and column that trigger the key in that position. + +つまりIDXは「欠けている層」ではない。Cのマクロの引数順として存在していて、形式化されていないだけ。 +引数の数え間違いでキーが1つずれるのが定番のバグであることも、この層が手書きである傍証になる。 + +出典: +- https://docs.qmk.fm/reference_configurator_support +- https://docs.qmk.fm/reference_info_json +- https://zmk.dev/docs/config/layout +- https://github.com/KMKfw/kmk_firmware/blob/main/docs/en/porting_to_kmk.md + +## 根拠: マトリクス順 ≠ キー番号順 の実物 + +KMKのCorne向け `coord_mapping`。 + +```python +coord_mapping = [ + 0, 1, 2, 3, 4, 5, 24, 25, 26, 27, 28, 29, + 6, 7, 8, 9, 10, 11, 30, 31, 32, 33, 34, 35, +12, 13, 14, 15, 16, 17, 36, 37, 38, 39, 40, 41, + 21, 22, 23, 42, 43, 44, +] +``` + +配列の位置がキー番号、値がマトリクス上の通し番号。左半身が0〜23、右半身が24〜47。 +キー番号の並びは視覚的な行ごとに左右交互で、マトリクス順とは一致しない。18〜20は欠番。 +1つの例で「並びが違う」と「穴がある」の両方が出ている。 + +KMKの説明: +> Split keyboards count as the total keyboard, not per side, the right side being offset by the +> number of keys on the left side, as if the rows were stacked. + +QMKの分割も同じ構造。 +> Since the matrix scanning procedure operates on entire rows, it first populates the left half's rows, +> then the right half's. Thus, the matrix as QMK views it has double the rows instead of double the columns. + +出典: https://docs.qmk.fm/features/split_keyboard + +## 保存形式を `order: string[]` にした理由 + +旧案 `keyMap: [{keyId, index}]` は、欠番・重複・非連続をスキーマが許してしまう。 +`{k-a, 0}, {k-b, 0}` が通る。 + +ELCの `LEDChain.order` で既に「並びの位置が番号」の形を採っている (eb96ff0)。同じ形に揃えた。 +不変条件が構造として保証され、バリデーションが不要になる。 + +## 捨てた仮説 + +- 「IDXはdecal/ghostの区別を持つべきではないか」。decalはPHYが `decal` フラグを持っているので + そこから分かる。ghostは何を意味するかがKLE由来で曖昧なので、IDXの側で規定しない。 + `order` に挙げたキーだけが番号を持つ、で足りる +- 「VIAとの往復でキー番号が要るのでは」。VIAのプロトコルはrow/col指定なので、キー番号は出てこない。 + ただし裏取りしていないので、ドキュメントには書かなかった diff --git a/keyboard-design-layers.md b/keyboard-design-layers.md index 26a97b6..8335f4a 100644 --- a/keyboard-design-layers.md +++ b/keyboard-design-layers.md @@ -16,8 +16,8 @@ ## レイヤー構造 -レイヤーは一列に積み上がらない。ELCとIDXはそれぞれ独立にPHYを参照する。 -ELCはIDXを参照せず、IDXもELCを参照しない。 +レイヤーは一列に積み上がらない。ELC・IDX・MAPはそれぞれ独立にPHYを参照する。 +ELCはIDXを参照せず、IDXもELCを参照しない。MAPもIDXを参照しない。 他レイヤーからキーや部品を指す参照には、PHYのノードIDを使う。 ### 1. PHYレイヤー (物理配置) @@ -37,18 +37,22 @@ ELCはIDXを参照せず、IDXもELCを参照しない。 ### 3. IDXレイヤー (キー識別) -- **役割**: PHYのキーと"キー番号"の対応付け +- **役割**: PHYのキーに"キー番号"を与える - **入力**: PHYのキーID -- **重要性**: 物理世界と論理世界を繋ぐ最も重要な抽象化層 -- **出力**: キーIDとキー番号の対応 -- **備考**: 現状のエコシステムで最も欠けている層 +- **対応ツール**: QMKの `LAYOUT` マクロ、ZMKの matrix transform、KMKの `coord_mapping` +- **出力**: 番号を振るキーの並び。並びの位置がキー番号 +- **備考**: キー番号はPHYの幾何からもELCの配線からも導出しない(導出すると、幾何の編集や再配線でキーマップがずれる) +- **備考**: キー番号は公開インターフェースであり、出力のたびに生成し直さず固定する - **備考**: マトリックス座標とLEDの識別番号はIDXが持たない(どちらも配線で決まるためELCが持つ) +- **備考**: 既存のエコシステムでは、この並びが `LAYOUT` マクロの引数順などとして手書きされている。欠けている層ではなく、形式化されていない層 ### 4. MAPレイヤー (キーマップ) -- **役割**: キー番号とキーコードの対応 +- **役割**: キーとキーコードの対応 +- **入力**: PHYのキーID - **対応ツール**: ファームウェア、VIA、Remap -- **出力**: レイヤー別キーコード配列 +- **出力**: レイヤー別キーコード +- **備考**: キーの参照にキー番号を使わない。配列としての並べ替えは、出力時にIDXの並び順で行う ### 5. IMEレイヤー (入力変換) @@ -81,7 +85,7 @@ ELCはIDXを参照せず、IDXもELCを参照しない。 #### Keymap Designer -- キー番号のリストを入力 +- PHY JSONを入力 - レイヤー別キーコード割り当て - VIA/Remapとの互換性 - 出力: MAP JSON @@ -111,11 +115,11 @@ PHY → ELC PHY → IDX Input: キーID + 配置 - Output: キーIDとキー番号の対応 + Output: 番号を振るキーIDの並び -IDX → MAP - Input: キー番号のリスト - Output: レイヤー別キーコード配列 +PHY → MAP + Input: キーID + Output: レイヤー別キーコード All → Firmware Output: QMK keyboard.json / VIA JSON / KMK .py diff --git a/keyboard-making-workflow.md b/keyboard-making-workflow.md index 945c5c6..abe0ea4 100644 --- a/keyboard-making-workflow.md +++ b/keyboard-making-workflow.md @@ -57,9 +57,12 @@ - マトリックス定義(ROW/COL) - ピン配置設定 - 物理レイアウト定義 -- **現在の課題**: KLEとファームウェア設定の手動変換 + - キーマップ配列の並び順を決める(QMKなら `LAYOUT` マクロの引数順、ZMKなら matrix transform の + `map`、KMKなら `coord_mapping`) +- **現在の課題**: KLEとファームウェア設定の手動変換。並び順は手書きで、引数の数え間違いがそのまま + キーのずれになる - **Beyond KLE対応**: **PHY** + **IDX** + **ELC** → ファームウェア自動生成。 - ROW/COLはELCの配線から出力時に割り当てる + ROW/COLはELCの配線から出力時に割り当てる。並び順は**IDXレイヤー**が正本として持つ #### b) キーマップ設定 @@ -68,7 +71,7 @@ - 各キーへのキーコード割り当て - レイヤー設計 - マクロ・特殊機能の設定 -- **Beyond KLE対応**: **MAPレイヤー** +- **Beyond KLE対応**: **MAPレイヤー**。キーの参照にはPHYのキーIDを使い、配列への並べ替えは出力時にIDXの順で行う ### 4. 製造・組み立て段階 diff --git a/phase1-json-schema.md b/phase1-json-schema.md index 41c6462..e710b20 100644 --- a/phase1-json-schema.md +++ b/phase1-json-schema.md @@ -5,11 +5,11 @@ ```txt PHY (物理配置) ├─→ ELC (電気配線) - └─→ IDX (キー識別) - └─→ MAP (キーマップ) + ├─→ IDX (キー識別) + └─→ MAP (キーマップ) ``` -ELCとIDXはどちらもPHYを参照する。ELCはIDXを参照しない。 +ELC・IDX・MAPはどれもPHYを参照する。ELCはIDXを参照せず、MAPもIDXを参照しない。 ファームウェアやVIA/Remap向けの出力は、各レイヤーの情報を統合して生成する。 他レイヤーからキーや部品を指す参照には、PHYのノードIDを使う。 @@ -316,9 +316,10 @@ ELCとIDXはどちらもPHYを参照する。ELCはIDXを参照しない。 ## 2. IDXレイヤー JSON Schema -IDXが持つのは、PHYのキーとキー番号の対応だけとする [推測:P-1]。 -マトリックス座標(row/col)とLEDの識別番号はここに置かない。どちらも配線で決まる情報で、 -row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を持つ。 +各フィールドの意味は `IDXレイヤー.md` を正とする。 + +番号を振るキーの並びだけを持つ。並びの位置がキー番号になるため、欠番・重複・番号の飛びは +構造として起こらない。 ### 基本構造 @@ -327,9 +328,9 @@ row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を "$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 PHY keys and key numbers", + "description": "Ordered list of PHY keys; the position in the list is the key number", "type": "object", - "required": ["version", "phyReference", "keyMap"], + "required": ["version", "phyReference", "order"], "properties": { "version": { "type": "string" }, "phyReference": { @@ -340,27 +341,12 @@ row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を "checksum": { "type": "string" } } }, - "keyMap": { + "order": { "type": "array", + "uniqueItems": true, "items": { - "$ref": "#/$defs/keyMapping" - } - } - }, - "$defs": { - "keyMapping": { - "type": "object", - "required": ["keyId", "index"], - "properties": { - "keyId": { - "type": "string", - "description": "Reference to PHY layer key ID" - }, - "index": { - "type": "integer", - "minimum": 0, - "description": "Key number" - } + "type": "string", + "description": "Reference to PHY layer key ID" } } } @@ -518,6 +504,9 @@ row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を ## 4. MAPレイヤー JSON Schema +キーの参照にはPHYのキーIDを使う。キー番号は参照しない。 +配列としての並べ替えは、ファームウェア出力時にIDXの並び順で行う。 + ### 基本構造 ```json @@ -527,10 +516,10 @@ row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を "title": "MAP Layer - Keymap Configuration", "description": "Key code assignments and layers", "type": "object", - "required": ["version", "idxReference", "layers"], + "required": ["version", "phyReference", "layers"], "properties": { "version": { "type": "string" }, - "idxReference": { + "phyReference": { "type": "object", "required": ["source", "checksum"], "properties": { @@ -562,9 +551,12 @@ row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を }, "keyAssignment": { "type": "object", - "required": ["keyIndex", "keycode"], + "required": ["keyId", "keycode"], "properties": { - "keyIndex": { "type": "integer" }, + "keyId": { + "type": "string", + "description": "Reference to PHY layer key ID" + }, "keycode": { "type": "string" } } } @@ -577,7 +569,7 @@ row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を ### PHY → IDX変換 1. PHYレイヤーからキーのリストを取得 -2. 各キーにキー番号を割り当て +2. 番号を振るキーを選び、並び順を決める ### PHY → ELC変換 @@ -588,7 +580,7 @@ row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を ### IDX + ELC + MAP → ファームウェア出力 1. ELCの配線から、ターゲットが要求する row / col を割り当て -2. キー番号の配列とキーマップ配列の生成 +2. IDXの並び順に従って、MAPのキーコードを配列へ並べ替え 3. VIA/QMK形式への出力 ## 次のステップ @@ -600,8 +592,4 @@ row/col はELCの配線から導く派生情報、LEDの番号はELCが正本を ## 未確認事項 -### P-1 IDXがキー番号以外に持つものがあるか - -- 位置づけ: 本文「2. IDXレイヤー JSON Schema」が前提にしている。IDXには仕様ドキュメントがなく、 - 決定済みの情報(row/col と LEDの番号はELC)から引き算した最小形として書いている -- 格上げ条件: IDXレイヤーの仕様をユーザーと決め、`IDXレイヤー.md` に書く +(なし)