feat(01-03): add QWERTZ keyboard renderer with finger colors and animations

- Full QWERTZ keyboard with DE and CH layout support
- Finger color mapping for all keys per spec section 8.3
- renderKeyboard, highlightKey, pressKey, updateKeyboardForLevel exports
- CSS: pulse animation for target key, press animation on keydown
- Inactive keys grayed at 0.5 opacity, active keys with finger colors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 11:07:52 +02:00
co-authored by Claude Opus 4.6
parent 20a7670ef9
commit 2f0784fd14
3 changed files with 225 additions and 10 deletions
+10 -10
View File
@@ -7,39 +7,39 @@ describe("levels", () => {
});
it("Level 1 newKeys = ['f', 'j', ' ']", () => {
expect(levels[0].newKeys).toEqual(["f", "j", " "]);
expect(levels[0]!.newKeys).toEqual(["f", "j", " "]);
});
it("Level 2 newKeys = ['d', 'k']", () => {
expect(levels[1].newKeys).toEqual(["d", "k"]);
expect(levels[1]!.newKeys).toEqual(["d", "k"]);
});
it("Level 3 newKeys = ['s', 'l']", () => {
expect(levels[2].newKeys).toEqual(["s", "l"]);
expect(levels[2]!.newKeys).toEqual(["s", "l"]);
});
it("Level 4 newKeys = ['a', 'ö']", () => {
expect(levels[3].newKeys).toEqual(["a", "ö"]);
expect(levels[3]!.newKeys).toEqual(["a", "ö"]);
});
it("Level 5 newKeys = ['g', 'h']", () => {
expect(levels[4].newKeys).toEqual(["g", "h"]);
expect(levels[4]!.newKeys).toEqual(["g", "h"]);
});
it("Level 6 newKeys = ['e', 'i']", () => {
expect(levels[5].newKeys).toEqual(["e", "i"]);
expect(levels[5]!.newKeys).toEqual(["e", "i"]);
});
it("Level 3 allKeys includes all keys from levels 1-3", () => {
const expected = ["f", "j", " ", "d", "k", "s", "l"];
for (const key of expected) {
expect(levels[2].allKeys).toContain(key);
expect(levels[2]!.allKeys).toContain(key);
}
expect(levels[2].allKeys).toHaveLength(expected.length);
expect(levels[2]!.allKeys).toHaveLength(expected.length);
});
it("Level 6 allKeys contains 13 keys", () => {
expect(levels[5].allKeys).toHaveLength(13);
expect(levels[5]!.allKeys).toHaveLength(13);
const expected = [
"f",
"j",
@@ -56,7 +56,7 @@ describe("levels", () => {
"i",
];
for (const key of expected) {
expect(levels[5].allKeys).toContain(key);
expect(levels[5]!.allKeys).toContain(key);
}
});