mock: 仮決定に沿ってモックを実装する(PHY Editor・IDX・ELC・MAP・Generator)
provisional/decisions.md の仮決定(T-1〜T-28)に従い、mock/ に Vue 3 + Vite + TypeScript のモックを置いた。 T-3 の1〜3(回転したクラスター内のドラッグと保存の往復、IDX・MAPとの連携、ELCと出力の対応表)まで。疑似デバイスとの往復(T-24)は未着手。 - KLE JSONの入出力は、KLE本家の serial.js の deserialize の結果を期待値とする fixture(tests/fixtures/kle-oracle.json)で照合した。本家のコードは取り込んでいない(T-7) - 基本キーコードの表は QMK の data/constants/keycodes/keycodes_0.0.1_basic.hjson(1e340cd)から抽出した - 実装は Codex(Luna)、レビューは Codex(Sol)。検証: Vitest 53件、Playwright 20件、vue-tsc、vite build - TypeScript は vue-tsc が 7 系に未対応のため 6 系に固定した - 操作性の改善(ELCの提供者の表示、一括配線とGeneratorのピン設定の引き継ぎ、キー内の文字のはみ出し)は別途行う Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AATqjRyp6rrnwTNdYw8XBT
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"$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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"$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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"$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": ["name", "keymap"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Layer identifier, unique within the MAP. Layer order is the array order"
|
||||
},
|
||||
"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" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
{
|
||||
"$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). Kept here for now; the eventual owning layer is deferred in backlog/README.md, PHY-P-6",
|
||||
"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). Kept here for now; the eventual owning layer is deferred in backlog/README.md, PHY-P-6",
|
||||
"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" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user