From 0d7b181efab453995dfcb6d668861a660539f1a5 Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Tue, 6 Jul 2021 20:47:22 +0700 Subject: [PATCH 01/10] feat: Generate .klc with headers --- input/Manoonchai.json | 7 +++++++ main.ts | 39 ++++++++++++++++++++++++++++++++++++++- output/test.klc | 6 ++++++ test/main.test.ts | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 output/test.klc diff --git a/input/Manoonchai.json b/input/Manoonchai.json index c5247f8..02a6f64 100644 --- a/input/Manoonchai.json +++ b/input/Manoonchai.json @@ -3,6 +3,13 @@ "version": "1.0", "language": "Thai", "layers": ["Base", "Shift"], + "license": "MIT", + "os": { + "windows": { + "company": "Manoonchai", + "localeId": "0000041e" + } + }, "keys": { "`": ["`", "~"], "1": ["1", "!"], diff --git a/main.ts b/main.ts index cc1bc6d..963a5d4 100644 --- a/main.ts +++ b/main.ts @@ -9,6 +9,7 @@ import { validate, } from "class-validator" import { js2xml } from "xml-js" +import fs from "fs" export async function validateLayout( content: Record @@ -23,6 +24,34 @@ export async function validateLayout( return !errors.length } +export async function generateKlc( + content: Record +): Promise { + const layout = plainToClass(Layout, content) + const errors = await validate(layout) + + if (errors.length) { + throw new Error(errors.map((e) => e.toString()).join(", ")) + } + + const locales = { + Thai: "th-TH", + } + + const lines = [ + `KBD\t${layout.name}\t"${layout.language} ${layout.name} v${layout.version}"`, + `COPYRIGHT\t"MIT"`, + `COMPANY\t"${layout.os.windows.company}"`, + `LOCALENAME\t"${locales[layout.language]}"`, + `LOCALEID\t"${layout.os.windows.localeId}"`, + `VERSION\t${layout.version}`, + ] + + fs.writeFileSync("./output/test.klc", lines.join("\n"), { + encoding: "utf8", + }) +} + export async function generateLayout( content: Record ): Promise { @@ -295,7 +324,8 @@ export class Layout { version: string @IsString() - language: string + @IsIn(["Thai"]) + language: "Thai" @ArrayNotEmpty() @IsIn(["Base", "Shift", "AltGr", "Command", "Option", "Control"], { @@ -306,4 +336,11 @@ export class Layout { @IsDefined() @IsNotEmptyObject() keys: Record + + @IsDefined() + os: OSAttributes +} + +interface OSAttributes { + windows: { company: string; localeId: string } } diff --git a/output/test.klc b/output/test.klc new file mode 100644 index 0000000..ffbc064 --- /dev/null +++ b/output/test.klc @@ -0,0 +1,6 @@ +KBD Manoonchai "Thai Manoonchai v1.0" +COPYRIGHT "MIT" +COMPANY "Manoonchai" +LOCALENAME "th-TH" +LOCALEID "0000041e" +VERSION 1.0 \ No newline at end of file diff --git a/test/main.test.ts b/test/main.test.ts index a0f0265..09fe32e 100644 --- a/test/main.test.ts +++ b/test/main.test.ts @@ -1,7 +1,7 @@ import fs from "fs" import path from "path" import { js2xml, xml2js } from "xml-js" -import { generateLayout, validateLayout } from "../main" +import { generateKlc, generateLayout, validateLayout } from "../main" import { fixUnicode } from "../utils" describe("validateLayout", () => { @@ -409,3 +409,35 @@ describe("generateLayout", () => { expect(keys[keys.length - 1].attributes["output"]).toEqual("\u001e") }) }) + +describe("generateKlc", () => { + it("exists", () => { + expect(generateKlc).toBeDefined() + }) + + it("generates .klc file", async () => { + const inputJson = JSON.parse( + fs.readFileSync( + path.join(process.cwd(), "input", "Manoonchai.json"), + "utf8" + ) + ) + + await generateKlc(inputJson) + + expect(fs.existsSync("output/test.klc")).toBeTruthy() + + const lines = fs + .readFileSync("output/test.klc", "utf-8") + .split("\n") + .filter(Boolean) + + // Assert file headers + expect(lines[0]).toEqual(`KBD\tManoonchai\t"Thai Manoonchai v1.0"`) + expect(lines[1]).toEqual(`COPYRIGHT\t"MIT"`) + expect(lines[2]).toEqual(`COMPANY\t"Manoonchai"`) + expect(lines[3]).toEqual(`LOCALENAME\t"th-TH"`) + expect(lines[4]).toEqual(`LOCALEID\t"0000041e"`) + expect(lines[5]).toEqual(`VERSION\t1.0`) + }) +}) From b91740433d73edde9b7f774297fa0df249ff5809 Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Tue, 6 Jul 2021 20:48:52 +0700 Subject: [PATCH 02/10] feat: Add newline between header lines --- main.ts | 2 +- output/test.klc | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/main.ts b/main.ts index 963a5d4..217cff7 100644 --- a/main.ts +++ b/main.ts @@ -47,7 +47,7 @@ export async function generateKlc( `VERSION\t${layout.version}`, ] - fs.writeFileSync("./output/test.klc", lines.join("\n"), { + fs.writeFileSync("./output/test.klc", lines.join("\n\n"), { encoding: "utf8", }) } diff --git a/output/test.klc b/output/test.klc index ffbc064..3ba2479 100644 --- a/output/test.klc +++ b/output/test.klc @@ -1,6 +1,11 @@ KBD Manoonchai "Thai Manoonchai v1.0" + COPYRIGHT "MIT" + COMPANY "Manoonchai" + LOCALENAME "th-TH" + LOCALEID "0000041e" + VERSION 1.0 \ No newline at end of file From fff6e5d82aef34d1780713a3a5b1ed1cf2f960aa Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Tue, 6 Jul 2021 22:56:19 +0700 Subject: [PATCH 03/10] feat: Generate layouts in klc --- main.ts | 100 ++++++++++++++++++++++++++++++++++++++++++++-- output/test.klc | 58 ++++++++++++++++++++++++++- test/main.test.ts | 31 ++++++++++++++ 3 files changed, 184 insertions(+), 5 deletions(-) diff --git a/main.ts b/main.ts index 217cff7..89fd5bf 100644 --- a/main.ts +++ b/main.ts @@ -34,22 +34,114 @@ export async function generateKlc( throw new Error(errors.map((e) => e.toString()).join(", ")) } - const locales = { + const klcLocales = { Thai: "th-TH", } + const klcShiftStates = { + Base: 0, + Shift: 1, + Ctrl: 2, + "Shift+Ctrl": 3, + Alt: 4, + "Shift+Alt": 5, + "Alt+Ctrl": 6, + "Shift+Alt+Ctrl": 7, + // From macOS config + Command: 4, + AltGr: 7, + Option: 7, + Control: 2, + } + + const klfDefaultLayout = { + "1": "02\t1", + "2": "03\t2", + "3": "04\t3", + "4": "05\t4", + "5": "06\t5", + "6": "07\t6", + "7": "08\t7", + "8": "09\t8", + "9": "0a\t9", + "0": "0b\t0", + "-": "0c\tOEM_MINUS", + "=": "0d\tOEM_PLUS", + q: "10\tQ", + w: "11\tW", + e: "12\tE", + r: "13\tR", + t: "14\tT", + y: "15\tY", + u: "16\tU", + i: "17\tI", + o: "18\tO", + p: "19\tP", + "[": "1a\tOEM_4", + "]": "1b\tOEM_6", + a: "1e\tA", + s: "1f\tS", + d: "20\tD", + f: "21\tF", + g: "22\tG", + h: "23\tH", + j: "24\tJ", + k: "25\tK", + l: "26\tL", + ";": "27\tOEM_1", + "'": "28\tOEM_7", + "`": "29\tOEM_3", + "\\": "2b\tOEM_5", + z: "2c\tZ", + x: "2d\tX", + c: "2e\tC", + v: "2f\tV", + b: "30\tB", + n: "31\tN", + m: "32\tM", + ",": "33\tOEM_COMMA", + ".": "34\tOEM_PERIOD", + "/": "35\tOEM_2", + } + const lines = [ `KBD\t${layout.name}\t"${layout.language} ${layout.name} v${layout.version}"`, `COPYRIGHT\t"MIT"`, `COMPANY\t"${layout.os.windows.company}"`, - `LOCALENAME\t"${locales[layout.language]}"`, + `LOCALENAME\t"${klcLocales[layout.language]}"`, `LOCALEID\t"${layout.os.windows.localeId}"`, `VERSION\t${layout.version}`, ] - fs.writeFileSync("./output/test.klc", lines.join("\n\n"), { - encoding: "utf8", + const shiftStateLines: Array = layout.layers.map((layer, idx) => { + if (layer in klcShiftStates) { + return `${klcShiftStates[layer]}\t// Column ${idx + 4} : ${layer}` + } else { + throw new Error("Layer not valid") + } + }) + + shiftStateLines.unshift("SHIFTSTATE", "") + + const layoutLines = ["LAYOUT"] + + Object.entries(klfDefaultLayout).forEach(([key, value]) => { + const extensions = layout.layers.map((_, idx) => layout.keys[key][idx]) + layoutLines.push([value, "0", ...extensions].join("\t")) }) + + fs.writeFileSync( + "./output/test.klc", + [ + lines.join("\n\n"), + shiftStateLines.join("\n"), + layoutLines.join("\n"), + "ENDKBD", + ].join("\n\n"), + { + encoding: "utf8", + } + ) } export async function generateLayout( diff --git a/output/test.klc b/output/test.klc index 3ba2479..6f294a8 100644 --- a/output/test.klc +++ b/output/test.klc @@ -8,4 +8,60 @@ LOCALENAME "th-TH" LOCALEID "0000041e" -VERSION 1.0 \ No newline at end of file +VERSION 1.0 + +SHIFTSTATE + +0 // Column 4 : Base +1 // Column 5 : Shift + +LAYOUT +0b 0 0 0 ) +02 1 0 1 ! +03 2 0 2 @ +04 3 0 3 # +05 4 0 4 $ +06 5 0 5 % +07 6 0 6 ^ +08 7 0 7 & +09 8 0 8 * +0a 9 0 9 ( +0c OEM_MINUS 0 - _ +0d OEM_PLUS 0 = + +10 Q 0 ใ ฒ +11 W 0 ต ฏ +12 E 0 ห ซ +13 R 0 ล ญ +14 T 0 ส ฟ +15 Y 0 ป ฉ +16 U 0 ั ึ +17 I 0 ก ธ +18 O 0 ิ ฐ +19 P 0 บ ฎ +1a OEM_4 0 ็ ฆ +1b OEM_6 0 ฬ ฑ +1e A 0 ง ษ +1f S 0 เ ถ +20 D 0 ร แ +21 F 0 น ช +22 G 0 ม พ +23 H 0 อ ผ +24 J 0 า ำ +25 K 0 ่ ข +26 L 0 ้ โ +27 OEM_1 0 ว ภ +28 OEM_7 0 ื " +29 OEM_3 0 ` ~ +2b OEM_5 0 ฯ ฌ +2c Z 0 ุ ฤ +2d X 0 ไ ฝ +2e C 0 ท ๆ +2f V 0 ย ณ +30 B 0 จ ๊ +31 N 0 ค ๋ +32 M 0 ี ์ +33 OEM_COMMA 0 ด ศ +34 OEM_PERIOD 0 ะ ฮ +35 OEM_2 0 ู ? + +ENDKBD \ No newline at end of file diff --git a/test/main.test.ts b/test/main.test.ts index 09fe32e..d99517d 100644 --- a/test/main.test.ts +++ b/test/main.test.ts @@ -439,5 +439,36 @@ describe("generateKlc", () => { expect(lines[3]).toEqual(`LOCALENAME\t"th-TH"`) expect(lines[4]).toEqual(`LOCALEID\t"0000041e"`) expect(lines[5]).toEqual(`VERSION\t1.0`) + + // Assert Shiftstate + // public enum ShiftState : int { + // Base = 0, // 0 + // Shft = 1, // 1 + // Ctrl = 2, // 2 + // ShftCtrl = Shft | Ctrl, // 3 + // Menu = 4, // 4 -- NOT USED + // ShftMenu = Shft | Menu, // 5 -- NOT USED + // MenuCtrl = Menu | Ctrl, // 6 + // ShiftMenuCtrl = Shft | Menu | Ctrl, // 7 + // } + + // 0 //Column 4 + // 1 //Column 5 : Shft + // 2 //Column 6 : Ctrl + // 6 //Column 7 : Ctrl Alt + // 7 //Column 8 : Shft Ctrl Alt + + expect(lines[6]).toEqual(`SHIFTSTATE`) + expect(lines[7]).toEqual(`0\t// Column 4 : Base`) + expect(lines[8]).toEqual(`1\t// Column 5 : Shift`) + + // Assert Layout + expect(lines[9]).toEqual(`LAYOUT`) + + expect(lines[10]).toEqual(`0b\t0\t0\t0\t)`) + expect(lines[11]).toEqual(`02\t1\t0\t1\t!`) + + // Assert ENDKBD + expect(lines.slice(-1)).toEqual(["ENDKBD"]) }) }) From 0c959cda7e83fa46a58e6b00c85ed6a963dfac5e Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Tue, 6 Jul 2021 22:58:28 +0700 Subject: [PATCH 04/10] fix: Add os to another json file --- input/Manoonchai-ColemakDH-Mod.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/input/Manoonchai-ColemakDH-Mod.json b/input/Manoonchai-ColemakDH-Mod.json index dedccf2..c30fc1e 100644 --- a/input/Manoonchai-ColemakDH-Mod.json +++ b/input/Manoonchai-ColemakDH-Mod.json @@ -3,6 +3,12 @@ "version": "1.0", "language": "Thai", "layers": ["Base", "Shift", "Command", "Option", "Control"], + "os": { + "windows": { + "company": "Manoonchai", + "localeId": "0000041e" + } + }, "keys": { "`": ["`", "~", "`", "`", "`"], "1": ["1", "!", "1", "1", "1"], From bc673cba2caf0cbed3e497046ab52d48b3de7c43 Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Wed, 7 Jul 2021 13:21:48 +0700 Subject: [PATCH 05/10] fix: Change file encoding & linefeed --- main.ts | 10 +++++----- output/test.klc | Bin 911 -> 1690 bytes test/main.test.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/main.ts b/main.ts index 89fd5bf..8dd1df5 100644 --- a/main.ts +++ b/main.ts @@ -133,13 +133,13 @@ export async function generateKlc( fs.writeFileSync( "./output/test.klc", [ - lines.join("\n\n"), - shiftStateLines.join("\n"), - layoutLines.join("\n"), + lines.join("\r\n\r\n"), + shiftStateLines.join("\r\n"), + layoutLines.join("\r\n"), "ENDKBD", - ].join("\n\n"), + ].join("\r\n\r\n"), { - encoding: "utf8", + encoding: "utf16le", } ) } diff --git a/output/test.klc b/output/test.klc index 6f294a8ce2fcf6e8ffd29c955cc8c427df2adc69..3810314e823106d9dc6ba4aa57e45795f3860803 100644 GIT binary patch literal 1690 zcmZ{l=~ELy5XD=|QiBpjyw4CoISl0Fu*#Cny(CLQHlX-HM1+zmg%6@1{o8$S@&-~K zwKX-fzkb`(-P4)0tZY?FSY&&4Y$te*ZQs7&?Fck&eXUdfRHW>u$M&8x`wV z*LJLDp*5@t5A+k^t^bEAf-bNPXa|bFBRKckhSv(zZL8sn0RC$3t5d|6f#>Fs?_q7| zvP&aE{}d7N!H{|zo_ZD|z79Mml#kCQhUd`g*dfLUFluL+W}#lhlR{)C_SJsaH&icW zMT}P%WmNGy_M)%cf3ziOW8`sUhjx1Ay^Y;=5Yra+q=&41f)DQL$EOiikGmajhyq8_!8E%k#n9FU&fj?a?T&bSFomyobz+>Rjg?v=R7CA zhc#{FoM*)MvF@PWk?p$v*+4B~=`xsirTaXj>!L%>Rj*;0cdDH8zNxP($%NfX4$g%Y zdnCD~n$RUl(Vtyx$nl zCGeIw&mj^ofw#q(o6m!HdW6UK!)@qhq~p1GUDw^EA>Mr_WT9M;lGX4^?#f73VSnQ) P$6q7w_9{-3|4F|AZYz+Y literal 911 zcmYk)S8vl$6bJC<|M?Ul3^7!C@3ozQgdoFhOyf8tZXrBS(l%8qLGnQ15m7*e4))$# z?7i%7wBRDezVl*FgI@Iv>sGSb+=LSU1S&LhQiuL=2ekZ>1FF4hZAnA?F9|+(}~(OZzODo zrO+i(7#_}42FFjG=x6NAo=mxWvPb&A(BZH>H$S_;Z!2|z@sQLMlzeuOGSFOVY$9dB zaA~lglnskZ3!6zfu(`Ccg;Wj>mkzd)$|J{R4o689kmoXwZKR4Qa9O}kQe70eEMhyU zX#}o6>3fawHb+C4BsC*Zvo6VA>>{mjkef+-9!YeAw8o*7(QO`ANgKGr56o547FxpB z%*&*0gu?gCr=%Tpgb$g^q;nV-eq;V7orfn}WZolPKp_0dyhgf+rtkst3hA!QGu?nB zzR1xM=_!fQ)hE$M9@k0tP!guUct?5`={l46Eb|T-g{pL4a5BC_(kXc_$da6nEg~uA1X&ezo%vWS)a5!arf%%wB4;AU&z@hVvczVH`wjVw#1QVD#2efMthGv1PAOWbr@Ua-DzUpfCwmLqxk Q4^5&N`-t2|b*x { expect(fs.existsSync("output/test.klc")).toBeTruthy() const lines = fs - .readFileSync("output/test.klc", "utf-8") - .split("\n") + .readFileSync("output/test.klc", "utf16le") + .split("\r\n") .filter(Boolean) // Assert file headers From c00f10ee1b547042c430352a30c0e1ff18eff949 Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Wed, 7 Jul 2021 22:09:34 +0700 Subject: [PATCH 06/10] fix: Correct Windows specific issues : utf16, BOM --- input/Manoonchai.json | 1 + main.ts | 17 +++++++++-------- output/test.klc | Bin 1690 -> 1686 bytes test/main.test.ts | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/input/Manoonchai.json b/input/Manoonchai.json index 02a6f64..4c17625 100644 --- a/input/Manoonchai.json +++ b/input/Manoonchai.json @@ -6,6 +6,7 @@ "license": "MIT", "os": { "windows": { + "installerName": "ThaiMnc", "company": "Manoonchai", "localeId": "0000041e" } diff --git a/main.ts b/main.ts index 8dd1df5..1fb78b3 100644 --- a/main.ts +++ b/main.ts @@ -105,7 +105,7 @@ export async function generateKlc( } const lines = [ - `KBD\t${layout.name}\t"${layout.language} ${layout.name} v${layout.version}"`, + `KBD\t${layout.os.windows.installerName}\t"${layout.language} ${layout.name} v${layout.version}"`, `COPYRIGHT\t"MIT"`, `COMPANY\t"${layout.os.windows.company}"`, `LOCALENAME\t"${klcLocales[layout.language]}"`, @@ -132,12 +132,13 @@ export async function generateKlc( fs.writeFileSync( "./output/test.klc", - [ - lines.join("\r\n\r\n"), - shiftStateLines.join("\r\n"), - layoutLines.join("\r\n"), - "ENDKBD", - ].join("\r\n\r\n"), + "\ufeff" + + [ + lines.join("\r\n\r\n"), + shiftStateLines.join("\r\n"), + layoutLines.join("\r\n"), + "ENDKBD", + ].join("\r\n\r\n"), { encoding: "utf16le", } @@ -434,5 +435,5 @@ export class Layout { } interface OSAttributes { - windows: { company: string; localeId: string } + windows: { company: string; localeId: string; installerName: string } } diff --git a/output/test.klc b/output/test.klc index 3810314e823106d9dc6ba4aa57e45795f3860803..7a0a642f8d5cde1eb5a354730d0035df3726fc71 100644 GIT binary patch delta 32 ncmbQmJB?TT|37aACk7V=PKFSM42DF8Oa@ { .filter(Boolean) // Assert file headers - expect(lines[0]).toEqual(`KBD\tManoonchai\t"Thai Manoonchai v1.0"`) + expect(lines[0]).toEqual(`\ufeffKBD\tThaiMnc\t"Thai Manoonchai v1.0"`) expect(lines[1]).toEqual(`COPYRIGHT\t"MIT"`) expect(lines[2]).toEqual(`COMPANY\t"Manoonchai"`) expect(lines[3]).toEqual(`LOCALENAME\t"th-TH"`) From e43d530c5a36333dd59671fa64fa31d7cf1eb337 Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Wed, 7 Jul 2021 22:58:24 +0700 Subject: [PATCH 07/10] feat: Validate installer name length --- main.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/main.ts b/main.ts index 1fb78b3..28803fb 100644 --- a/main.ts +++ b/main.ts @@ -6,6 +6,7 @@ import { IsIn, IsNotEmptyObject, IsString, + MaxLength, validate, } from "class-validator" import { js2xml } from "xml-js" @@ -34,6 +35,14 @@ export async function generateKlc( throw new Error(errors.map((e) => e.toString()).join(", ")) } + const windowsErrors = await validate( + plainToClass(WindowsAttributes, layout.os.windows) + ) + + if (windowsErrors.length) { + throw new Error(windowsErrors.map((e) => e.toString()).join(", ")) + } + const klcLocales = { Thai: "th-TH", } @@ -435,5 +444,19 @@ export class Layout { } interface OSAttributes { - windows: { company: string; localeId: string; installerName: string } + windows: WindowsAttributes +} + +class WindowsAttributes { + @IsString() + company: string + + @IsString() + localeId: string + + @IsString() + @MaxLength(8, { + message: "Installer name cannot be longer than 8 characters", + }) + installerName: string } From 7796213b46f9f2992187b9d54e6de8386c108182 Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Thu, 8 Jul 2021 00:02:56 +0700 Subject: [PATCH 08/10] refactor: Refactor & add klc generation to cli --- cli.ts | 18 +- generateKeylayout.ts | 268 +++++++++++++++++++ generateKlc.ts | 134 ++++++++++ input/Manoonchai-ColemakDH-Mod.json | 1 + main.ts | 397 +--------------------------- output/Manoonchai-ColemakDH-Mod.klc | Bin 0 -> 2396 bytes output/Manoonchai.klc | Bin 0 -> 1686 bytes test/main.test.ts | 13 +- 8 files changed, 428 insertions(+), 403 deletions(-) create mode 100644 generateKeylayout.ts create mode 100644 generateKlc.ts create mode 100644 output/Manoonchai-ColemakDH-Mod.klc create mode 100644 output/Manoonchai.klc diff --git a/cli.ts b/cli.ts index 38eace1..9de84ee 100644 --- a/cli.ts +++ b/cli.ts @@ -1,7 +1,8 @@ import prompts from "prompts" import fs from "fs" import path from "path" -import { generateLayout } from "./main" +import { generateKeylayout } from "./generateKeylayout" +import { generateKlc } from "./generateKlc" import { fixUnicode } from "./utils" // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -25,8 +26,9 @@ const choices = filenames.map((filename) => ({ fs.readFileSync(path.join(process.cwd(), "input", response.input), "utf8") ) + // Keylayout try { - const keylayoutXml = await generateLayout(jsonInput) + const keylayoutXml = await generateKeylayout(jsonInput) const layoutName = response.input.split(".")[0] const outputFilename = `output/${layoutName}.keylayout` fs.writeFileSync(outputFilename, keylayoutXml) @@ -38,4 +40,16 @@ const choices = filenames.map((filename) => ({ console.error(e) process.exit(1) } + + // Klc + try { + const layoutName = response.input.split(".")[0] + const outputFilename = `output/${layoutName}.klc` + await generateKlc(jsonInput, outputFilename) + + console.log(`Output : ${outputFilename}`) + } catch (e) { + console.error(e) + process.exit(1) + } })() diff --git a/generateKeylayout.ts b/generateKeylayout.ts new file mode 100644 index 0000000..d2f82eb --- /dev/null +++ b/generateKeylayout.ts @@ -0,0 +1,268 @@ +import { plainToClass } from "class-transformer" +import { validate } from "class-validator" +import { js2xml } from "xml-js" +import { Layout } from "./main" + +export async function generateKeylayout( + content: Record +): Promise { + const layout = plainToClass(Layout, content) + const errors = await validate(layout) + + if (errors.length) { + throw new Error(errors.map((e) => e.toString()).join(", ")) + } + + const defaultKeyMapSelects = [ + { layerName: "Base", layerValue: "anyControl? command?" }, + { layerName: "Shift", layerValue: "anyShift anyControl? command?" }, + { + layerName: "Command", + layerValue: "command anyShift? anyControl? anyOption?", + }, + { + layerName: "Alt", + layerValue: "command anyShift? anyControl? anyOption?", + }, + { + layerName: "Option", + layerValue: "anyOption anyShift? anyControl? command?", + }, + { + layerName: "Control", + layerValue: "anyControl anyShift? anyOption? command?", + }, + { layerName: "Caps", layerValue: "caps anyControl? command?" }, + { + layerName: "Caps+Shift", + layerValue: "caps anyShift anyControl? command?", + }, + { + layerName: "Caps+Option", + layerValue: "caps anyOption anyControl? command?", + }, + { + layerName: "Caps+Shift+Option", + layerValue: "caps anyShift anyOption anyControl? command?", + }, + ] + + const defaultKeys = [ + { code: 0, output: "a" }, + { code: 1, output: "s" }, + { code: 2, output: "d" }, + { code: 3, output: "f" }, + { code: 4, output: "h" }, + { code: 5, output: "g" }, + { code: 6, output: "z" }, + { code: 7, output: "x" }, + { code: 8, output: "c" }, + { code: 9, output: "v" }, + { code: 10, output: "§" }, + { code: 11, output: "b" }, + { code: 12, output: "q" }, + { code: 13, output: "w" }, + { code: 14, output: "e" }, + { code: 15, output: "r" }, + { code: 16, output: "y" }, + { code: 17, output: "t" }, + { code: 18, output: "1" }, + { code: 19, output: "2" }, + { code: 20, output: "3" }, + { code: 21, output: "4" }, + { code: 22, output: "6" }, + { code: 23, output: "5" }, + { code: 24, output: "=" }, + { code: 25, output: "9" }, + { code: 26, output: "7" }, + { code: 27, output: "-" }, + { code: 28, output: "8" }, + { code: 29, output: "0" }, + { code: 30, output: "]" }, + { code: 31, output: "o" }, + { code: 32, output: "u" }, + { code: 33, output: "[" }, + { code: 34, output: "i" }, + { code: 35, output: "p" }, + { code: 36, output: " ", unicode: true }, + { code: 37, output: "l" }, + { code: 38, output: "j" }, + { code: 39, output: "'" }, + { code: 40, output: "k" }, + { code: 41, output: ";" }, + { code: 42, output: "\\" }, + { code: 43, output: "," }, + { code: 44, output: "/" }, + { code: 45, output: "n" }, + { code: 46, output: "m" }, + { code: 47, output: "." }, + { code: 48, output: " ", unicode: true }, + { code: 49, output: " " }, + { code: 50, output: "`" }, + { code: 51, output: "", unicode: true }, + { code: 52, output: "", unicode: true }, + { code: 53, output: "", unicode: true }, + { code: 65, output: "." }, + { code: 66, output: "", unicode: true }, + { code: 67, output: "*" }, + { code: 69, output: "+" }, + { code: 70, output: "", unicode: true }, + { code: 71, output: "", unicode: true }, + { code: 72, output: "", unicode: true }, + { code: 75, output: "/" }, + { code: 76, output: "", unicode: true }, + { code: 77, output: "", unicode: true }, + { code: 78, output: "-" }, + { code: 81, output: "=" }, + { code: 82, output: "0" }, + { code: 83, output: "1" }, + { code: 84, output: "2" }, + { code: 85, output: "3" }, + { code: 86, output: "4" }, + { code: 87, output: "5" }, + { code: 88, output: "6" }, + { code: 89, output: "7" }, + { code: 91, output: "8" }, + { code: 92, output: "9" }, + { code: 96, output: "", unicode: true }, + { code: 97, output: "", unicode: true }, + { code: 98, output: "", unicode: true }, + { code: 99, output: "", unicode: true }, + { code: 100, output: "", unicode: true }, + { code: 101, output: "", unicode: true }, + { code: 102, output: "", unicode: true }, + { code: 103, output: "", unicode: true }, + { code: 104, output: "", unicode: true }, + { code: 105, output: "", unicode: true }, + { code: 106, output: "", unicode: true }, + { code: 107, output: "", unicode: true }, + { code: 108, output: "", unicode: true }, + { code: 109, output: "", unicode: true }, + { code: 110, output: "", unicode: true }, + { code: 111, output: "", unicode: true }, + { code: 112, output: "", unicode: true }, + { code: 113, output: "", unicode: true }, + { code: 114, output: "", unicode: true }, + { code: 115, output: "", unicode: true }, + { code: 116, output: " ", unicode: true }, + { code: 117, output: "", unicode: true }, + { code: 118, output: "", unicode: true }, + { code: 119, output: "", unicode: true }, + { code: 120, output: "", unicode: true }, + { code: 121, output: " ", unicode: true }, + { code: 122, output: "", unicode: true }, + { code: 123, output: "", unicode: true }, + { code: 124, output: "", unicode: true }, + { code: 125, output: "", unicode: true }, + { code: 126, output: "", unicode: true }, + ] + + const layoutLayers = layout.layers + const layoutKeyMapSelects = layoutLayers.map((layoutLayerName) => { + const keyMapSelect = defaultKeyMapSelects.find( + (defaultKeyMapSelect) => defaultKeyMapSelect.layerName === layoutLayerName + ) + + if (!keyMapSelect) { + throw new Error(`Invalid layer : ${layoutLayerName}`) + } + + return keyMapSelect.layerValue + }) + + const document = { + declaration: { attributes: { version: "1.1", encoding: "UTF-8" } }, + elements: [ + { + doctype: + 'keyboard SYSTEM "file://localhost/System/Library/DTDs/KeyboardLayout.dtd"', + type: "doctype", + }, + { + type: "element", + name: "keyboard", + attributes: { + group: "0", + id: "12345", + name: layout.name, + maxout: "1", + }, + elements: [ + { + type: "element", + name: "layouts", + elements: [ + { + type: "element", + name: "layout", + attributes: { + first: "0", + last: "0", + mapSet: "defaultKeyMapSet", + modifiers: "defaultModifierMap", + }, + }, + ], + }, + { + type: "element", + name: "modifierMap", + attributes: { id: "defaultModifierMap", defaultIndex: "0" }, + elements: layoutKeyMapSelects.map((keyMapSelect, idx) => ({ + type: "element", + name: "keyMapSelect", + attributes: { + mapIndex: `${idx}`, + }, + elements: [ + { + type: "element", + name: "modifier", + attributes: { + keys: keyMapSelect, + }, + }, + ], + })), + }, + { + type: "element", + name: "keyMapSet", + attributes: { id: "defaultKeyMapSet" }, + elements: layout.layers.map((_, idx) => ({ + type: "element", + name: "keyMap", + attributes: { index: idx }, + elements: defaultKeys.map(({ code, output, unicode }) => { + let overrideKey + + // Override only index 0-50 + // Since some symbols are the same in numpad's position and should not be overridden + if (code <= 50) { + overrideKey = layout.keys[output]?.[idx] || output + } else { + overrideKey = output + } + + if (overrideKey == "&") { + overrideKey = escape("&") + } + + if (unicode) { + overrideKey = encodeURIComponent(output) + } + return { + type: "element", + name: "key", + attributes: { code, output: overrideKey }, + } + }), + })), + }, + ], + }, + ], + } + + return js2xml(document, { spaces: 2 }) +} diff --git a/generateKlc.ts b/generateKlc.ts new file mode 100644 index 0000000..66ca2f0 --- /dev/null +++ b/generateKlc.ts @@ -0,0 +1,134 @@ +import { plainToClass } from "class-transformer" +import { validate } from "class-validator" +import fs from "fs" +import { Layout, WindowsAttributes } from "./main" + +export async function generateKlc( + content: Record, + outputPath: string +): Promise { + const layout = plainToClass(Layout, content) + const errors = await validate(layout) + + if (errors.length) { + throw new Error(errors.map((e) => e.toString()).join(", ")) + } + + const windowsErrors = await validate( + plainToClass(WindowsAttributes, layout.os.windows) + ) + + if (windowsErrors.length) { + throw new Error(windowsErrors.map((e) => e.toString()).join(", ")) + } + + const klcLocales = { + Thai: "th-TH", + } + + const klcShiftStates = { + Base: 0, + Shift: 1, + Ctrl: 2, + "Shift+Ctrl": 3, + Alt: 4, + "Shift+Alt": 5, + "Alt+Ctrl": 6, + "Shift+Alt+Ctrl": 7, + // From macOS config + Command: 4, + AltGr: 7, + Option: 7, + Control: 2, + } + + const klfDefaultLayout = { + "1": "02\t1", + "2": "03\t2", + "3": "04\t3", + "4": "05\t4", + "5": "06\t5", + "6": "07\t6", + "7": "08\t7", + "8": "09\t8", + "9": "0a\t9", + "0": "0b\t0", + "-": "0c\tOEM_MINUS", + "=": "0d\tOEM_PLUS", + q: "10\tQ", + w: "11\tW", + e: "12\tE", + r: "13\tR", + t: "14\tT", + y: "15\tY", + u: "16\tU", + i: "17\tI", + o: "18\tO", + p: "19\tP", + "[": "1a\tOEM_4", + "]": "1b\tOEM_6", + a: "1e\tA", + s: "1f\tS", + d: "20\tD", + f: "21\tF", + g: "22\tG", + h: "23\tH", + j: "24\tJ", + k: "25\tK", + l: "26\tL", + ";": "27\tOEM_1", + "'": "28\tOEM_7", + "`": "29\tOEM_3", + "\\": "2b\tOEM_5", + z: "2c\tZ", + x: "2d\tX", + c: "2e\tC", + v: "2f\tV", + b: "30\tB", + n: "31\tN", + m: "32\tM", + ",": "33\tOEM_COMMA", + ".": "34\tOEM_PERIOD", + "/": "35\tOEM_2", + } + + const lines = [ + `KBD\t${layout.os.windows.installerName}\t"${layout.language} ${layout.name} v${layout.version}"`, + `COPYRIGHT\t"MIT"`, + `COMPANY\t"${layout.os.windows.company}"`, + `LOCALENAME\t"${klcLocales[layout.language]}"`, + `LOCALEID\t"${layout.os.windows.localeId}"`, + `VERSION\t${layout.version}`, + ] + + const shiftStateLines: Array = layout.layers.map((layer, idx) => { + if (layer in klcShiftStates) { + return `${klcShiftStates[layer]}\t// Column ${idx + 4} : ${layer}` + } else { + throw new Error("Layer not valid") + } + }) + + shiftStateLines.unshift("SHIFTSTATE", "") + + const layoutLines = ["LAYOUT"] + + Object.entries(klfDefaultLayout).forEach(([key, value]) => { + const extensions = layout.layers.map((_, idx) => layout.keys[key][idx]) + layoutLines.push([value, "0", ...extensions].join("\t")) + }) + + fs.writeFileSync( + outputPath, + "\ufeff" + + [ + lines.join("\r\n\r\n"), + shiftStateLines.join("\r\n"), + layoutLines.join("\r\n"), + "ENDKBD", + ].join("\r\n\r\n"), + { + encoding: "utf16le", + } + ) +} diff --git a/input/Manoonchai-ColemakDH-Mod.json b/input/Manoonchai-ColemakDH-Mod.json index c30fc1e..bf40a92 100644 --- a/input/Manoonchai-ColemakDH-Mod.json +++ b/input/Manoonchai-ColemakDH-Mod.json @@ -5,6 +5,7 @@ "layers": ["Base", "Shift", "Command", "Option", "Control"], "os": { "windows": { + "installerName": "Mnc-DH", "company": "Manoonchai", "localeId": "0000041e" } diff --git a/main.ts b/main.ts index 28803fb..f961240 100644 --- a/main.ts +++ b/main.ts @@ -9,8 +9,6 @@ import { MaxLength, validate, } from "class-validator" -import { js2xml } from "xml-js" -import fs from "fs" export async function validateLayout( content: Record @@ -25,399 +23,6 @@ export async function validateLayout( return !errors.length } -export async function generateKlc( - content: Record -): Promise { - const layout = plainToClass(Layout, content) - const errors = await validate(layout) - - if (errors.length) { - throw new Error(errors.map((e) => e.toString()).join(", ")) - } - - const windowsErrors = await validate( - plainToClass(WindowsAttributes, layout.os.windows) - ) - - if (windowsErrors.length) { - throw new Error(windowsErrors.map((e) => e.toString()).join(", ")) - } - - const klcLocales = { - Thai: "th-TH", - } - - const klcShiftStates = { - Base: 0, - Shift: 1, - Ctrl: 2, - "Shift+Ctrl": 3, - Alt: 4, - "Shift+Alt": 5, - "Alt+Ctrl": 6, - "Shift+Alt+Ctrl": 7, - // From macOS config - Command: 4, - AltGr: 7, - Option: 7, - Control: 2, - } - - const klfDefaultLayout = { - "1": "02\t1", - "2": "03\t2", - "3": "04\t3", - "4": "05\t4", - "5": "06\t5", - "6": "07\t6", - "7": "08\t7", - "8": "09\t8", - "9": "0a\t9", - "0": "0b\t0", - "-": "0c\tOEM_MINUS", - "=": "0d\tOEM_PLUS", - q: "10\tQ", - w: "11\tW", - e: "12\tE", - r: "13\tR", - t: "14\tT", - y: "15\tY", - u: "16\tU", - i: "17\tI", - o: "18\tO", - p: "19\tP", - "[": "1a\tOEM_4", - "]": "1b\tOEM_6", - a: "1e\tA", - s: "1f\tS", - d: "20\tD", - f: "21\tF", - g: "22\tG", - h: "23\tH", - j: "24\tJ", - k: "25\tK", - l: "26\tL", - ";": "27\tOEM_1", - "'": "28\tOEM_7", - "`": "29\tOEM_3", - "\\": "2b\tOEM_5", - z: "2c\tZ", - x: "2d\tX", - c: "2e\tC", - v: "2f\tV", - b: "30\tB", - n: "31\tN", - m: "32\tM", - ",": "33\tOEM_COMMA", - ".": "34\tOEM_PERIOD", - "/": "35\tOEM_2", - } - - const lines = [ - `KBD\t${layout.os.windows.installerName}\t"${layout.language} ${layout.name} v${layout.version}"`, - `COPYRIGHT\t"MIT"`, - `COMPANY\t"${layout.os.windows.company}"`, - `LOCALENAME\t"${klcLocales[layout.language]}"`, - `LOCALEID\t"${layout.os.windows.localeId}"`, - `VERSION\t${layout.version}`, - ] - - const shiftStateLines: Array = layout.layers.map((layer, idx) => { - if (layer in klcShiftStates) { - return `${klcShiftStates[layer]}\t// Column ${idx + 4} : ${layer}` - } else { - throw new Error("Layer not valid") - } - }) - - shiftStateLines.unshift("SHIFTSTATE", "") - - const layoutLines = ["LAYOUT"] - - Object.entries(klfDefaultLayout).forEach(([key, value]) => { - const extensions = layout.layers.map((_, idx) => layout.keys[key][idx]) - layoutLines.push([value, "0", ...extensions].join("\t")) - }) - - fs.writeFileSync( - "./output/test.klc", - "\ufeff" + - [ - lines.join("\r\n\r\n"), - shiftStateLines.join("\r\n"), - layoutLines.join("\r\n"), - "ENDKBD", - ].join("\r\n\r\n"), - { - encoding: "utf16le", - } - ) -} - -export async function generateLayout( - content: Record -): Promise { - const layout = plainToClass(Layout, content) - const errors = await validate(layout) - - if (errors.length) { - throw new Error(errors.map((e) => e.toString()).join(", ")) - } - - const defaultKeyMapSelects = [ - { layerName: "Base", layerValue: "anyControl? command?" }, - { layerName: "Shift", layerValue: "anyShift anyControl? command?" }, - { - layerName: "Command", - layerValue: "command anyShift? anyControl? anyOption?", - }, - { - layerName: "Alt", - layerValue: "command anyShift? anyControl? anyOption?", - }, - { - layerName: "Option", - layerValue: "anyOption anyShift? anyControl? command?", - }, - { - layerName: "Control", - layerValue: "anyControl anyShift? anyOption? command?", - }, - { layerName: "Caps", layerValue: "caps anyControl? command?" }, - { - layerName: "Caps+Shift", - layerValue: "caps anyShift anyControl? command?", - }, - { - layerName: "Caps+Option", - layerValue: "caps anyOption anyControl? command?", - }, - { - layerName: "Caps+Shift+Option", - layerValue: "caps anyShift anyOption anyControl? command?", - }, - ] - - const defaultKeys = [ - { code: 0, output: "a" }, - { code: 1, output: "s" }, - { code: 2, output: "d" }, - { code: 3, output: "f" }, - { code: 4, output: "h" }, - { code: 5, output: "g" }, - { code: 6, output: "z" }, - { code: 7, output: "x" }, - { code: 8, output: "c" }, - { code: 9, output: "v" }, - { code: 10, output: "§" }, - { code: 11, output: "b" }, - { code: 12, output: "q" }, - { code: 13, output: "w" }, - { code: 14, output: "e" }, - { code: 15, output: "r" }, - { code: 16, output: "y" }, - { code: 17, output: "t" }, - { code: 18, output: "1" }, - { code: 19, output: "2" }, - { code: 20, output: "3" }, - { code: 21, output: "4" }, - { code: 22, output: "6" }, - { code: 23, output: "5" }, - { code: 24, output: "=" }, - { code: 25, output: "9" }, - { code: 26, output: "7" }, - { code: 27, output: "-" }, - { code: 28, output: "8" }, - { code: 29, output: "0" }, - { code: 30, output: "]" }, - { code: 31, output: "o" }, - { code: 32, output: "u" }, - { code: 33, output: "[" }, - { code: 34, output: "i" }, - { code: 35, output: "p" }, - { code: 36, output: " ", unicode: true }, - { code: 37, output: "l" }, - { code: 38, output: "j" }, - { code: 39, output: "'" }, - { code: 40, output: "k" }, - { code: 41, output: ";" }, - { code: 42, output: "\\" }, - { code: 43, output: "," }, - { code: 44, output: "/" }, - { code: 45, output: "n" }, - { code: 46, output: "m" }, - { code: 47, output: "." }, - { code: 48, output: " ", unicode: true }, - { code: 49, output: " " }, - { code: 50, output: "`" }, - { code: 51, output: "", unicode: true }, - { code: 52, output: "", unicode: true }, - { code: 53, output: "", unicode: true }, - { code: 65, output: "." }, - { code: 66, output: "", unicode: true }, - { code: 67, output: "*" }, - { code: 69, output: "+" }, - { code: 70, output: "", unicode: true }, - { code: 71, output: "", unicode: true }, - { code: 72, output: "", unicode: true }, - { code: 75, output: "/" }, - { code: 76, output: "", unicode: true }, - { code: 77, output: "", unicode: true }, - { code: 78, output: "-" }, - { code: 81, output: "=" }, - { code: 82, output: "0" }, - { code: 83, output: "1" }, - { code: 84, output: "2" }, - { code: 85, output: "3" }, - { code: 86, output: "4" }, - { code: 87, output: "5" }, - { code: 88, output: "6" }, - { code: 89, output: "7" }, - { code: 91, output: "8" }, - { code: 92, output: "9" }, - { code: 96, output: "", unicode: true }, - { code: 97, output: "", unicode: true }, - { code: 98, output: "", unicode: true }, - { code: 99, output: "", unicode: true }, - { code: 100, output: "", unicode: true }, - { code: 101, output: "", unicode: true }, - { code: 102, output: "", unicode: true }, - { code: 103, output: "", unicode: true }, - { code: 104, output: "", unicode: true }, - { code: 105, output: "", unicode: true }, - { code: 106, output: "", unicode: true }, - { code: 107, output: "", unicode: true }, - { code: 108, output: "", unicode: true }, - { code: 109, output: "", unicode: true }, - { code: 110, output: "", unicode: true }, - { code: 111, output: "", unicode: true }, - { code: 112, output: "", unicode: true }, - { code: 113, output: "", unicode: true }, - { code: 114, output: "", unicode: true }, - { code: 115, output: "", unicode: true }, - { code: 116, output: " ", unicode: true }, - { code: 117, output: "", unicode: true }, - { code: 118, output: "", unicode: true }, - { code: 119, output: "", unicode: true }, - { code: 120, output: "", unicode: true }, - { code: 121, output: " ", unicode: true }, - { code: 122, output: "", unicode: true }, - { code: 123, output: "", unicode: true }, - { code: 124, output: "", unicode: true }, - { code: 125, output: "", unicode: true }, - { code: 126, output: "", unicode: true }, - ] - - const layoutLayers = layout.layers - const layoutKeyMapSelects = layoutLayers.map((layoutLayerName) => { - const keyMapSelect = defaultKeyMapSelects.find( - (defaultKeyMapSelect) => defaultKeyMapSelect.layerName === layoutLayerName - ) - - if (!keyMapSelect) { - throw new Error(`Invalid layer : ${layoutLayerName}`) - } - - return keyMapSelect.layerValue - }) - - const document = { - declaration: { attributes: { version: "1.1", encoding: "UTF-8" } }, - elements: [ - { - doctype: - 'keyboard SYSTEM "file://localhost/System/Library/DTDs/KeyboardLayout.dtd"', - type: "doctype", - }, - { - type: "element", - name: "keyboard", - attributes: { - group: "0", - id: "12345", - name: layout.name, - maxout: "1", - }, - elements: [ - { - type: "element", - name: "layouts", - elements: [ - { - type: "element", - name: "layout", - attributes: { - first: "0", - last: "0", - mapSet: "defaultKeyMapSet", - modifiers: "defaultModifierMap", - }, - }, - ], - }, - { - type: "element", - name: "modifierMap", - attributes: { id: "defaultModifierMap", defaultIndex: "0" }, - elements: layoutKeyMapSelects.map((keyMapSelect, idx) => ({ - type: "element", - name: "keyMapSelect", - attributes: { - mapIndex: `${idx}`, - }, - elements: [ - { - type: "element", - name: "modifier", - attributes: { - keys: keyMapSelect, - }, - }, - ], - })), - }, - { - type: "element", - name: "keyMapSet", - attributes: { id: "defaultKeyMapSet" }, - elements: layout.layers.map((_, idx) => ({ - type: "element", - name: "keyMap", - attributes: { index: idx }, - elements: defaultKeys.map(({ code, output, unicode }) => { - let overrideKey - - // Override only index 0-50 - // Since some symbols are the same in numpad's position and should not be overridden - if (code <= 50) { - overrideKey = layout.keys[output]?.[idx] || output - } else { - overrideKey = output - } - - if (overrideKey == "&") { - overrideKey = escape("&") - } - - if (unicode) { - overrideKey = encodeURIComponent(output) - } - return { - type: "element", - name: "key", - attributes: { code, output: overrideKey }, - } - }), - })), - }, - ], - }, - ], - } - - return js2xml(document, { spaces: 2 }) -} - export class Layout { @IsString() name: string @@ -447,7 +52,7 @@ interface OSAttributes { windows: WindowsAttributes } -class WindowsAttributes { +export class WindowsAttributes { @IsString() company: string diff --git a/output/Manoonchai-ColemakDH-Mod.klc b/output/Manoonchai-ColemakDH-Mod.klc new file mode 100644 index 0000000000000000000000000000000000000000..4a93fcb1bdd923cea83bc4d84472bb34431cb76f GIT binary patch literal 2396 zcmZ{m`E!$16o!wEqZS3+_oWn2w$da`HyuaPJv1@33tCw#i&SZgP*C`RKV5y^?>)C< z7(1C~&bc|yz2Duk{QJ*ROvh{tL^pQgW(<=r(i)1jxEq_X6@yeZsqONTQQmzV^*A3j z%)@c0VkUaA92>C`?O0%xwQ8I$t+f}dE?z5sz}1ufZ~W(@b{KUAyAyNtXkpJ)z4n;N zFe5Majot6It5pp#!}4k}Hw&G6gRd(|LdpvDDZ=07+2o*c!K8I_Nvqy3a8 zNAX%l<>Tp0lFO5$_*F*b6X{Hm%afycCZqDnbT-N5$x%F&QTbFlH_3b4y>48GHaB&h z8|gm7#Ot7Fd^Wdemp;pUa-R42j>=h3H0n{WX7D5LX(lsA2I3v~lib+Rsr{7B`_zxZ zU$l)Koti$M8s{waH{cGrv7=L4N#`W>!*HLrv7=L4OXm#rFF`lX*wLwNr1L5D0r;J^ zv7=L4Pp3|O9PZIJc64g(bY7zV5j>)8?C8{b=`^Xo1y5)jJ36)HbUvYe1iqzh?C8YH z$!Lw*rvG|qAAEqF+7?C2iMr}HuOL$F8N*wH;$Naq;!FW@)Y#*XfRerxosVd|g3Ho38* zJ-V1qgZe1!&^C5-50=tdr2aB!Ka3siUniY5^=a6mZS3eCl>OEkHLtJn24R=n*!6wz zr&9goPT&ox&pvi_FUq)Dv)mi-ETi%p)u+8t-S6URU2;bl_f5D$Zal8O8GcD;lKKgF zO551cH^I;8%u@dx{-$m0XrE@%`H=cNXl!Fgcjr<%L)6~|jcw$NR&$+B=XL5A;W2Gv zNBgSZTCM99>Pzqkxv`_W(@kfLdI#>(Hg>d+WuNrFqsyz@|M&1u!8sh`KDtY#&vNzK o-=}A7z%?sQ*;|0dNl4ng9R* literal 0 HcmV?d00001 diff --git a/output/Manoonchai.klc b/output/Manoonchai.klc new file mode 100644 index 0000000000000000000000000000000000000000..7a0a642f8d5cde1eb5a354730d0035df3726fc71 GIT binary patch literal 1686 zcmZ{l=~ELy5XD=|QiBrj`w9V+!$3|Bt1QW#CA%ah0mTnuR4A!Z`5^kyKV5xq_Dx7> zSzA*x`+L1T-94Sj?_X`JSj`ePuunF$LyPRIeZYHqL{eZw&?7w5V!M?6u$(Pf7M7DX z;YQUu*0o*hSzt|T!2|t7cpLm>6~V4-Td<8S{`G(2HH6nHtV65gi!%Jx-B-tmBm>W_ zG2i31fy*n62>nw;#QS5`JMh%AKH}@Zb3*y}Z2IsVSOYusG0GVAQKnf~FXKrevLpL! z-|Y)(ld>YlGmHu<_6>W{SMEQyCE50o$DtkA@yL6K-F6YvHuj{4ti6X1?&-(8cpi-Y z$vIDn7qF&A&Ur(;h&45G&eP&0tf`T6o)KTcni@IhS@Bh@sgZMjE53#`HFD06#n-W> zM$UObe26tQa?W$&?7$A{9oe4ipDom)FS`upJ=uL6v+JTmPOP3oS#(x8=l!L=t|k+9 zH5s1^EA~qK2KXg$R*XHys20I5h_hln@mcUmaaL>~z5xD6oE2k-)6YHdgg7g<6gI$bI-Ym=@eX00)8J)s*0&*k2Yf=D^=*nzfj<;yeOuxg@cZJ-VN*O0UJz$} z+u|+o)8fo26c4~F;;gUFo3Sz8C(u*U<2uJ(M_cNv(Br!(?>f|f&seFy!}#g?Ebo4$ zUT197m+}2Y`Da(Z5nltJ5oZ^_7O#On5a&5m#czQ(#CZ-o;%V?}ju+g174h@no8rus z_Z#Cm1Kt+rIYi { @@ -43,7 +45,7 @@ describe("generateLayout", () => { ) ) - const manoonchaiXml = await generateLayout(manoonchaiJson) + const manoonchaiXml = await generateKeylayout(manoonchaiJson) expect(manoonchaiXml).toBeDefined() @@ -423,12 +425,13 @@ describe("generateKlc", () => { ) ) - await generateKlc(inputJson) + const filepath = "output/test.klc" + await generateKlc(inputJson, filepath) - expect(fs.existsSync("output/test.klc")).toBeTruthy() + expect(fs.existsSync(filepath)).toBeTruthy() const lines = fs - .readFileSync("output/test.klc", "utf16le") + .readFileSync(filepath, "utf16le") .split("\r\n") .filter(Boolean) From 908acf7306e13c6750de275c7e03a2e62082c77f Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Thu, 8 Jul 2021 01:03:34 +0700 Subject: [PATCH 09/10] fix: Exclude layer 4 since KLC does not support it --- generateKlc.ts | 24 +++++++++++++++++++----- output/Manoonchai-ColemakDH-Mod.klc | Bin 2396 -> 2256 bytes output/Manoonchai.klc | Bin 1686 -> 1690 bytes 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/generateKlc.ts b/generateKlc.ts index 66ca2f0..4826291 100644 --- a/generateKlc.ts +++ b/generateKlc.ts @@ -31,12 +31,12 @@ export async function generateKlc( Shift: 1, Ctrl: 2, "Shift+Ctrl": 3, - Alt: 4, + Alt: 4, // Layer number 4 is not supported in KLC "Shift+Alt": 5, "Alt+Ctrl": 6, "Shift+Alt+Ctrl": 7, // From macOS config - Command: 4, + Command: 4, // Layer number 4 is not supported in KLC AltGr: 7, Option: 7, Control: 2, @@ -101,9 +101,23 @@ export async function generateKlc( `VERSION\t${layout.version}`, ] - const shiftStateLines: Array = layout.layers.map((layer, idx) => { + const shiftStateLines: Array = [] + + layout.layers.forEach((layer, idx) => { if (layer in klcShiftStates) { - return `${klcShiftStates[layer]}\t// Column ${idx + 4} : ${layer}` + // Filter out layer 4 (Command) + if (klcShiftStates[layer] === 4) { + layout.keys = Object.fromEntries( + Object.entries(layout.keys).map(([key, keys]) => { + keys.splice(idx, 1) + return [key, keys] + }) + ) + } else { + shiftStateLines.push( + `${klcShiftStates[layer]}\t// Column ${idx + 4} : ${layer}` + ) + } } else { throw new Error("Layer not valid") } @@ -125,7 +139,7 @@ export async function generateKlc( lines.join("\r\n\r\n"), shiftStateLines.join("\r\n"), layoutLines.join("\r\n"), - "ENDKBD", + "ENDKBD\r\n", ].join("\r\n\r\n"), { encoding: "utf16le", diff --git a/output/Manoonchai-ColemakDH-Mod.klc b/output/Manoonchai-ColemakDH-Mod.klc index 4a93fcb1bdd923cea83bc4d84472bb34431cb76f..4718474fc7614c6421adcf0ad1fc074766f365ab 100644 GIT binary patch delta 220 zcmWm6uL{Co9EIU;oBQL?pvhqRf?+|%@Crl?3I;KlynqFx7`y_5h(9ri!AlUsqG5Oe z%VIJNuVVNvJco12`82b}{a{FHoMd5Y+2V#5eyCYmLd>zj)rusuLNO~^Iv8VxBNDt( zK-*eIi0wemNa;CzM@ttotZ~5;UpQ_c5ti5?arr|5?eT{gdpx{Ia<3=tYS5}HUzLVx Lss?JJ`eeEP9t%H= delta 338 zcmW;Ay)Q#?7{&4DaozSJB9)X$BKZv+uD80qr9>p?By0vL;Zo|Yz4iW*7{nkV63v-R z1~FM|29rTd605`}A|`);#fhAJKf`&Ay*J)nsHRZS=6G6KrH>6uC`B%ilH_rwm)ZqW zWi*K_?M8umIHf7vQXelgjUU>Bm}RsV9?f9F^0XZ%R#FFW&u1NPbQiyL5iQk5%NV3{ z2Bm~?#Yz`5f#bxOU| N{+E?;8Ie&LqJIZrQ`!Ik diff --git a/output/Manoonchai.klc b/output/Manoonchai.klc index 7a0a642f8d5cde1eb5a354730d0035df3726fc71..311bb49db70d811bf4eb8a51c4a1cff0d9fac1c8 100644 GIT binary patch delta 12 TcmbQnJBxS2G&UAq1}+8w7&Zd3 delta 7 OcmbQmJB@e4G&TSV?*f+q From d3fab38653e5a4678c18352f5135cb4d7305ff6b Mon Sep 17 00:00:00 2001 From: Manassarn Manoonchai Date: Thu, 8 Jul 2021 01:05:39 +0700 Subject: [PATCH 10/10] chore: Update test klc file --- output/test.klc | Bin 1686 -> 1690 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/output/test.klc b/output/test.klc index 7a0a642f8d5cde1eb5a354730d0035df3726fc71..311bb49db70d811bf4eb8a51c4a1cff0d9fac1c8 100644 GIT binary patch delta 12 TcmbQnJBxS2G&UAq1}+8w7&Zd3 delta 7 OcmbQmJB@e4G&TSV?*f+q