provisional/idx-editor-feedback.md の合意事項と T-31 の仮決定に沿って、IdxPanel を捨てて ui/idx/ に一から書き直した。 並びのリストは置かず、左にバリアントのツリー、中央のキャンバスでドラッグして並べ替え、右にプリセット・取り込み・未登録の登録を置いた。 - order の操作(隠れたキーのくっつき先、挿入、入れ替え、連番の並べ直し)を domain/idxOrder.ts に、 走査・半身の分割・行の割り当てのずらし・マトリクス順を domain/idxScan.ts に、純粋関数として置いた。行と列の判定は rows.ts をそのまま使う - QMK keyboard.json・VIA定義・KLE JSON の取り込みは、位置と回転で表示中のキーに照合する(io/layoutImport.ts、domain/idxImport.ts) - 回転したキーでも出力番号を正立させる - Codex の設定ファイルが壊れていて起動できなかったため、実装は Claude が直接行った - 検証: vue-tsc 0件、Vitest 101件(うち新規17件)、Playwright 48件(IDX 8件を書き直し)、prettier --check Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GzX13A7HseQz9n6etGbFDg
92 lines
2.8 KiB
Vue
92 lines
2.8 KiB
Vue
<script setup lang="ts">
|
|
// 既存の定義(QMK keyboard.json / VIA定義 / KLE JSON)からの取り込み。照合は親が表示中のキーで行う。T-31。
|
|
import { ref } from "vue";
|
|
import type { Placement } from "../../domain/idxImport";
|
|
import { readLayoutPlacements } from "../../io/layoutImport";
|
|
defineProps<{ message: string }>();
|
|
const emit = defineEmits<{ apply: [placements: Placement[], source: string]; error: [text: string] }>();
|
|
const input = ref<HTMLInputElement>();
|
|
const pending = ref<{ name: string; data: unknown; layouts: string[] } | null>(null);
|
|
const layout = ref("");
|
|
async function read(file: File) {
|
|
try {
|
|
const data = JSON.parse(await file.text()) as unknown;
|
|
const result = readLayoutPlacements(data);
|
|
if (result.layouts && !result.layout) {
|
|
pending.value = { name: file.name, data, layouts: result.layouts };
|
|
layout.value = result.layouts[0]!;
|
|
return;
|
|
}
|
|
pending.value = null;
|
|
emit("apply", result.placements, result.layout ? `${file.name} / ${result.layout}` : file.name);
|
|
} catch (e) {
|
|
emit("error", String(e));
|
|
}
|
|
}
|
|
function applyLayout() {
|
|
if (!pending.value) return;
|
|
const { placements } = readLayoutPlacements(pending.value.data, layout.value);
|
|
emit("apply", placements, `${pending.value.name} / ${layout.value}`);
|
|
pending.value = null;
|
|
}
|
|
</script>
|
|
<template>
|
|
<section class="import">
|
|
<h3>既存の定義から取り込む</h3>
|
|
<p class="hint">
|
|
QMK keyboard.json・VIA定義・KLE JSON のキー順を、位置が一致する表示中のキーに写します。
|
|
</p>
|
|
<button @click="input?.click()">ファイルを選ぶ…</button>
|
|
<input
|
|
ref="input"
|
|
hidden
|
|
type="file"
|
|
accept=".json,application/json"
|
|
data-testid="idx-import-file"
|
|
@change="
|
|
(e) => {
|
|
const f = (e.target as HTMLInputElement).files?.[0];
|
|
if (f) void read(f);
|
|
(e.target as HTMLInputElement).value = '';
|
|
}
|
|
"
|
|
/>
|
|
<div v-if="pending" class="layouts">
|
|
<label
|
|
>レイアウト
|
|
<select v-model="layout" aria-label="取り込むレイアウト">
|
|
<option v-for="name in pending.layouts" :key="name" :value="name">{{ name }}</option>
|
|
</select>
|
|
</label>
|
|
<button @click="applyLayout">取り込む</button>
|
|
<button @click="pending = null">やめる</button>
|
|
</div>
|
|
<p v-if="message" class="result" data-testid="idx-import-result">{{ message }}</p>
|
|
</section>
|
|
</template>
|
|
<style scoped>
|
|
.import {
|
|
margin: 0 0 14px;
|
|
}
|
|
h3 {
|
|
font-size: 14px;
|
|
margin: 0 0 6px;
|
|
}
|
|
.hint,
|
|
.result {
|
|
font-size: 12px;
|
|
color: #667;
|
|
margin: 4px 0;
|
|
}
|
|
.result {
|
|
color: #20242b;
|
|
}
|
|
.layouts {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
align-items: center;
|
|
margin-top: 6px;
|
|
}
|
|
</style>
|