Files
Beyond-KLE/provisional/decisions.md
T
ewdernaandClaude Opus 5.5 e5678d79e6 mock: クラスターを選んでキーを足すと、そのクラスターに新しい行を作る(T-30 単位6)
これまではクラスターを1つのノードとして扱い、その右隣(親の中)に足していた。
クラスターの中に新しい行を作り、その左端に置く。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XsamNKrWY3obJwR746oQRq
2026-10-23 08:18:22 +09:00

267 lines
20 KiB
Markdown

# 仮決定
各項目: 仮決定 / 理由(短く) / 覆すときに影響する箇所。
「モック対象外」は、未決のまま進めてもモックが作れるという意味で、問いが解決したことを意味しない。
## A. モックの範囲と技術
### T-1 モックの目的
配置を編集しても割り当てがずれないこと、全バリアントを保持したまま選択した構成を出力・往復できることを、動くもので確かめる。
完成品のツールにはしない。
### T-2 対象範囲
ブラウザで動く1つのアプリ。1画面でタブを切り替える。
| ツール | 作る範囲 |
| --- | --- |
| PHY Editor | KLE JSONの新規取り込み、キー・部品の追加、移動・回転・サイズ編集、クラスター化・解除・reparent・数値入力のreframe、バリアントの編集・表示切替、KLE JSON出力 |
| Key Indexer | 全バリアントのキーの一覧、`order` への登録・除外・並べ替え、選択構成での出力番号のプレビュー |
| Wiring Editor | MCUの提供者と接点キーの端子のフォーム編集、配線の表示、未配線・衝突の表示、LEDチェーンの順序編集 |
| Keymap Designer | 基本キーコードと名前によるレイヤー動作の割り当て、レイヤーの追加・改名・並べ替え、未割り当ての表示 |
| Generator(プレビュー) | 選択構成の平坦な配置、IDXの並び、row/col、MAPの配列、対応表を `mock-target.json` に出力。QMKにそのまま渡せる生成物とは扱わない |
| 共通 | プロジェクトの保存・読み込み、Undo/Redo、検証結果の一覧 |
対象外: 実機との通信(VIA/WebHID)、QMKのビルド・書き込み、HST、アナログ入力、マルチプレクサ、光学スイッチ、エンコーダ等への割り当て、マクロ・タップダンス等の動作定義。
後段(任意): 疑似デバイスとの往復(書き込み → 外部での変更を模擬 → 読み出し → 差分確認 → MAPへ取り込み)。着手する場合は T-24 に従う。
### T-3 作る順番
1. 回転したクラスター内のキーのドラッグと、保存・再読み込みの往復(描画方式の確認を兼ねる)
2. IDX・MAPとの連携
3. ELCと出力の対応表
4. (任意)疑似デバイスとの往復
### T-4 検証に使うデータ
`samples/kle/macropad-4x4.json`、`samples/kle/cr0wn60.json`(幾何のみ)、手作りの回転・入れ子バリアントのfixture。
実在の配線とVIAの適合は検証の対象にしない。
### T-5 技術選定 (implementation-plan P-2)
| 項目 | 仮決定 |
| --- | --- |
| 言語 | TypeScript(strict) |
| UI | Vue 3(Composition API) + Vite。Nuxtは使わない(SSR・ルーティングが不要) |
| 描画 | 素のSVG。PHYの階層を入れ子の `<g>` に対応させ、ドラッグは親要素の `getScreenCTM()` の逆行列でローカル座標へ戻す |
| 状態管理 | 文書全体を1つの `shallowRef` に置き、純粋な更新関数(コマンド)で置き換える。UIの状態は別に持つ。Piniaは入れない |
| Schema検証 | Ajv(draft 2020-12)。参照の整合性などの意味的な検証は別の関数にする |
| テスト | Vitest(変換・参照整合性)、Playwright(SVG上の操作・保存の往復) |
| パッケージ管理 | pnpm。依存の版はlockfileで固定 |
理由: 描画の変換がPHYの `Translate · Rotate` と同じ定義で、座標の逆変換をブラウザに任せられる。Vueは既存の経験を使える。
### T-6 リポジトリ構成 (implementation-plan P-3)
このリポジトリの `mock/` に単一のパッケージとして置く。
中は `domain/`(層のモデル・変換。Vue・DOMを参照しない)、`io/`(KLE・プロジェクトファイル)、`ui/` に分ける。
モノレポ化とパッケージの分割はモックの後に決める。
### T-7 KLE JSONの読み書き (implementation-plan P-1)
kle-serial2 を使わず、読み書きともに自作する。ijprest/keyboard-layout-editor・kle-serial・過去のkbd-editorのコードも取り込まない。
KLE本家の挙動の確認は、既存の検証ハーネスで本家の `serial.js` を動かして比べる(コードは写さない)。
理由: 出自の確認を待たずに進められ、後からライブラリへ差し替えられる。
### T-29 PHY Editorの作り直し
`ui/phy/` だけを作り直し、`domain/`・`io/` とそのテストは流用する。作り直したものもモックとして扱う。
入力は [phy-editor-feedback.md](phy-editor-feedback.md) と [phy-editor-keybindings.md](phy-editor-keybindings.md)。
理由: 所感はすべて画面の操作と表示に関するもので、データモデル側に作り直す理由がない。
### T-30 作り直し1回目の所感への対応
T-29 のコードを作り直さず、[phy-editor-feedback.md](phy-editor-feedback.md)「作り直し1回目(T-29)を触った所感」を下の単位で順に直す。
1単位1コミットとし、各単位で既存の Vitest・Playwright を通してから次へ進む。
理由: 所感はどれも局所的な修正か機能の追加で、T-29 の構成(composable・純粋モジュールの分離)のまま入る。
| 順 | 単位 | 内容 | 主な箇所 |
| --- | --- | --- | --- |
| 1 | 選択の描画層 | 選択枠を太くし、`NodeSvg` の再帰から外して最前面の層に描く。以後のオーバーレイ(5・8)もこの層に載せる | `NodeSvg.vue`、`PhyEditor.vue` |
| 2 | パネルの幅 | 階層パネルを閉じたら、キャンバスが空いた幅を埋める | `PhyEditor.vue` の `grid-template-columns` |
| 3 | バリアント | ポップオーバーの印を「既定」と分かる見た目にする。バッジには名前を書かず、表示中のバリアント名はツールチップで示す。クラスター内のキーから作ったバリアントグループは、そのクラスターの中に作る | `NodeSvg.vue`、`usePhyCommands.ts` |
| 4 | ドラッグ中の自動スクロール | キーを画面の端へ寄せると、キャンバスがスクロールする | `usePhyCanvasPointer.ts`、`usePhyCanvasView.ts` |
| 5 | クラスターの原点 | 選択中のクラスターにだけ、原点に丸を出す | 1の層 |
| 6 | キー追加の位置 | 選択中のキー(複数なら最後に選んだキー)の行の右端に追加する。何も選んでいなければ、同じコンテナの全キーの下に新しい行を作り、その左端に置く。クラスターを選んでいれば、そのクラスターの中に同じように新しい行を作る。行の判定は `domain/` に純粋関数として実装してテストし、のちにIDX Editorでも使う。規則は [idx-editor-feedback.md](idx-editor-feedback.md)「行と列の判定」 | `domain/rows.ts`、`usePhyCommands.ts` |
| 7a | 長押しの切り出し | 長押しの処理を composable に切り出す。挙動は変えない | `usePhyCanvasPointer.ts` |
| 7b | 長押しの2方式 | ループ方式(間隔を延ばし、円形インジケータと選択先のリストを付ける)と、長押しでリストを出して選ぶ方式を入れ、切り替えて比べる | 7aの composable |
| 8 | スイッチ・スタビライザーの表示 | 既定は非表示にし、トグルで色違いの十字を出す。プロパティ欄に `switchOffset` の入力欄を置く | 1の層、`PhyProperties.vue` |
順番の理由: 1で作る層を5と8が使う。2〜5は小さく独立しているので先に片付ける。
7・8は、着手前に下の点を決める必要がある。7は最も大きく、試して比べる部分なので、挙動を変えない切り出しを先に入れる。
着手前に決めること:
- 7b: 長押しの間隔の値。リスト方式でどう選ぶか(離した位置で決めるのか、リストを出したあとにクリックするのか)。
- 8: スタビライザーを付けるキー幅と、幅ごとの位置。これは事実なので、出典で裏取りしてから決める。推測で実装しない。
覆すときに影響する箇所: `mock/src/ui/phy/`、6の行判定の関数とそのテスト。
## B. 明示的な未決事項
### 未確認事項
| 出典 | 仮決定 |
| --- | --- |
| 全体 P-1 アナログ値の解釈の所属 | モック対象外。ELCにも仮置きしない |
| MAP P-1 動作定義とキーごとの設定の保存形式 | モック対象外。MAPは現行Schemaの `layers[].keymap[]` だけを扱う。モック後の叩き台として: マクロ・Staticの動作は `map.definitions[]`(`name` で識別、`kind` と種別ごとの内容)に置き、キーごとの設定はPHYのキーIDに付ける(`map.keySettings[keyId]`) |
| MAP P-2 VIAのコマンドは0.34.4のソースどおり[推測] | 推測に依存しない。実機通信を作らない。疑似デバイスはアプリ側の操作を模擬するだけで、VIAの動作とは扱わない |
| MAP P-3 プロジェクトと実機の対応・状態管理 | 本線のモックでは対象外。疑似デバイスの段階で T-24 を使う |
| HST P-1 使う側が逆引きを判断できる[推測] | 推測に依存しない。逆引き・Key Overrideの候補生成を作らない |
| HST P-2 対象ホストを複数持つか | モックではホストを持たない。MAPはQMKの表記のまま表示し、よく使うコードだけUS配列の文字を添える(T-23) |
| implementation-plan P-1〜P-3 | T-7、T-5、T-6 |
### backlog
| 項目 | 仮決定 |
| --- | --- |
| PHY-P-2 KLE取り込みの照合基準 | 新規PHYとしての取り込みだけ。照合・更新は作らない |
| PHY-P-6 見た目情報の所属層 | 現行Schemaどおり、キー単位とファイル単位の `kleCompatibility` に退避し書き戻す。編集UIは作らない。ラベルと `a` の往復はT-20 |
| PHY-P-8 ラベルのMAP適用 | 自動適用しない。ラベルは表示だけ |
| PHY-P-12 部品の表裏 | 描き分けない。予約グループ名は `@led` `@encoder` `@screw` `@oled` の4つを仮に置き、形だけ変えて描く。それ以外は共通のマーカーと名前 |
| PHY-P-13 reframeの操作系 | 通常の移動・回転とは別の「座標枠の変更」コマンドにし、原点と基準角度を数値で入れる。ハンドル操作は作らない |
| PHY-P-14 原点の吸着 | 作らない |
| ELC-P-1 光学スイッチ | 対象外。接点に読み替えない |
| ELC-P-2 複数マルチプレクサ | 対象外。analog-muxの提供者はデータとして読み書きだけする |
| ELC-P-3 走査設定 | Generatorのプレビューは単純な接点マトリクスに限る(T-19)。走査方向は出力設定で明示させ、配線から推定しない |
| IDX-P-1 既存コミュニティレイアウトとの互換 | 保証しない。独自の `order` を編集・保存するだけ |
| MAP-P-4 ON/OFF以外の入力 | 対象外。エンコーダはPHYの部品ノードとして置くまで |
| MAP-P-5 表示するOS配列 | T-23 |
| MAP-P-6 developブランチ・Community Modules | 対象外(QMKへの実出力をしない) |
| MAP-P-7 再ビルド時の調整の保全 | 対象外 |
| MAP-P-8 QMK以外のキーコード表記 | QMK表記だけを保存する |
| MAP-P-9 Cコードの生成 | 対象外 |
ELC本文にある「LEDのドライバIC方式」「デジタル側の提供者の種別ごとの記述」も対象外とする。
## C. 暗黙の未決事項
仕様だけでは実装が一意にならない箇所。
### T-10 プロジェクトファイル
1ファイル `*.bkle.json` に束ねる。
```ts
type ProjectFile = {
format: "beyond-kle-mock-project",
version: "0.1.0",
projectId: string,
phy: PhyFile, elc: ElcFile, idx: IdxFile, map: MapFile, // 各層はSchemaどおりのファイル形
output: { variantSelection: Record<string, string>, matrix?: MatrixOutputSettings },
ui?: unknown // 表示中のバリアント、ズーム等。仕様の層には入れない
}
```
各層を単独のJSONとして書き出す・読み込む機能も持つ。モック独自の拡張は各層の中に入れず、`ProjectFile` の直下に置く。
### T-11 ID
新規ノードは種別の接頭辞 + ランダム文字列(`key-`, `cl-`, `cmp-`, `vg-`, `v-` + `crypto.randomUUID()` の先頭12桁)。
プロファイル・提供者・LEDチェーンも同じ規則。PHY内の全ノードでIDを一意にする。
移動・並べ替え・reparentではIDを変えない。複製(コピー&ペーストを含む)は配下も含めて新しいIDにし、ELC・MAP・IDXへの参照は複製しない。
複製した範囲の中で閉じた参照(VariantGroupの `default`)は新しいIDへ付け替える。複製したキーはIDXの末尾へ足さず、未登録のままにする。
同じプロジェクト内のカット&ペーストはreparentとして扱い、IDと参照を保つ(削除+複製にすると T-15 で参照が消える)。
KLEの取り込みは毎回新しいIDを振る。
### T-12 phyReference
- `source`: プロジェクト内では `project:phy`。単独ファイルでは読み込んだPHYのファイル名(利用者がPHYを選び直せる)
- `checksum`: PHY全体をJCS(RFC 8785)で正規化し、UTF-8のSHA-256を `sha256-jcs:<hex>` とする
- 不一致は警告だけ。IDを付け替えず、参照の検証をやり直す。保存時に更新する
### T-13 version
モックが出力する各層とプロジェクトファイルは `0.1.0`。読み込みは `0.1.0` だけを受け付ける(文書の例にある `1.0.0` は互換の約束とは扱わない)。
### T-14 読み込んだ未知のフィールド
保持して書き戻す。新しく書き出すデータには追加しない。
### T-15 ノードの削除
IDX・ELC・MAP・LEDチェーンにある参照を一覧で示し、同じ操作(1回のUndo単位)で削除する。
Variantを削除したときは、VariantGroupの `default` を先頭のVariantへ移す。子が1つになるVariantGroupの削除は、確認して止める。
### T-16 検証の段階
- 保存を止めるもの: Schema違反、ID重複、参照先がない
- 出力(`mock-target.json`)を止めるもの: IDXに未登録のキー、選択構成のキーの未配線・未割り当て、非排他キーの接続先の衝突
- どちらでも警告として一覧に出す。編集途中の状態は保存できる
### T-17 Undo/Redo
プロジェクト全体のスナップショットで持つ。ドラッグ1回を1操作にまとめる。
### T-18 IDXの初期の並び
KLE取り込み時は、取り込んだファイルでのキーの出現順で `order` を1回だけ作る。手で置いたキーは作った順に末尾へ足す。
その後はPHYから作り直さない。IDXに未登録のキーは「未登録」と表示する。
`decal` と部品ノードは対象外、`ghost` は対象にする。
### T-19 row/col の割り当て(Generatorのプレビュー)
出力設定に順序付きの `rowPins` と `colPins` を利用者が指定し、その添字をrow/colとする。
各接点キーの両端子が、片方は `rowPins`、もう片方は `colPins` に属することを検査する。満たさない構成(charlieplexing、direct)は「未対応」と表示して出力しない。
端子の `a`/`b` の順、キーの位置、IDXの順からは推定しない。
### T-20 KLEの入出力の細部
- 入力は厳密なJSON(KLEの緩いJSONは読まない)。VIA定義の `layouts.keymap` は、KLE JSONとして幾何だけを取り込む
- ラベルは欄の位置と空欄を保って保持する。`a` 等の属性の引き継ぎとリセットは入出力の処理で解決する
- 書き出しは、同じ絶対角度のキーに共通の回転中心を使い、キーの順を固定する。座標は小数6桁に丸める(PHYの値は丸めない)
- 書き出し時のバリアント選択は `output.variantSelection` を使い、指定のないものはPHYの `default`
### T-21 プロファイルと座標の単位
モックで編集できるのは、全プロファイルの `unitSize` が同じ場合だけ。違うものは読み込んで保持するが、編集不可と表示する。
理由: 親の座標系のUを、どのプロファイルでmmへ換算するかが決まっていない([discrepancies.md](discrepancies.md) D-6)。
### T-22 MAPの割り当て
- 未割り当ては `keymap` に要素がない状態で表す。`KC_NO`・`KC_TRNS` は明示の割り当てとして区別する
- 同じレイヤーでの `keyId` の重複は禁止
- 新規MAPは名前 `Base` の空のレイヤー1枚から始める。レイヤー名は大文字小文字を区別し、MAP内で一意
- レイヤーへの参照は `MO(Nav)` のように括弧内にレイヤー名を書く。対象は `MO` `TG` `TO` `TT` `DF` `OSL` `LT(名前, kc)` に限る。専用の解析器で読み、`eval` は使わない
- レイヤー名の変更は、参照を同じ操作で書き換える。参照されているレイヤーの削除は止める
- 編集できるキーコードは、基本キー・修飾キー・`KC_NO`・`KC_TRNS`・上のレイヤー動作・修飾キー付き(`LCTL(kc)` 等)に限る。知らないコードは保持し、表示だけする
### T-23 キーコードの表示
QMKの表記をそのまま出し、基本キーだけUS配列の文字を添える。表示用の小さな対応表はUI側に置き、HSTのデータ形式としては扱わない。
### T-24 疑似デバイスとの対応(後段・任意)
MAP P-3 の問いを、モック用に次のように固定する。
- 出力のたびに「出力記録」を作り、選択構成・キーID↔出力番号↔row/col・レイヤー名↔番号を保存する。疑似デバイスは記録のIDを持つ
- 書き込みは選択構成のキーだけ。選択外のバリアントのキーの割り当てはMAPに残す
- 読み出した値は、記録にある選択構成のキーだけへ戻す。通信上のアドレスからPHYキーを推定しない
- layout optionは疑似デバイス側で変えられないものとする。バリアントを変えたら新しい出力記録にする
- 配線・IDXの対象キー・バリアント選択・IDXの順・レイヤーの順が変わったら記録を失効させる。座標やラベルの変更では失効させない
- 「現在のMAP」「出力時のMAP」「最後に読んだ値」を分けて持つ。読み出しは差分を見せてから取り込む。自動同期はしない
### T-25 非排他キーの接続先の衝突判定
ダイオードがあれば、接続先を(アノード側のピン, カソード側のピン)の順序付きの組として比べる。ダイオードがなければ順序のない組として比べる。
理由: 順序のない組で比べると、Cr0wn60のようなcharlieplexingが常にエラーになる([discrepancies.md](discrepancies.md) D-5)。
### T-26 MCUのピン名
提供者の `part` は1種類(RP2040)だけをモックに組み込み、ピン名(`GP0`〜`GP29`)を候補として出す。知らない `part` は自由入力にする。
提供者のIDに `.` を含めない(`PinRef` の区切りと衝突するため)。
### T-27 reparentの計算
ワールド変換を使って `新しいローカル変換 = (新しい親のワールド変換)⁻¹ · 旧ワールド変換` とする。
仕様の式を、親が兄弟でない場合にも当てはまる形で実装する([discrepancies.md](discrepancies.md) D-7)。
reparent・reframeの前後で、絶対位置の差が1e-9U以下であることをテストで確かめる。
### T-28 スナップ
移動(ドラッグ・矢印キー)はグリッドの刻みに従う。刻みはエディタの状態として持ち、候補は 1U・0.25U・0.125U・1/12U、初期値は0.25U。
サイズは0.25U、回転は15度刻みで固定する。いずれも操作開始時の値から刻む。
数値の直接入力はグリッド外の値も受け付ける。計算結果は丸めない。