行の判定は idx-editor-feedback.md の規則(そろっている軸は座標でまとめ、 ずれている軸は隣とのつながりで1対1にたどる)を domain/rows.ts に実装し、 IDX Editor でも使えるよう列の判定も合わせて置いた。 何も選んでいないときは、コンテナの全キーの下に新しい行を作り、その左端に置く。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XsamNKrWY3obJwR746oQRq
79 lines
3.0 KiB
TypeScript
79 lines
3.0 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { detectColumns, detectRows, newRowPosition, rowEndPosition, type KeyRect } from "../src/domain/rows";
|
|
|
|
const key = (id: string, x: number, y: number, w = 1, h = 1): KeyRect => ({ id, x, y, w, h });
|
|
|
|
describe("row detection", () => {
|
|
it("groups row-staggered keys by y and traces columns", () => {
|
|
const keys = [
|
|
key("tab", 0, 0, 1.5),
|
|
key("q", 1.5, 0),
|
|
key("w", 2.5, 0),
|
|
key("caps", 0, 1, 1.75),
|
|
key("a", 1.75, 1),
|
|
key("s", 2.75, 1),
|
|
key("space", 0.5, 2, 3),
|
|
];
|
|
expect(detectRows(keys)).toEqual([["tab", "q", "w"], ["caps", "a", "s"], ["space"]]);
|
|
expect(detectColumns(keys)[0]).toEqual(["tab", "caps", "space"]);
|
|
});
|
|
|
|
it("traces column-staggered rows through the nearest key of the next column", () => {
|
|
const offsets = [0.5, 0.25, 0, 0.25];
|
|
const keys = offsets.flatMap((dy, col) => [0, 1, 2].map((row) => key(`c${col}r${row}`, col, row + dy)));
|
|
expect(detectRows(keys)).toEqual([0, 1, 2].map((row) => offsets.map((_, col) => `c${col}r${row}`)));
|
|
expect(detectColumns(keys)[2]).toEqual(["c2r0", "c2r1", "c2r2"]);
|
|
});
|
|
|
|
it("keeps rows apart when an outer column has fewer keys", () => {
|
|
const keys = [
|
|
key("p1", 0, 1.5),
|
|
key("p2", 0, 2.5),
|
|
...[0, 1, 2].map((row) => key(`r${row}`, 1, row + 1.25)),
|
|
...[0, 1, 2, 3].map((row) => key(`i${row}`, 2, row + 1)),
|
|
];
|
|
expect(detectRows(keys)).toEqual([["p1", "r0", "i0"], ["p2", "r1", "i1"], ["r2", "i2"], ["i3"]]);
|
|
});
|
|
|
|
it("does not link keys a full row apart", () => {
|
|
const keys = [key("a0", 0, 0), key("a1", 0, 1), key("b1", 1, 1.1), key("b2", 1, 2.1)];
|
|
expect(detectRows(keys)).toEqual([["a0"], ["a1", "b1"], ["b2"]]);
|
|
});
|
|
|
|
it("puts a tall key in its top row", () => {
|
|
const keys = [key("7", 0, 0), key("8", 1, 0), key("plus", 2, 0, 1, 2), key("4", 0, 1), key("5", 1, 1)];
|
|
expect(detectRows(keys)).toEqual([
|
|
["7", "8", "plus"],
|
|
["4", "5"],
|
|
]);
|
|
});
|
|
|
|
it("links the inner columns across the gap between halves", () => {
|
|
const columns: [string, number, number][] = [
|
|
["l", 0, 0.5],
|
|
["m", 1, 0.25],
|
|
["n", 4, 0.25],
|
|
["o", 5, 0.5],
|
|
];
|
|
const keys = columns.flatMap(([name, x, dy]) =>
|
|
[0, 1, 2].map((row) => key(`${name}${row}`, x, row + dy)),
|
|
);
|
|
expect(detectRows(keys)).toEqual([0, 1, 2].map((row) => columns.map(([name]) => `${name}${row}`)));
|
|
});
|
|
});
|
|
|
|
describe("key insertion position", () => {
|
|
const keys = [key("a", 0, 0), key("b", 1, 0, 2), key("c", 0, 1)];
|
|
|
|
it("places a new key after the right end of the selected key's row", () => {
|
|
expect(rowEndPosition(keys, "a")).toEqual({ x: 3, y: 0 });
|
|
expect(rowEndPosition(keys, "c")).toEqual({ x: 1, y: 1 });
|
|
expect(rowEndPosition(keys, "missing")).toBeUndefined();
|
|
});
|
|
|
|
it("starts a new row at the left below every key", () => {
|
|
expect(newRowPosition([...keys, key("tall", 3, 0.5, 1, 2)])).toEqual({ x: 0, y: 2.5 });
|
|
expect(newRowPosition([])).toBeUndefined();
|
|
});
|
|
});
|