├── .gitignore
├── README.md
├── VialToKeymapDrawerJS.hxproj
├── VialToKeymapDrawerNeko.hxproj
├── bin
├── VialToKeymapDrawer-example.bat
├── VialToKeymapDrawer-example.sh
├── pack.bat
├── test-all.bat
├── test-one.bat
├── test.bat
├── yal-sofle.yaml
└── yal-sofle
│ ├── encoder-defs.txt
│ ├── key-labels.txt
│ ├── layer-names.txt
│ ├── move-defs.txt
│ └── yal-sofle.vil
├── build-neko.hxml
├── build-web.hxml
├── docs
├── FileSaver.min.js
├── VialToKeymapDrawer.js
├── examples
│ ├── cepstrum.json
│ ├── cepstrum.vil
│ ├── corvett-piantor.json
│ ├── corvett-piantor.vil
│ ├── key-range-editor.mp4
│ ├── yal-sofle.json
│ └── yal-sofle.vil
├── index.html
├── qmk_keyboards.js
├── style.css
└── style.less
├── example.svg
├── media
├── cli.png
├── example-blue.png
└── web.png
└── src
├── CommandLineOption.hx
├── Main.hx
├── SysMain.hx
├── VilToDrawer.hx
├── VilToDrawerOpt.hx
├── drawer
└── DrawerKeymap.hx
├── tools
├── BuildDate.hx
├── ERegTools.hx
├── JsonParserWithComments.hx
└── JsonPrinterWithOrder.hx
├── via
├── ViaKeyNames.hx
├── ViaKeyNamesGen.hx
└── key.ts
├── vial
├── VialKey.hx
├── VialKeyNames.hx
├── VialKeyNamesGen.hx
├── VialKeyNamesGen.n
├── VialKeymap.hx
├── VialKeysWithShiftState.hx
└── keycodes.py
└── web
└── KeyRangeCreator.hx
/.gitignore:
--------------------------------------------------------------------------------
1 | /docs/style.min.css
2 | /docs/VialToKeymapDrawer.js.map
3 | *.n
4 | /bin/*.exe
5 | /bin/*.dll
6 | /bin/*.ndll
7 | /bin/*.zip
8 | /bin/*.svg
9 | /bin/*.png
10 | /bin/yal-sofle-*.yaml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vial layout to Keymap Drawer converter
2 |
3 | **Quick links:** [web version](https://yal-tools.github.io/vial-to-keymap-drawer/)
4 | · [itch](https://yellowafterlife.itch.io/vial-to-keymap-drawer) (for donations and pre-built binaries)
5 |
6 | Takes your Vial `.vil` layouts and converts them to YAML that you can pass to
7 | [keymap-drawer](https://github.com/caksoylar/keymap-drawer)
8 | to render your layout to SVG/PNG images that you can show to people instead
9 | of taking screenshots of Vial configurator.
10 |
11 | Also lets you label your layers/keys to make the keymap easier to read
12 | and displays combos/tap dances.
13 |
14 | 
15 |
16 | ## Inevitable caveats
17 |
18 | Apparently the order in which Vial stores keys in `.vil` files does not necessarily match up
19 | with how keys are defined in QMK, therefore the keys may appear out of order, depending on the keyboard.
20 |
21 | And if your keyboard has optional keys (like a wide LShift vs narrow LShift + extra key), the way those are represented is not standardized either - they could be set to `-1`, or just left as blanks in the layout file.
22 |
23 | For this I'm giving you a bunch of tools to deal with common and uncommon mishaps -
24 | from simple (get rid of extra blank/`-1` keys, auto-fix key order in split Vial keyboards)
25 | to advanced (specify key order in a [visual editor](./docs/examples/key-range-editor.mp4) or hand-write key locations to swap or pick).
26 |
27 | This makes this tool slightly less entry-level, but good news - you'll only need to do this once per keyboard.
28 | Perhaps contribute an example `.vil` + `.json` if it's a particularly quirky one?
29 |
30 | ## Using
31 |
32 | For the web version, hopefully the use should be apparent enough,
33 | and you can load an example configuration to see how to deal with keys being out of order.
34 |
35 | The native version is invoked through command-line/terminal.
36 |
37 | On Windows this is done as following:
38 | ```
39 | .\VialToKeymapDrawer.exe <...options>
40 | ```
41 |
42 | On Mac/Linux this is done as following (you'll need [Neko VM](https://nekovm.org/download/) installed):
43 | ```
44 | neko ./VialToKeymapDrawer.n <...options>
45 | ```
46 | For a full list of supported options, run without arguments or with `--help`;
47 |
48 | Options might look like this, for instance (for a Sofle Choc):
49 | ```
50 | --keyboard splitkb/aurora/sofle_v2/rev1 --half-after-half --mirror-right-half --move-defs yal-sofle/move-defs.txt --key-labels yal-sofle/key-labels.txt --layer-names yal-sofle/layer-names.txt --vil yal-sofle/yal-sofle.vil yal-sofle.yaml
51 | ```
52 | Other keyboards may require less tinkering.
53 |
54 | ## Building
55 |
56 | Web:
57 | ```text
58 | haxe build-web.hxml
59 | ```
60 | Native:
61 | ```text
62 | haxe build-neko.hxml
63 | nekotools boot bin/VialToKeymapDrawer.n
64 | ```
65 |
66 | ## License and credits
67 |
68 | Tool by YellowAfterlife
69 |
70 | - Written in [Haxe](https://haxe.org).
71 | - Uses [FileSaver.js](https://github.com/eligrey/FileSaver.js/).
72 |
73 | Display data comes from the following:
74 |
75 | - Vial key labels: [Vial GUI](https://github.com/vial-kb/vial-gui)
76 | - VIA key labels: [VIA GUI](https://github.com/the-via/app)
77 | - QMK keyboard list: [QMK Configurator](https://config.qmk.fm/)
78 | By which I mean not the _source code_ of QMK configurator, but taken straight from the web page.
79 |
80 | As result of sourcing data from software licensed under GPLv2, this tool is probably also GPLv2.
81 |
--------------------------------------------------------------------------------
/VialToKeymapDrawerJS.hxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/VialToKeymapDrawerNeko.hxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/bin/VialToKeymapDrawer-example.bat:
--------------------------------------------------------------------------------
1 | cmd /C .\VialToKeymapDrawer.exe --keyboard splitkb/aurora/sofle_v2/rev1 --half-after-half --mirror-right-half --move-defs yal-sofle/move-defs.txt --key-labels yal-sofle/key-labels.txt --layer-names yal-sofle/layer-names.txt --vil yal-sofle/yal-sofle.vil yal-sofle.yaml
2 | pause
--------------------------------------------------------------------------------
/bin/VialToKeymapDrawer-example.sh:
--------------------------------------------------------------------------------
1 | neko VialToKeymapDrawer.n --keyboard splitkb/aurora/sofle_v2/rev1 --half-after-half --mirror-right-half --move-defs yal-sofle/move-defs.txt --key-labels yal-sofle/key-labels.txt --layer-names yal-sofle/layer-names.txt --vil yal-sofle/yal-sofle.vil yal-sofle.yaml
2 |
--------------------------------------------------------------------------------
/bin/pack.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | del /Q VialToKeymapDrawer-Windows.zip
3 | cmd /C nekotools boot VialToKeymapDrawer.n
4 | cmd /C 7z a VialToKeymapDrawer-Windows.zip VialToKeymapDrawer.exe *.dll *.ndll ..\README.md yal-sofle VialToKeymapDrawer-example.bat
5 |
6 | del /Q VialToKeymapDrawer-Unix.zip
7 | cmd /C 7z a VialToKeymapDrawer-Unix.zip VialToKeymapDrawer.n VialToKeymapDrawer-example.sh yal-sofle ..\README.md
8 |
9 | set /p ver="Version?: "
10 | echo Uploading %ver%...
11 |
12 | cmd /C itchio-butler push VialToKeymapDrawer-Windows.zip yellowafterlife/vial-to-keymap-drawer:windows --userversion=%ver%
13 | cmd /C itchio-butler push VialToKeymapDrawer-Unix.zip yellowafterlife/vial-to-keymap-drawer:unix --userversion=%ver%
14 | pause
--------------------------------------------------------------------------------
/bin/test-all.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cmd /C test-one 0
3 | cmd /C test-one 1
4 | cmd /C test-one 2
5 | cmd /C test-one 3
6 | pause
--------------------------------------------------------------------------------
/bin/test-one.bat:
--------------------------------------------------------------------------------
1 | neko VialToKeymapDrawer.n --keyboard sofle_rotated --half-after-half --mirror-right-half --move-defs yal-sofle/move-defs.txt --key-labels yal-sofle/key-labels.txt --layer-names yal-sofle/layer-names.txt --encoder-defs yal-sofle/encoder-defs.txt --vil yal-sofle/yal-sofle.vil --layer %1 yal-sofle-%1.yaml
2 | keymap draw yal-sofle-%1.yaml>yal-sofle-%1.svg
3 | cmd /C inkscape yal-sofle-%1.svg --export-filename="yal-sofle-%1.png"
4 | cmd /C magick yal-sofle-%1.png -background #889EC5 -flatten yal-sofle-%1-blue.png
5 |
--------------------------------------------------------------------------------
/bin/test.bat:
--------------------------------------------------------------------------------
1 | cd bin
2 | neko VialToKeymapDrawer.n --keyboard sofle_rotated --half-after-half --mirror-right-half --move-defs yal-sofle/move-defs.txt --key-labels yal-sofle/key-labels.txt --layer-names yal-sofle/layer-names.txt --encoder-defs yal-sofle/encoder-defs.txt --vil yal-sofle/yal-sofle.vil --layer 0 yal-sofle.yaml
3 | keymap draw yal-sofle.yaml>yal-sofle.svg
4 | pause
--------------------------------------------------------------------------------
/bin/yal-sofle.yaml:
--------------------------------------------------------------------------------
1 | layout: {"qmk_keyboard":"sofle_rotated"}
2 | layers:
3 | DEF:
4 | - {"s":"~","t":"`"}
5 | - {"s":"!","t":"1"}
6 | - {"s":"@","t":"2"}
7 | - {"s":"#","t":"3"}
8 | - {"s":"$","t":"4"}
9 | - {"s":"%","t":"5"}
10 | - {"s":"^","t":"6"}
11 | - {"s":"&","t":"7"}
12 | - {"s":"*","t":"8"}
13 | - {"s":"(","t":"9"}
14 | - {"s":")","t":"0"}
15 | - Bksp
16 | - Tab
17 | - Q
18 | - W
19 | - E
20 | - R
21 | - T
22 | - Y
23 | - U
24 | - I
25 | - O
26 | - P
27 | - {"s":"{","t":"["}
28 | - LShift
29 | - A
30 | - S
31 | - D
32 | - F
33 | - G
34 | - H
35 | - J
36 | - K
37 | - L
38 | - {"s":":","t":";"}
39 | - {"h":"RCtrl","s":"TD(1)","t":"\" '"}
40 | - LCtrl
41 | - Z
42 | - X
43 | - C
44 | - V
45 | - B
46 | - Home
47 | - End
48 | - N
49 | - M
50 | - {"s":"<","t":","}
51 | - {"s":">","t":"."}
52 | - {"s":"?","t":"/"}
53 | - {"h":"RSFT","s":"}","t":"]"}
54 | - LGui
55 | - LAlt
56 | - Space
57 | - {"h":"MO ALT","t":"Esc"}
58 | - {"h":"MO NUM","s":"TD(0)","t":"Enter"}
59 | - {"s":"|","t":"\\"}
60 | - {"s":"_","t":"-"}
61 | - {"s":"+","t":"="}
62 | - {"h":"RALT","t":"Menu"}
63 | - "NAV ON"
64 |
--------------------------------------------------------------------------------
/bin/yal-sofle/encoder-defs.txt:
--------------------------------------------------------------------------------
1 | 4,5 => 0
2 | 9,5 => 1
3 |
--------------------------------------------------------------------------------
/bin/yal-sofle/key-labels.txt:
--------------------------------------------------------------------------------
1 | "F16" => Mute
2 | "F21" => Scroll Down
3 | "F20" => Scroll Up
4 | "F22" => Lang
5 | "LGui+F22" => Magic 1
6 | "LAlt+F22" => Magic 2
7 | "LCtl+F22" => Magic 3
8 | "M1" => NAV ON
9 | "M0" => NAV OFF
10 | "M2" => NUM ON
11 | "M3" => NUM OFF
12 | "Browser\nFav." => Search
--------------------------------------------------------------------------------
/bin/yal-sofle/layer-names.txt:
--------------------------------------------------------------------------------
1 | DEF
2 | NAV
3 | ALT
4 | NUM
--------------------------------------------------------------------------------
/bin/yal-sofle/move-defs.txt:
--------------------------------------------------------------------------------
1 | 4,5 => 3,6
2 | 9,5 => 8,6
--------------------------------------------------------------------------------
/bin/yal-sofle/yal-sofle.vil:
--------------------------------------------------------------------------------
1 | {"version": 1, "uid": 12888985424971681029, "layout": [[["KC_GRAVE", "KC_1", "KC_2", "KC_3", "KC_4", "KC_5"], ["KC_TAB", "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T"], ["KC_LSHIFT", "KC_A", "KC_S", "KC_D", "KC_F", "KC_G"], ["KC_LCTRL", "KC_Z", "KC_X", "KC_C", "KC_V", "KC_B"], ["KC_LGUI", "KC_LALT", "KC_SPACE", "LT2(KC_ESCAPE)", "TD(0)", "KC_HOME"], ["KC_BSPACE", "KC_0", "KC_9", "KC_8", "KC_7", "KC_6"], ["KC_LBRACKET", "KC_P", "KC_O", "KC_I", "KC_U", "KC_Y"], ["TD(1)", "KC_SCOLON", "KC_L", "KC_K", "KC_J", "KC_H"], ["RSFT_T(KC_RBRACKET)", "KC_SLASH", "KC_DOT", "KC_COMMA", "KC_M", "KC_N"], ["M1", "RALT_T(KC_APPLICATION)", "KC_EQUAL", "KC_MINUS", "KC_BSLASH", "KC_END"]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "M0", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_DELETE", "KC_END", "KC_UP", "KC_HOME", "RCTL(KC_LEFT)"], ["KC_TRNS", "KC_TRNS", "KC_RIGHT", "KC_DOWN", "KC_LEFT", "RCTL(KC_RIGHT)"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_PGDOWN", "KC_PGUP"], ["M0", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"]], [["KC_ESCAPE", "KC_F1", "KC_F2", "KC_F3", "KC_F4", "KC_F5"], ["KC_TAB", "KC_F11", "KC_F12", "KC_PAUSE", "KC_PSCREEN", "KC_WFAV"], ["KC_LSHIFT", "KC_LGUI", "KC_LSHIFT", "KC_LCTRL", "KC_LALT", "KC_F22"], ["KC_LCTRL", "LGUI(KC_F22)", "LALT(KC_F22)", "LCTL(KC_F22)", "M2", "KC_F16"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_F10", "KC_F9", "KC_F8", "KC_F7", "KC_F6"], ["KC_TRNS", "KC_DELETE", "KC_END", "KC_UP", "KC_HOME", "KC_PGUP"], ["KC_TRNS", "KC_TRNS", "KC_RIGHT", "KC_DOWN", "KC_LEFT", "KC_PGDOWN"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_INSERT", "KC_CAPSLOCK"], ["KC_SCROLLLOCK", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_HOME", "KC_UP", "KC_END", "KC_PGUP", "KC_NO"], ["KC_TRNS", "KC_LEFT", "KC_DOWN", "KC_RIGHT", "KC_PGDOWN", "KC_NO"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "M3", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_KP_MINUS", "KC_KP_ASTERISK", "KC_KP_SLASH", "KC_EQUAL", "KC_NUMLOCK"], ["KC_TRNS", "KC_DELETE", "KC_KP_9", "KC_KP_8", "KC_KP_7", "LSFT(KC_9)"], ["KC_TRNS", "KC_KP_PLUS", "KC_KP_6", "KC_KP_5", "KC_KP_4", "KC_BSPACE"], ["KC_TRNS", "KC_KP_ENTER", "KC_KP_3", "KC_KP_2", "KC_KP_1", "LSFT(KC_0)"], ["M3", "KC_KP_DOT", "KC_KP_0", "KC_KP_0", "KC_COMMA", "KC_TRNS"]]], "encoder_layout": [[["KC_DOWN", "KC_UP"], ["KC_RIGHT", "KC_LEFT"]], [["KC_TRNS", "KC_TRNS"], ["KC_F21", "KC_F20"]], [["KC_VOLD", "KC_VOLU"], ["KC_DOWN", "KC_UP"]], [["KC_TRNS", "KC_TRNS"], ["KC_F21", "KC_F20"]]], "layout_options": -1, "macro": [[["tap", "KC_F23", "TG(1)"]], [["tap", "RCTL(KC_F23)", "TG(1)"]], [["tap", "RSFT(KC_F23)", "TG(3)"]], [["tap", "KC_F23", "TG(3)"]], [["tap", "KC_END", "LSFT(KC_HOME)"]], [], [], [], [], [], [], [], [], [], [], []], "vial_protocol": 5, "via_protocol": 9, "tap_dance": [["KC_ENTER", "MO(3)", "KC_NO", "KC_NO", 250], ["KC_QUOTE", "KC_RCTRL", "KC_NO", "KC_NO", 200], ["LSFT(KC_MINUS)", "KC_LSHIFT", "KC_NO", "KC_NO", 120], ["KC_MINUS", "KC_RALT", "KC_NO", "KC_NO", 200], ["LSFT(KC_8)", "LSFT(KC_6)", "KC_NO", "KC_NO", 160], ["LSFT(KC_8)", "LSFT(KC_3)", "KC_NO", "KC_NO", 160], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 160], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 160]], "combo": [["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"]], "key_override": [], "settings": {}}
--------------------------------------------------------------------------------
/build-neko.hxml:
--------------------------------------------------------------------------------
1 | -cp src
2 | -neko bin/VialToKeymapDrawer.n
3 | -main SysMain
--------------------------------------------------------------------------------
/build-web.hxml:
--------------------------------------------------------------------------------
1 | -cp src
2 | -js docs/VialToKeymapDrawer.js
3 | -main Main
--------------------------------------------------------------------------------
/docs/FileSaver.min.js:
--------------------------------------------------------------------------------
1 | (function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(b,c,d){var e=new XMLHttpRequest;e.open("GET",b),e.responseType="blob",e.onload=function(){a(e.response,c,d)},e.onerror=function(){console.error("could not download file")},e.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(a,b,d,e){if(e=e||open("","_blank"),e&&(e.document.title=e.document.body.innerText="downloading..."),"string"==typeof a)return c(a,b,d);var g="application/octet-stream"===a.type,h=/constructor/i.test(f.HTMLElement)||f.safari,i=/CriOS\/[\d]+/.test(navigator.userAgent);if((i||g&&h)&&"object"==typeof FileReader){var j=new FileReader;j.onloadend=function(){var a=j.result;a=i?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),e?e.location.href=a:location=a,e=null},j.readAsDataURL(a)}else{var k=f.URL||f.webkitURL,l=k.createObjectURL(a);e?e.location=l:location.href=l,e=null,setTimeout(function(){k.revokeObjectURL(l)},4E4)}});f.saveAs=a.saveAs=a,"undefined"!=typeof module&&(module.exports=a)});
2 |
3 | //# sourceMappingURL=FileSaver.min.js.map
--------------------------------------------------------------------------------
/docs/examples/cepstrum.json:
--------------------------------------------------------------------------------
1 | {
2 | "resourceType": "https://yal-tools.github.io/vial-to-keymap-drawer/",
3 | "fields": {
4 | "keyboard": "keebio/cepstrum/rev1",
5 | "layout": "",
6 | "half-after-half": false,
7 | "mirror-right-half": false,
8 | "omit-non-keys": 2,
9 | "key-ranges": [
10 | "0,0-8",
11 | "0,45-53",
12 |
13 | "0,9-17",
14 | "0,54-62",
15 |
16 | "0,18-26",
17 | "0,63-71",
18 |
19 | "0,27-35",
20 | "0,72-80",
21 |
22 | "0,36-43",
23 | "0,81-89",
24 |
25 | ""
26 | ],
27 | "layer-names": [],
28 | "key-overrides": [],
29 | "notes": [
30 | "Uses Key Ranges to order keys correctly and 'Omit KC_NO' to avoid the various SINC PCB's optional keys."
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/docs/examples/cepstrum.vil:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Cepstrum Rev. 1",
3 | "vendorProductId": 3406828887,
4 | "macros": ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""],
5 | "layers": [
6 | [
7 | "KC_F22",
8 | "KC_PSCR",
9 | "KC_GRV",
10 | "KC_1",
11 | "KC_2",
12 | "KC_3",
13 | "KC_4",
14 | "KC_5",
15 | "KC_6",
16 | "KC_VOLU",
17 | "KC_CALC",
18 | "KC_TAB",
19 | "KC_Q",
20 | "KC_W",
21 | "KC_E",
22 | "KC_R",
23 | "KC_T",
24 | "KC_NO",
25 | "KC_VOLD",
26 | "KC_F16",
27 | "KC_ESC",
28 | "KC_A",
29 | "KC_S",
30 | "KC_D",
31 | "KC_F",
32 | "KC_G",
33 | "KC_NO",
34 | "KC_PGUP",
35 | "KC_PGDN",
36 | "KC_LSFT",
37 | "KC_NO",
38 | "KC_Z",
39 | "KC_X",
40 | "KC_C",
41 | "KC_V",
42 | "KC_B",
43 | "KC_MPRV",
44 | "KC_MNXT",
45 | "KC_LCTL",
46 | "KC_LGUI",
47 | "KC_LALT",
48 | "LT(1,KC_SPC)",
49 | "KC_NO",
50 | "KC_SPC",
51 | "KC_NO",
52 | "KC_7",
53 | "KC_8",
54 | "KC_9",
55 | "KC_0",
56 | "KC_MINS",
57 | "KC_EQL",
58 | "KC_NO",
59 | "KC_BSPC",
60 | "KC_DEL",
61 | "KC_Y",
62 | "KC_U",
63 | "KC_I",
64 | "KC_O",
65 | "KC_P",
66 | "KC_LBRC",
67 | "KC_RBRC",
68 | "KC_BSLS",
69 | "KC_APP",
70 | "KC_H",
71 | "KC_J",
72 | "KC_K",
73 | "KC_L",
74 | "KC_SCLN",
75 | "KC_QUOT",
76 | "KC_NO",
77 | "KC_ENT",
78 | "KC_HOME",
79 | "KC_N",
80 | "KC_M",
81 | "KC_COMM",
82 | "KC_DOT",
83 | "KC_SLSH",
84 | "KC_NO",
85 | "KC_RSFT",
86 | "KC_UP",
87 | "KC_END",
88 | "KC_NO",
89 | "KC_SPC",
90 | "KC_RALT",
91 | "KC_RGUI",
92 | "KC_RCTL",
93 | "KC_NO",
94 | "KC_LEFT",
95 | "KC_DOWN",
96 | "KC_RGHT"
97 | ],
98 | [
99 | "KC_TRNS",
100 | "KC_TRNS",
101 | "KC_ESC",
102 | "KC_F1",
103 | "KC_F2",
104 | "KC_F3",
105 | "KC_F4",
106 | "KC_F5",
107 | "KC_F6",
108 | "KC_TRNS",
109 | "KC_TRNS",
110 | "KC_TRNS",
111 | "KC_TRNS",
112 | "KC_TRNS",
113 | "KC_TRNS",
114 | "KC_TRNS",
115 | "KC_TRNS",
116 | "KC_NO",
117 | "KC_TRNS",
118 | "KC_TRNS",
119 | "KC_TRNS",
120 | "KC_TRNS",
121 | "KC_TRNS",
122 | "KC_TRNS",
123 | "KC_TRNS",
124 | "KC_TRNS",
125 | "KC_NO",
126 | "KC_TRNS",
127 | "KC_TRNS",
128 | "KC_TRNS",
129 | "KC_NO",
130 | "KC_TRNS",
131 | "KC_TRNS",
132 | "KC_TRNS",
133 | "KC_TRNS",
134 | "KC_TRNS",
135 | "KC_TRNS",
136 | "KC_TRNS",
137 | "KC_TRNS",
138 | "KC_TRNS",
139 | "KC_TRNS",
140 | "KC_TRNS",
141 | "KC_NO",
142 | "KC_TRNS",
143 | "KC_NO",
144 | "KC_F7",
145 | "KC_F8",
146 | "KC_F9",
147 | "KC_F10",
148 | "KC_F11",
149 | "KC_F12",
150 | "KC_NO",
151 | "KC_TRNS",
152 | "KC_PSCR",
153 | "KC_TRNS",
154 | "KC_TRNS",
155 | "KC_TRNS",
156 | "KC_TRNS",
157 | "KC_TRNS",
158 | "KC_TRNS",
159 | "KC_TRNS",
160 | "KC_TRNS",
161 | "KC_PAUS",
162 | "KC_TRNS",
163 | "KC_TRNS",
164 | "KC_TRNS",
165 | "KC_TRNS",
166 | "KC_TRNS",
167 | "KC_TRNS",
168 | "KC_NO",
169 | "KC_TRNS",
170 | "C(KC_PGUP)",
171 | "KC_TRNS",
172 | "KC_TRNS",
173 | "KC_TRNS",
174 | "KC_TRNS",
175 | "KC_TRNS",
176 | "KC_NO",
177 | "KC_TRNS",
178 | "KC_TRNS",
179 | "C(KC_PGDN)",
180 | "KC_NO",
181 | "KC_TRNS",
182 | "KC_RGUI",
183 | "KC_RALT",
184 | "KC_TRNS",
185 | "KC_NO",
186 | "KC_TRNS",
187 | "KC_TRNS",
188 | "KC_TRNS"
189 | ],
190 | [
191 | "KC_TRNS",
192 | "KC_TRNS",
193 | "KC_TRNS",
194 | "KC_TRNS",
195 | "KC_TRNS",
196 | "KC_TRNS",
197 | "KC_TRNS",
198 | "KC_TRNS",
199 | "KC_TRNS",
200 | "KC_TRNS",
201 | "KC_TRNS",
202 | "KC_TRNS",
203 | "KC_TRNS",
204 | "KC_TRNS",
205 | "KC_TRNS",
206 | "KC_TRNS",
207 | "KC_TRNS",
208 | "KC_TRNS",
209 | "KC_TRNS",
210 | "KC_TRNS",
211 | "KC_TRNS",
212 | "KC_TRNS",
213 | "KC_TRNS",
214 | "KC_TRNS",
215 | "KC_TRNS",
216 | "KC_TRNS",
217 | "KC_TRNS",
218 | "KC_TRNS",
219 | "KC_TRNS",
220 | "KC_TRNS",
221 | "KC_TRNS",
222 | "KC_TRNS",
223 | "KC_TRNS",
224 | "KC_TRNS",
225 | "KC_TRNS",
226 | "KC_TRNS",
227 | "KC_TRNS",
228 | "KC_TRNS",
229 | "KC_TRNS",
230 | "KC_TRNS",
231 | "KC_TRNS",
232 | "KC_TRNS",
233 | "KC_TRNS",
234 | "KC_TRNS",
235 | "KC_TRNS",
236 | "KC_TRNS",
237 | "KC_TRNS",
238 | "KC_TRNS",
239 | "KC_TRNS",
240 | "KC_TRNS",
241 | "KC_TRNS",
242 | "KC_TRNS",
243 | "KC_TRNS",
244 | "KC_TRNS",
245 | "KC_TRNS",
246 | "KC_TRNS",
247 | "KC_TRNS",
248 | "KC_TRNS",
249 | "KC_TRNS",
250 | "KC_TRNS",
251 | "KC_TRNS",
252 | "KC_TRNS",
253 | "KC_TRNS",
254 | "KC_TRNS",
255 | "KC_TRNS",
256 | "KC_TRNS",
257 | "KC_TRNS",
258 | "KC_TRNS",
259 | "KC_TRNS",
260 | "KC_TRNS",
261 | "KC_TRNS",
262 | "KC_TRNS",
263 | "KC_TRNS",
264 | "KC_TRNS",
265 | "KC_TRNS",
266 | "KC_TRNS",
267 | "KC_TRNS",
268 | "KC_TRNS",
269 | "KC_TRNS",
270 | "KC_TRNS",
271 | "KC_TRNS",
272 | "KC_TRNS",
273 | "KC_TRNS",
274 | "KC_TRNS",
275 | "KC_TRNS",
276 | "KC_TRNS",
277 | "KC_TRNS",
278 | "KC_TRNS",
279 | "KC_TRNS",
280 | "KC_TRNS"
281 | ],
282 | [
283 | "KC_TRNS",
284 | "KC_TRNS",
285 | "KC_TRNS",
286 | "KC_TRNS",
287 | "KC_TRNS",
288 | "KC_TRNS",
289 | "KC_TRNS",
290 | "KC_TRNS",
291 | "KC_TRNS",
292 | "KC_TRNS",
293 | "KC_TRNS",
294 | "KC_TRNS",
295 | "KC_TRNS",
296 | "KC_TRNS",
297 | "KC_TRNS",
298 | "KC_TRNS",
299 | "KC_TRNS",
300 | "KC_TRNS",
301 | "KC_TRNS",
302 | "KC_TRNS",
303 | "KC_TRNS",
304 | "KC_TRNS",
305 | "KC_TRNS",
306 | "KC_TRNS",
307 | "KC_TRNS",
308 | "KC_TRNS",
309 | "KC_TRNS",
310 | "KC_TRNS",
311 | "KC_TRNS",
312 | "KC_TRNS",
313 | "KC_TRNS",
314 | "KC_TRNS",
315 | "KC_TRNS",
316 | "KC_TRNS",
317 | "KC_TRNS",
318 | "KC_TRNS",
319 | "KC_TRNS",
320 | "KC_TRNS",
321 | "KC_TRNS",
322 | "KC_TRNS",
323 | "KC_TRNS",
324 | "KC_TRNS",
325 | "KC_TRNS",
326 | "KC_TRNS",
327 | "KC_TRNS",
328 | "KC_TRNS",
329 | "KC_TRNS",
330 | "KC_TRNS",
331 | "KC_TRNS",
332 | "KC_TRNS",
333 | "KC_TRNS",
334 | "KC_TRNS",
335 | "KC_TRNS",
336 | "KC_TRNS",
337 | "KC_TRNS",
338 | "KC_TRNS",
339 | "KC_TRNS",
340 | "KC_TRNS",
341 | "KC_TRNS",
342 | "KC_TRNS",
343 | "KC_TRNS",
344 | "KC_TRNS",
345 | "KC_TRNS",
346 | "KC_TRNS",
347 | "KC_TRNS",
348 | "KC_TRNS",
349 | "KC_TRNS",
350 | "KC_TRNS",
351 | "KC_TRNS",
352 | "KC_TRNS",
353 | "KC_TRNS",
354 | "KC_TRNS",
355 | "KC_TRNS",
356 | "KC_TRNS",
357 | "KC_TRNS",
358 | "KC_TRNS",
359 | "KC_TRNS",
360 | "KC_TRNS",
361 | "KC_TRNS",
362 | "KC_TRNS",
363 | "KC_TRNS",
364 | "KC_TRNS",
365 | "KC_TRNS",
366 | "KC_TRNS",
367 | "KC_TRNS",
368 | "KC_TRNS",
369 | "KC_TRNS",
370 | "KC_TRNS",
371 | "KC_TRNS",
372 | "KC_TRNS"
373 | ]
374 | ],
375 | "encoders": []
376 | }
--------------------------------------------------------------------------------
/docs/examples/corvett-piantor.json:
--------------------------------------------------------------------------------
1 | {
2 | "resourceType": "https://yal-tools.github.io/vial-to-keymap-drawer/",
3 | "fields": {
4 | "keyboard": "corne_rotated",
5 | "layout": "LAYOUT_split_3x6_3",
6 | "half-after-half": true,
7 | "mirror-right-half": false,
8 | "omit-m1": true,
9 | "omit-non-keys": "0",
10 | "move-defs": [],
11 | "layer-names": [],
12 | "key-overrides": [],
13 | "mark-non-keys": "unused",
14 | "notes": [
15 | "Marks unused keys, otherwise a relatively normal configuration."
16 | ]
17 | }
18 | }
--------------------------------------------------------------------------------
/docs/examples/corvett-piantor.vil:
--------------------------------------------------------------------------------
1 | {"version": 1, "uid": 1113869408211332572, "layout": [[["KC_NO", "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T"], ["KC_NO", "KC_A", "TD(5)", "TD(4)", "TD(3)", "KC_G"], ["KC_NO", "KC_Z", "KC_X", "KC_C", "KC_V", "KC_COMMA"], ["KC_ENTER", "KC_ENTER", "MO(1)", -1, -1, -1], ["KC_Y", "KC_U", "KC_I", "KC_O", "KC_P", "KC_NO"], ["KC_H", "TD(6)", "TD(7)", "TD(8)", "KC_SCOLON", "KC_NO"], ["KC_B", "KC_N", "KC_M", "KC_DOT", "TD(11)", "KC_NO"], ["MO(2)", "KC_SPACE", "KC_SPACE", -1, -1, -1]], [["KC_NO", "KC_GRAVE", "KC_ESCAPE", "KC_BTN2", "KC_MS_U", "KC_BTN1"], ["KC_TRNS", "KC_PGUP", "KC_WH_U", "KC_MS_L", "KC_MS_D", "KC_MS_R"], ["KC_TRNS", "KC_PGDOWN", "KC_WH_D", "KC_NO", "KC_TAB", "LSFT(KC_GRAVE)"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1], ["KC_NO", "KC_7", "KC_8", "KC_9", "KC_BSPACE", "KC_NO"], ["KC_LBRACKET", "KC_4", "KC_5", "KC_6", "KC_BSLASH", "KC_NO"], ["KC_RBRACKET", "KC_1", "KC_2", "KC_3", "KC_DOT", "KC_NO"], ["MO(3)", "KC_0", "KC_0", -1, -1, -1]], [["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "C_S(KC_8)", "LCTL(KC_D)", "LCTL(KC_F4)", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "M0", "KC_NO"], ["KC_TRNS", "KC_TRNS", "MO(3)", -1, -1, -1], ["KC_HOME", "KC_UP", "KC_END", "KC_MINUS", "KC_DELETE", "KC_NO"], ["KC_LEFT", "KC_DOWN", "KC_RIGHT", "KC_EQUAL", "KC_BSLASH", "KC_NO"], ["KC_BSPACE", "KC_DELETE", "KC_LBRACKET", "KC_RBRACKET", "KC_QUOTE", "KC_NO"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1]], [["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_BRIU", "KC_VOLU"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_BRID", "KC_VOLD"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_NO", "KC_MUTE"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1], ["KC_NO", "KC_F7", "KC_F8", "KC_F9", "KC_F12", "KC_NO"], ["KC_NO", "KC_F4", "KC_F5", "KC_F6", "KC_F11", "KC_NO"], ["KC_NO", "KC_F1", "KC_F2", "KC_F3", "KC_F10", "KC_NO"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1], ["KC_TRNS", "LSFT(KC_7)", "LSFT(KC_8)", "LSFT(KC_9)", "LSFT(KC_MINUS)", "KC_TRNS"], ["LSFT(KC_LBRACKET)", "LSFT(KC_4)", "LSFT(KC_5)", "LSFT(KC_6)", "LSFT(KC_BSLASH)", "KC_NO"], ["LSFT(KC_RBRACKET)", "LSFT(KC_1)", "LSFT(KC_2)", "LSFT(KC_3)", "LSFT(KC_QUOTE)", "KC_NO"], ["KC_TRNS", "LSFT(KC_0)", "KC_TRNS", -1, -1, -1]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", -1, -1, -1]]], "encoder_layout": [[], [], [], [], [], [], [], [], [], []], "layout_options": -1, "macro": [[["text", "thisisunsafe"]], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "vial_protocol": 5, "via_protocol": 9, "tap_dance": [["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "MO(1)", "TG(1)", "KC_NO", 200], ["KC_NO", "MO(2)", "TG(2)", "KC_NO", 200], ["KC_F", "KC_LCTRL", "KC_NO", "KC_NO", 150], ["KC_D", "KC_LALT", "KC_NO", "KC_NO", 150], ["KC_S", "KC_LSHIFT", "KC_NO", "KC_NO", 100], ["KC_J", "KC_RCTRL", "KC_NO", "KC_NO", 150], ["KC_K", "KC_RALT", "KC_NO", "KC_NO", 150], ["KC_L", "KC_LSHIFT", "KC_NO", "KC_NO", 100], ["KC_LGUI", "MO(2)", "KC_NO", "KC_NO", 50], ["KC_TAB", "MO(1)", "KC_NO", "KC_NO", 50], ["KC_SLASH", "KC_RGUI", "KC_NO", "KC_NO", 150], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 0], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 200]], "combo": [["TD(3)", "KC_G", "KC_NO", "KC_NO", "KC_TAB"], ["TD(3)", "TD(4)", "KC_NO", "KC_NO", "LCTL(KC_LALT)"], ["TD(6)", "TD(7)", "KC_NO", "KC_NO", "LCTL(KC_LALT)"], ["TD(3)", "TD(5)", "KC_NO", "KC_NO", "LCTL(KC_LSHIFT)"], ["TD(6)", "TD(8)", "KC_NO", "KC_NO", "LCTL(KC_LSHIFT)"], ["TD(4)", "TD(5)", "KC_NO", "KC_NO", "LALT(KC_LSHIFT)"], ["TD(5)", "MO(1)", "KC_NO", "KC_NO", "MO(4)"], ["KC_W", "KC_E", "KC_NO", "KC_NO", "KC_ESCAPE"], ["KC_E", "KC_R", "KC_NO", "KC_NO", "KC_TAB"], ["KC_U", "KC_I", "KC_NO", "KC_NO", "KC_BSPACE"], ["KC_I", "KC_O", "KC_NO", "KC_NO", "KC_DELETE"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"]], "key_override": [{"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}, {"trigger": "KC_NO", "replacement": "KC_NO", "layers": 65535, "trigger_mods": 0, "negative_mod_mask": 0, "suppressed_mods": 0, "options": 7}], "settings": {"1": 0, "2": 30, "3": 0, "4": 175, "5": 5, "6": 5000, "7": 160, "8": 0, "9": 10, "10": 20, "11": 4, "12": 4, "13": 30, "14": 10, "15": 80, "16": 8, "17": 40, "18": 0, "19": 80, "20": 5, "21": 0}}
--------------------------------------------------------------------------------
/docs/examples/key-range-editor.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YAL-Tools/vial-to-keymap-drawer/7aa058f163f786e28a92e15446af9eb39ca80d56/docs/examples/key-range-editor.mp4
--------------------------------------------------------------------------------
/docs/examples/yal-sofle.json:
--------------------------------------------------------------------------------
1 | {
2 | "resourceType": "https://yal-tools.github.io/vial-to-keymap-drawer/",
3 | "fields": {
4 | "notes": [
5 | "An example of fixing a few out-of-order keys. Shows off layer names, custom key labels, tap dances, and chords."
6 | ],
7 | "keyboard": "splitkb/aurora/sofle_v2/rev1",
8 | "layout": "",
9 | "include-layers": "",
10 | "half-after-half": true,
11 | "mirror-right-half": true,
12 | "omit-m1": false,
13 | "omit-non-keys": "0",
14 | "move-defs": [
15 | "4,5 => 3,6",
16 | "9,5 => 8,6"
17 | ],
18 | "key-ranges": [],
19 | "encoder-defs": [
20 | "4,5 => 0",
21 | "9,5 => 1"
22 | ],
23 | "layer-names": [
24 | "DEF",
25 | "NAV",
26 | "ALT",
27 | "NUM"
28 | ],
29 | "key-overrides": [
30 | "\"F16\" => Mute ",
31 | "\"F21\" => Scroll Down",
32 | "\"F20\" => Scroll Up",
33 | "\"F22\" => Lang",
34 | "\"LGui+F22\" => Magic 1",
35 | "\"LAlt+F22\" => Magic 2",
36 | "\"LCtl+F22\" => Magic 3",
37 | "\"M1\" => NAV ON",
38 | "\"M0\" => NAV OFF",
39 | "\"M2\" => NUM ON",
40 | "\"M3\" => NUM OFF",
41 | "\"Browser\\nFav.\" => Search"
42 | ],
43 | "mark-non-keys": ""
44 | }
45 | }
--------------------------------------------------------------------------------
/docs/examples/yal-sofle.vil:
--------------------------------------------------------------------------------
1 | {"version": 1, "uid": 12888985424971681029, "layout": [[["KC_GRAVE", "KC_1", "KC_2", "KC_3", "KC_4", "KC_5"], ["KC_TAB", "KC_Q", "KC_W", "KC_E", "KC_R", "KC_T"], ["KC_LSHIFT", "KC_A", "KC_S", "KC_D", "KC_F", "KC_G"], ["KC_LCTRL", "KC_Z", "KC_X", "KC_C", "KC_V", "KC_B"], ["KC_LGUI", "KC_LALT", "KC_SPACE", "LT2(KC_ESCAPE)", "TD(0)", "KC_HOME"], ["KC_BSPACE", "KC_0", "KC_9", "KC_8", "KC_7", "KC_6"], ["KC_LBRACKET", "KC_P", "KC_O", "KC_I", "KC_U", "KC_Y"], ["TD(1)", "KC_SCOLON", "KC_L", "KC_K", "KC_J", "KC_H"], ["RSFT_T(KC_RBRACKET)", "KC_SLASH", "KC_DOT", "KC_COMMA", "KC_M", "KC_N"], ["M1", "RALT_T(KC_APPLICATION)", "KC_EQUAL", "KC_MINUS", "KC_BSLASH", "KC_END"]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "M0", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_DELETE", "KC_END", "KC_UP", "KC_HOME", "RCTL(KC_LEFT)"], ["KC_TRNS", "KC_TRNS", "KC_RIGHT", "KC_DOWN", "KC_LEFT", "RCTL(KC_RIGHT)"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_PGDOWN", "KC_PGUP"], ["M0", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"]], [["KC_ESCAPE", "KC_F1", "KC_F2", "KC_F3", "KC_F4", "KC_F5"], ["KC_TAB", "KC_F11", "KC_F12", "KC_PAUSE", "KC_PSCREEN", "KC_WFAV"], ["KC_LSHIFT", "KC_LGUI", "KC_LSHIFT", "KC_LCTRL", "KC_LALT", "KC_F22"], ["KC_LCTRL", "LGUI(KC_F22)", "LALT(KC_F22)", "LCTL(KC_F22)", "M2", "KC_F16"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_F10", "KC_F9", "KC_F8", "KC_F7", "KC_F6"], ["KC_TRNS", "KC_DELETE", "KC_END", "KC_UP", "KC_HOME", "KC_PGUP"], ["KC_TRNS", "KC_TRNS", "KC_RIGHT", "KC_DOWN", "KC_LEFT", "KC_PGDOWN"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_INSERT", "KC_CAPSLOCK"], ["KC_SCROLLLOCK", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"]], [["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_HOME", "KC_UP", "KC_END", "KC_PGUP", "KC_NO"], ["KC_TRNS", "KC_LEFT", "KC_DOWN", "KC_RIGHT", "KC_PGDOWN", "KC_NO"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "M3", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_KP_MINUS", "KC_KP_ASTERISK", "KC_KP_SLASH", "KC_EQUAL", "KC_NUMLOCK"], ["KC_TRNS", "KC_DELETE", "KC_KP_9", "KC_KP_8", "KC_KP_7", "LSFT(KC_9)"], ["KC_TRNS", "KC_KP_PLUS", "KC_KP_6", "KC_KP_5", "KC_KP_4", "KC_BSPACE"], ["KC_TRNS", "KC_KP_ENTER", "KC_KP_3", "KC_KP_2", "KC_KP_1", "LSFT(KC_0)"], ["M3", "KC_KP_DOT", "KC_KP_0", "KC_KP_0", "KC_COMMA", "KC_TRNS"]]], "encoder_layout": [[["KC_DOWN", "KC_UP"], ["KC_RIGHT", "KC_LEFT"]], [["KC_TRNS", "KC_TRNS"], ["KC_F21", "KC_F20"]], [["KC_VOLD", "KC_VOLU"], ["KC_DOWN", "KC_UP"]], [["KC_TRNS", "KC_TRNS"], ["KC_F21", "KC_F20"]]], "layout_options": -1, "macro": [[["tap", "KC_F23", "TG(1)"]], [["tap", "RCTL(KC_F23)", "TG(1)"]], [["tap", "RSFT(KC_F23)", "TG(3)"]], [["tap", "KC_F23", "TG(3)"]], [["tap", "KC_END", "LSFT(KC_HOME)"]], [], [], [], [], [], [], [], [], [], [], []], "vial_protocol": 5, "via_protocol": 9, "tap_dance": [["KC_ENTER", "MO(3)", "KC_NO", "KC_NO", 250], ["KC_QUOTE", "KC_RCTRL", "KC_NO", "KC_NO", 200], ["LSFT(KC_MINUS)", "KC_LSHIFT", "KC_NO", "KC_NO", 120], ["KC_MINUS", "KC_RALT", "KC_NO", "KC_NO", 200], ["LSFT(KC_8)", "LSFT(KC_6)", "KC_NO", "KC_NO", 160], ["LSFT(KC_8)", "LSFT(KC_3)", "KC_NO", "KC_NO", 160], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 160], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", 160]], "combo": [["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"], ["KC_NO", "KC_NO", "KC_NO", "KC_NO", "KC_NO"]], "key_override": [], "settings": {}}
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Vial To Keymap Drawer
6 |
7 |
8 |
9 |
10 |
11 |
12 | night mode
13 |
14 |
31 | Vial To Keymap Drawer
32 |
39 |
40 |
41 | Note on VIA support
42 | The tool will try to process VIA keymaps, but if you've got a split keyboard
43 | that only runs VIA, tough luck - VIA backups do not contain any information about
44 | which keys belong to which row, so you might have to enter the key ranges by hand.
45 |
46 | Consider using via2json instead.
47 |
48 |
49 |
52 |
53 |
54 |
55 | ·
56 |
57 | Load example
58 |
59 | YAL's Sofle Choc
60 | corvett's Pianthor
61 | Cepstrum (VIA)
62 |
63 |
64 |
65 |
66 |
67 |
68 | What kind of keyboard is this? (as per Configurator )
69 |
70 |
71 |
72 | Layout name (optional; also look it up in Configurator):
73 |
74 |
75 | Include only these layer(s):
76 |
77 |
78 |
79 |
80 | Keys out of order or too many keys?
81 |
82 |
83 | One half after another (instead of row by row)
84 |
85 |
86 |
87 |
88 | Mirrored keys on the right half
89 |
90 |
91 |
92 |
93 | Omit -1
keys
94 |
95 |
96 | Omit keys that are KC_NO on:
97 |
98 |
99 | (don't omit)
100 | First layer
101 | First two layers
102 | All layers
103 |
104 |
105 | Key move definitions (old row, old col => new row, new col):
106 |
107 |
108 | OR enter key ranges to pick from the source file:
109 |
110 |
111 | Encoder "key" locations (row, col => index):
112 |
113 |
114 |
115 |
116 | Show key row,col inside the "Shift" state
117 |
118 |
119 |
120 |
121 | Customization
122 | Layer names:
123 |
124 | Custom key labels (layer, row, col => newkey):
125 |
126 | Mark keys that are disabled on all layers as:
127 |
128 | (don't mark)
129 | Ghost/optional
130 | Dashed border
131 | Invisible
132 |
133 | Note: Dashed border/invisible styles only work in dev version of keymap-drawer as of Sep 2023.
134 |
135 |
136 | Pick (or paste) your VIL layout:
137 |
140 |
141 |
142 |
143 |
144 | Copy to clipboard afterwards
145 |
146 |
147 |
148 | Output YAML-JSON:
149 |
150 |
151 | Output log:
152 |
153 |
154 |
155 | A tool by YellowAfterlife .
156 | You can
157 | give me a few dollars
158 | for making this.
159 |
160 |
161 |
162 |
171 |
172 | How to use:
173 |
174 | Click "Copy YAML" to copy data for a single layer.
175 |
176 | Paste that in the Keymap Drawer
177 |
178 | Download the SVG of the keymap (right-click the preview and Save As)
179 |
180 | Import the SVG here using "Import SVG"
181 |
182 | Click or click-and-drag the keys on the displayed layout to add them in the order they appear on your actual keyboard.
183 | The second layout preview will reflect the key order.
184 | You can click on a key again to remove it, or import an SVG again to start anew.
185 |
186 | You can watch an
example video (48 seconds) if you'd like
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
204 |
205 |
--------------------------------------------------------------------------------
/docs/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #899FC6;
3 | font: 15px sans-serif;
4 | line-height: 1.5;
5 | }
6 | main {
7 | position: relative;
8 | background-color: white;
9 | max-width: 640px;
10 | margin: 0 auto;
11 | padding: 0.5em;
12 | }
13 | main h1 {
14 | margin: 0;
15 | }
16 | main label:not(.inline) {
17 | display: block;
18 | }
19 | main input[type="text"],
20 | main input[type="file"],
21 | main select:not(.inline),
22 | main textarea {
23 | width: 100%;
24 | box-sizing: border-box;
25 | }
26 | main textarea.prop {
27 | font-family: sans-serif;
28 | font-size: 15px;
29 | }
30 | main textarea {
31 | resize: vertical;
32 | }
33 | main #vil-form {
34 | margin-bottom: 0.25em;
35 | }
36 | main details {
37 | padding: 0.2em 0.5em;
38 | margin: 0.5em 0;
39 | border: 1px dashed #ccc;
40 | }
41 | main details > summary {
42 | padding-left: 0.5em;
43 | }
44 | input[type="text"],
45 | textarea {
46 | padding: 0.25em 0.4em;
47 | }
48 | input[type="button"],
49 | select {
50 | padding: 0.2em 0.6em;
51 | }
52 | #key-range-editor {
53 | position: fixed;
54 | inset: 0.5em;
55 | background: white;
56 | border: 1px solid #444;
57 | box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
58 | display: flex;
59 | }
60 | #key-range-editor .sidebar {
61 | width: 8em;
62 | background: #f0f0f0;
63 | display: flex;
64 | flex-direction: column;
65 | overflow: hidden auto;
66 | padding: 0.3em;
67 | }
68 | #key-range-editor .sidebar input {
69 | margin-bottom: 0.3em;
70 | }
71 | #key-range-editor .sidebar #key-range-close {
72 | margin-top: 0.3em;
73 | margin-bottom: 0;
74 | }
75 | #key-range-editor .sidebar textarea {
76 | flex-grow: 1;
77 | }
78 | #key-range-editor .svg-ctr {
79 | flex-grow: 1;
80 | overflow-y: auto;
81 | }
82 | #key-range-editor .howto {
83 | padding: 0.5em;
84 | }
85 | #key-range-editor .svg-ctr svg {
86 | max-width: 100%;
87 | }
88 | body.night {
89 | background: #405070;
90 | color: white;
91 | }
92 | body.night a {
93 | color: #9DEC76;
94 | }
95 | body.night a:visited {
96 | color: #7ef0ff;
97 | }
98 | body.night hr,
99 | body.night details {
100 | border-color: #899FC6;
101 | }
102 | body.night input[type="text"],
103 | body.night textarea,
104 | body.night select {
105 | background: black;
106 | color: white;
107 | border: 1px solid #899FC6;
108 | }
109 | body.night input[type="button"] {
110 | background: #283248;
111 | border: 1px solid #899FC6;
112 | color: white;
113 | padding: 0.25em 0.6em;
114 | }
115 | body.night input[type="button"]:hover {
116 | background: #899FC6;
117 | }
118 | body.night main,
119 | body.night #key-range-editor .sidebar {
120 | background: #1A202D;
121 | }
122 | body.night #key-range-editor {
123 | background: #899FC6;
124 | }
125 | label[for="night-mode"] {
126 | position: absolute;
127 | right: 0.5em;
128 | top: 0.5em;
129 | }
130 |
--------------------------------------------------------------------------------
/docs/style.less:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #899FC6;
3 | font: 15px sans-serif;
4 | line-height: 1.5;
5 | }
6 | main {
7 | position: relative;
8 | background-color: white;
9 | max-width: 640px;
10 | margin: 0 auto;
11 | padding: 0.5em;
12 |
13 | h1 {
14 | margin: 0;
15 | }
16 | label:not(.inline) {
17 | display: block;
18 | }
19 | input[type="text"],
20 | input[type="file"],
21 | select:not(.inline),
22 | textarea {
23 | width: 100%;
24 | box-sizing: border-box;
25 | }
26 | textarea.prop {
27 | font-family: sans-serif;
28 | font-size: 15px;
29 | }
30 | textarea {
31 | resize: vertical;
32 | }
33 | #vil-form {
34 | margin-bottom: 0.25em;
35 | }
36 | details {
37 | padding: 0.2em 0.5em;
38 | margin: 0.5em 0;
39 | border: 1px dashed #ccc;
40 | > summary {
41 | padding-left: 0.5em;
42 | }
43 | }
44 | }
45 | input[type="text"],
46 | textarea {
47 | padding: 0.25em 0.4em;
48 | }
49 | input[type="button"], select {
50 | padding: 0.2em 0.6em;
51 | }
52 |
53 | #key-range-editor {
54 | position: fixed;
55 | inset: 0.5em;
56 | background: white;
57 | border: 1px solid #444;
58 | box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
59 | display: flex;
60 |
61 | .sidebar {
62 | width: 8em;
63 | background: #f0f0f0;
64 | display: flex;
65 | flex-direction: column;
66 | overflow: hidden auto;
67 | padding: 0.3em;
68 | }
69 | .sidebar input {
70 | margin-bottom: 0.3em;
71 | }
72 | .sidebar #key-range-close {
73 | margin-top: 0.3em;
74 | margin-bottom: 0;
75 | }
76 | .sidebar textarea {
77 | flex-grow: 1;
78 | }
79 | .svg-ctr {
80 | flex-grow: 1;
81 | overflow-y: auto;
82 | }
83 | .howto {
84 | padding: 0.5em;
85 | }
86 | .svg-ctr svg {
87 | max-width: 100%;
88 | }
89 | }
90 |
91 | body.night {
92 | background: #405070;
93 | color: white;
94 |
95 | a {
96 | color: #9DEC76;
97 | }
98 | a:visited {
99 | color: #7ef0ff;
100 | }
101 | .control-border() {
102 | border: 1px solid #899FC6;
103 | }
104 | hr, details {
105 | border-color: #899FC6;
106 | }
107 |
108 | input[type="text"],
109 | textarea,
110 | select {
111 | background: black;
112 | color: white;
113 | .control-border();
114 | }
115 |
116 | input[type="button"] {
117 | background: #283248;
118 | .control-border();
119 | color: white;
120 | padding: 0.25em 0.6em;
121 | &:hover {
122 | background: #899FC6;
123 | }
124 | }
125 | main, #key-range-editor .sidebar {
126 | background: #1A202D;
127 | }
128 | #key-range-editor {
129 | background: #899FC6;
130 | }
131 | }
132 |
133 | label[for="night-mode"] {
134 | position: absolute;
135 | right: 0.5em;
136 | top: 0.5em;
137 | }
--------------------------------------------------------------------------------
/example.svg:
--------------------------------------------------------------------------------
1 |
2 |
103 |
104 | DEF:
105 |
106 |
107 |
108 | `
109 | ~
110 |
111 |
112 |
113 | 1
114 | !
115 |
116 |
117 |
118 | 2
119 | @
120 |
121 |
122 |
123 | 3
124 | #
125 |
126 |
127 |
128 | 4
129 | $
130 |
131 |
132 |
133 | 5
134 | %
135 |
136 |
137 |
138 | 6
139 | ^
140 |
141 |
142 |
143 | 7
144 | &
145 |
146 |
147 |
148 | 8
149 | *
150 |
151 |
152 |
153 | ( 9
154 | _ -
155 | TD(0)
156 |
157 |
158 |
159 | ) 0
160 | + =
161 | TD(1)
162 |
163 |
164 |
165 | Bksp
166 |
167 |
168 |
169 | Tab
170 |
171 |
172 |
173 | Q
174 |
175 |
176 |
177 | W
178 |
179 |
180 |
181 | E
182 |
183 |
184 |
185 | R
186 |
187 |
188 |
189 | T
190 |
191 |
192 |
193 | Y
194 |
195 |
196 |
197 | U
198 |
199 |
200 |
201 | I
202 |
203 |
204 |
205 | O
206 |
207 |
208 |
209 | P
210 |
211 |
212 |
213 | { [
214 | | \
215 | TD(2)
216 |
217 |
218 |
219 | LShift
220 |
221 |
222 |
223 | A
224 |
225 |
226 |
227 | S
228 |
229 |
230 |
231 | D
232 |
233 |
234 |
235 | F
236 |
237 |
238 |
239 | G
240 |
241 |
242 |
243 | H
244 |
245 |
246 |
247 | J
248 |
249 |
250 |
251 | K
252 |
253 |
254 |
255 | L
256 |
257 |
258 |
259 | ;
260 | :
261 |
262 |
263 |
264 | '
265 | "
266 |
267 |
268 |
269 | LCtrl
270 |
271 |
272 |
273 | Z
274 |
275 |
276 |
277 | X
278 |
279 |
280 |
281 | C
282 |
283 |
284 |
285 | V
286 |
287 |
288 |
289 | B
290 |
291 |
292 |
293 |
294 | Caps Lock
295 |
296 |
297 |
298 |
299 | Mute
300 |
301 |
302 |
303 | N
304 |
305 |
306 |
307 | M
308 |
309 |
310 |
311 | ,
312 | <
313 |
314 |
315 |
316 | .
317 | >
318 |
319 |
320 |
321 | /
322 | ?
323 |
324 |
325 |
326 | ]
327 | RSFT
328 | }
329 |
330 |
331 |
332 | LGui
333 |
334 |
335 |
336 | LAlt
337 |
338 |
339 |
340 | Space
341 |
342 |
343 |
344 |
345 | MO NAV
346 |
347 |
348 |
349 |
350 | Enter
351 | MO MISC
352 |
353 |
354 |
355 | Esc
356 | MO MISC
357 |
358 |
359 |
360 | Left
361 |
362 |
363 |
364 | Right
365 |
366 |
367 |
368 | Up
369 |
370 |
371 |
372 | Down
373 |
374 |
375 |
376 | Esc
377 |
378 |
379 |
380 |
381 |
--------------------------------------------------------------------------------
/media/cli.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YAL-Tools/vial-to-keymap-drawer/7aa058f163f786e28a92e15446af9eb39ca80d56/media/cli.png
--------------------------------------------------------------------------------
/media/example-blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YAL-Tools/vial-to-keymap-drawer/7aa058f163f786e28a92e15446af9eb39ca80d56/media/example-blue.png
--------------------------------------------------------------------------------
/media/web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YAL-Tools/vial-to-keymap-drawer/7aa058f163f786e28a92e15446af9eb39ca80d56/media/web.png
--------------------------------------------------------------------------------
/src/CommandLineOption.hx:
--------------------------------------------------------------------------------
1 | package ;
2 | import haxe.extern.EitherType;
3 |
4 | /**
5 | * ...
6 | * @author YellowAfterlife
7 | */
8 | class CommandLineOption {
9 | public static var prefix:String = "--";
10 | public var name:String;
11 | public var args:Array;
12 | public var func:Dynamic;
13 | public var desc:Array;
14 | public var groupHeader:String;
15 | public function new(
16 | name:String,
17 | args:Array,
18 | func:Dynamic,
19 | desc:EitherType>,
20 | ?group:String
21 | ) {
22 | this.name = name;
23 | this.func = func;
24 | this.args = args;
25 | this.desc = desc is String ? [desc] : desc;
26 | groupHeader = group;
27 | }
28 | public static function run(args:Array, options:Array) {
29 | var i = 0;
30 | while (i < args.length) {
31 | var arg = args[i];
32 | if (!StringTools.startsWith(arg, prefix)) {
33 | i += 1;
34 | continue;
35 | }
36 | var name = arg.substr(prefix.length);
37 | var opt = options.filter(o -> o.name == name)[0];
38 | if (opt == null) throw '"$arg" is not a known command-line option.';
39 |
40 | var sub = args.slice(i + 1, i + 1 + opt.args.length);
41 | try {
42 | Reflect.callMethod(opt, opt.func, sub);
43 | } catch (x:Dynamic) {
44 | throw "Error processing " + prefix + opt.name + sub + ": " + x;
45 | }
46 | args.splice(i, 1 + opt.args.length);
47 | }
48 | }
49 | public static function print(options:Array) {
50 | Sys.println("The following options are supported."
51 | + " Arguments are denoted in here");
52 | for (i => opt in options) {
53 | if (opt.groupHeader != null) {
54 | if (i > 0) Sys.println("");
55 | Sys.println(opt.groupHeader + ":");
56 | }
57 | Sys.print(prefix + opt.name);
58 | for (arg in opt.args) {
59 | Sys.print(' <$arg>');
60 | }
61 | Sys.println("");
62 | for (line in opt.desc) {
63 | Sys.println(" " + line);
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Main.hx:
--------------------------------------------------------------------------------
1 | package;
2 |
3 | import haxe.DynamicAccess;
4 | import haxe.Http;
5 | import haxe.Json;
6 | import js.Browser;
7 | import js.Lib;
8 | import js.html.Blob;
9 | import js.html.DataListElement;
10 | import js.html.Element;
11 | import js.html.Event;
12 | import js.html.FileReader;
13 | import js.html.FormElement;
14 | import js.html.InputElement;
15 | import js.html.SelectElement;
16 | import js.html.TextAreaElement;
17 | import drawer.DrawerKeymap;
18 | import js.html.Window;
19 | import tools.ERegTools;
20 | import tools.JsonParserWithComments;
21 | import vial.VialKeymap;
22 | import VilToDrawer;
23 | using StringTools;
24 |
25 | /**
26 | * ...
27 | * @author YellowAfterlife
28 | */
29 | class Main {
30 | public static inline var buildDate:String = tools.BuildDate.asString();
31 | public static inline function find(id:String, ?c:Class):T {
32 | return cast Browser.document.getElementById(id);
33 | }
34 | static var fdVil:TextAreaElement = find("vil");
35 | static var fdOut:TextAreaElement = find("out");
36 | static var fdLog:TextAreaElement = find("log");
37 |
38 | static var fmVil:FormElement = find("vil-form");
39 | static var ffVil:InputElement = find("vil-picker");
40 |
41 | static var cbHalfAfterHalf:InputElement = find("half-after-half");
42 | static var cbMirrorRightHalf:InputElement = find("mirror-right-half");
43 | static var cbDebugKeyPos:InputElement = find("show-key-pos");
44 | static var ddOmitNonKeys:SelectElement = find("omit-non-keys");
45 | static var ddMarkNonKeysAs:SelectElement = find("mark-non-keys");
46 | static var cbOmitM1:InputElement = find("omit-m1");
47 | static var fdKeyboard:InputElement = find("keyboard");
48 | static var fdLayout:InputElement = find("layout");
49 | static var fdMoveDefs:TextAreaElement = find("move-defs");
50 | static var fdEncoderDefs:TextAreaElement = find("encoder-defs");
51 | public static var fdKeyRanges:TextAreaElement = find("key-ranges");
52 |
53 | static var wndKeyRangeEditor:Window = null;
54 |
55 | static var fdLayerNames:TextAreaElement = find("layer-names");
56 | static var fdIncludeLayers:InputElement = find("include-layers");
57 | static var fdKeyOverrides:TextAreaElement = find("key-overrides");
58 |
59 | static var btConvert:InputElement = find("convert");
60 | static var cbCopyAfterConvert:InputElement = find("copy-after-convert");
61 |
62 | static var ddSample:SelectElement = find("sample");
63 | static var btLoad:InputElement = find("load-settings");
64 | static var btSave:InputElement = find("save-settings");
65 | static var btClear:InputElement = find("clear");
66 |
67 | static var fmLoad:FormElement = find("load-form");
68 | static var ffLoad:InputElement = find("load-picker");
69 |
70 |
71 | static var fields:Array = [];
72 |
73 | public static var latestOpt:VilToDrawerOpt = null;
74 | public static function convert_impl(forKeyRange:Bool = false) {
75 | var opt = new VilToDrawerOpt();
76 | latestOpt = opt;
77 |
78 | fdLog.value = "";
79 | opt.log = (level:String, v:Any) -> {
80 | fdLog.value += (fdLog.value.length > 0 ? "\n" : "")
81 | + '[$level] ' + v;
82 | };
83 |
84 | opt.qmkKeyboard = fdKeyboard.value.trim();
85 | opt.qmkLayout = fdLayout.value.trim();
86 | opt.parseVil(fdVil.value);
87 |
88 | if (!forKeyRange) {
89 | opt.halfAfterHalf = cbHalfAfterHalf.checked;
90 | opt.mirrorRightHalf = cbMirrorRightHalf.checked;
91 | opt.parseMoveDefs(fdMoveDefs.value);
92 | opt.parseRangeDefs(fdKeyRanges.value);
93 | opt.parseEncoderDefs(fdEncoderDefs.value);
94 | opt.showKeyPos = cbDebugKeyPos.checked;
95 | } else {
96 | opt.combos = false;
97 | opt.outKeys = [];
98 | }
99 | opt.omitNonKeys = Std.parseInt(ddOmitNonKeys.value);
100 | opt.omitM1 = cbOmitM1.checked;
101 |
102 | opt.parseLayerNames(fdLayerNames.value);
103 | if (!forKeyRange) {
104 | opt.parseIncludeLayers(fdIncludeLayers.value);
105 | } else opt.includeLayers.push(0);
106 | opt.parseKeyOverrides(fdKeyOverrides.value);
107 | opt.markNonKeysAs = ddMarkNonKeysAs.value;
108 | if (opt.markNonKeysAs == "") opt.markNonKeysAs = null;
109 |
110 | try {
111 | var result = VilToDrawer.runTxt(opt);
112 | opt.info("Done!");
113 | return result;
114 | } catch (x:Dynamic) {
115 | Browser.console.error("Conversion error:", x);
116 | opt.error(x);
117 | return null;
118 | }
119 | }
120 | static function convert() {
121 | var text = convert_impl();
122 | if (text != null) {
123 | fdOut.value = text;
124 | } else fdOut.value = "error!";
125 | }
126 | static function clear() {
127 | for (fd in fields) fd.reset();
128 | }
129 | static function applySettings(root:WebSettings) {
130 | clear();
131 | for (id => val in root.fields) {
132 | var fd = fields.filter(f -> f.id == id)[0];
133 | if (fd == null) continue;
134 | fd.set(val);
135 | }
136 | }
137 | static function saveSettings() {
138 | var root:WebSettings = {
139 | resourceType: "https://yal-tools.github.io/vial-to-keymap-drawer/",
140 | fields: {},
141 | };
142 | for (fd in fields) {
143 | root.fields[fd.id] = fd.get();
144 | }
145 | var blob = new Blob([Json.stringify(root, null, "\t")]);
146 | (cast Browser.window).saveAs(blob, "settings.json", "application/json");
147 | }
148 | static function loadSample(name:String = "yal-sofle") {
149 | var sfx = "?t=" + buildDate;
150 | var rs = new Http('examples/$name.json' + sfx);
151 | rs.onData = function(s) {
152 | applySettings(Json.parse(s));
153 | var rv = new Http('examples/$name.vil' + sfx);
154 | rv.onData = function(s) {
155 | fdVil.value = s;
156 | convert();
157 | }
158 | rv.request();
159 | };
160 | rs.request();
161 | }
162 | static function main() {
163 | var local = Browser.document.location.hostname == "localhost";
164 |
165 | for (node in Browser.document.querySelectorAll([
166 | "main input[id]",
167 | "main textarea[id]",
168 | "main select[id]",
169 | ].join(", "))) {
170 | var el:Element = cast node;
171 | if (el.classList.contains("transient")) continue;
172 | if (el == fdOut || el == fdVil) continue;
173 | if (el.tagName == "INPUT") {
174 | var ei:InputElement = cast el;
175 | switch (ei.type) {
176 | case "text": fields.push({
177 | id: el.id,
178 | get: function() return ei.value,
179 | set: function(val) ei.value = val,
180 | reset: function() ei.value = "",
181 | });
182 | case "checkbox": fields.push({
183 | id: el.id,
184 | get: function() return ei.checked,
185 | set: function(val) ei.checked = val,
186 | reset: function() ei.checked = false,
187 | });
188 | }
189 | }
190 | else if (el.tagName == "SELECT") {
191 | var es:SelectElement = cast el;
192 | fields.push({
193 | id: el.id,
194 | get: function() return es.value,
195 | set: function(val) es.value = val,
196 | reset: function() es.selectedIndex = 0,
197 | });
198 | }
199 | else {
200 | var et:TextAreaElement = cast el;
201 | fields.push({
202 | id: el.id,
203 | get: function() {
204 | if (et.value.trim() == "") return [];
205 | return et.value.split("\n");
206 | },
207 | set: function(val) {
208 | if (val is Array) {
209 | et.value = (val:Array).join("\n");
210 | } else et.value = val;
211 | },
212 | reset: function() et.value = "",
213 | });
214 | }
215 | }
216 |
217 | ffVil.onchange = function(e:Event) {
218 | var file = ffVil.files[0];
219 | if (file == null) return;
220 |
221 | var fileReader = new FileReader();
222 | fileReader.onloadend = function() fmVil.reset();
223 | fileReader.onload = function() {
224 | fdVil.value = fileReader.result;
225 | }
226 | fileReader.readAsText(file);
227 | }
228 |
229 | btClear.onclick = function() {
230 | if (!Browser.window.confirm(
231 | "Are you sure that you want to clear settings? This cannot be undone!"
232 | )) return;
233 | clear();
234 | }
235 |
236 | ffLoad.onchange = function() {
237 | var file = ffLoad.files[0];
238 | if (file == null) return;
239 |
240 | var fileReader = new FileReader();
241 | fileReader.onloadend = function() fmLoad.reset();
242 | fileReader.onload = function() {
243 | try {
244 | applySettings(JsonParserWithComments.parse(fileReader.result));
245 | } catch (x:Dynamic) {
246 | Browser.alert("Error loading settings: " + x);
247 | }
248 | }
249 | fileReader.readAsText(file);
250 | }
251 |
252 | btConvert.onclick = function() {
253 | convert();
254 | if (cbCopyAfterConvert.checked) Browser.navigator.clipboard.writeText(fdOut.value);
255 | }
256 | btSave.onclick = function() saveSettings();
257 | btLoad.onclick = function() ffLoad.click();
258 | ddSample.onchange = function() {
259 | var name = ddSample.value;
260 | if (name == "") return;
261 | if (!Browser.window.confirm(
262 | "Are you sure that you want to replace your settings with the example? This cannot be undone!"
263 | )) {
264 | ddSample.selectedIndex = 0;
265 | return;
266 | }
267 | ddSample.selectedIndex = 0;
268 | loadSample(name);
269 | }
270 |
271 | var kbjs = Browser.document.createScriptElement();
272 | kbjs.onload = function() {
273 | var list:DataListElement = find("qmk_keyboard");
274 | for (kb in ((cast Browser.window).qmk_keyboards:Array)) {
275 | var opt = Browser.document.createOptionElement();
276 | opt.value = kb;
277 | list.appendChild(opt);
278 | }
279 | };
280 | kbjs.async = true;
281 | kbjs.src = "qmk_keyboards.js";
282 | Browser.document.body.appendChild(kbjs);
283 |
284 | if (local) loadSample("cepstrum");
285 | web.KeyRangeCreator.init();
286 | Browser.console.info("Hello!");
287 | }
288 |
289 | }
290 | typedef PageField = {
291 | id: String,
292 | reset: Void->Void,
293 | get: Void->Any,
294 | set: Any->Void,
295 | }
296 | typedef WebSettings = {
297 | resourceType:String,
298 | fields:DynamicAccess,
299 | }
--------------------------------------------------------------------------------
/src/SysMain.hx:
--------------------------------------------------------------------------------
1 | package ;
2 | import sys.io.File;
3 | using StringTools;
4 |
5 | /**
6 | * ...
7 | * @author YellowAfterlife
8 | */
9 | class SysMain {
10 | static var config:VilToDrawerOpt = {
11 | var c = new VilToDrawerOpt();
12 | c.log = function(level, val) {
13 | Sys.print('[$level] ');
14 | Sys.print(val);
15 | Sys.println('');
16 | };
17 | c;
18 | };
19 |
20 | static function error(text:String) {
21 | Sys.println(text);
22 | Sys.exit(1);
23 | }
24 | static function getText(path:String) {
25 | try {
26 | var text = File.getContent(path);
27 | text = text.replace("\r", "");
28 | return text;
29 | } catch (x:Dynamic) {
30 | error('Failed to read "$path":' + x);
31 | return null;
32 | }
33 | }
34 | static var cliOpts:Array = [
35 | new CommandLineOption("help", [], function() {
36 | help();
37 | }, [
38 | "Shows this documentation and exits.",
39 | ]),
40 |
41 | new CommandLineOption("vil", ["path"], function(path) {
42 | config.parseVil(getText(path));
43 | }, "Specifies a Vial .vil file to convert"),
44 | new CommandLineOption("via", ["path"], function(path) {
45 | config.parseVil(getText(path));
46 | }, "Specifies a VIA .json file to convert"),
47 | new CommandLineOption("force-json", [], function() {
48 | config.yamlLike = false;
49 | }, [
50 | "Outputs strict JSON instead of YAML"
51 | ]),
52 |
53 | new CommandLineOption("keyboard", ["name"], function(val) {
54 | config.qmkKeyboard = val;
55 | }, [
56 | "Specifies a QMK keyboard name to use, such as",
57 | "corne_rotated",
58 | "splitkb/aurora/sofle_v2/rev1",
59 | "Find the name in configurator: https://config.qmk.fm/",
60 | ]),
61 | new CommandLineOption("layout", ["name"], function(val) {
62 | config.qmkLayout = val;
63 | }, [
64 | "Specifies a physical layout to use, if keyboard has multiple.",
65 | "For example, corne_rotated has a LAYOUT_split_3x5_3.",
66 | ]),
67 | new CommandLineOption("layer", ["index(es)"], function(val) {
68 | config.parseIncludeLayers(val);
69 | }, [
70 | "Only includes the specified layer (like `0`) or layers (like `0,1` in the output)."
71 | ]),
72 |
73 | new CommandLineOption("layer-names", ["path"], function(path) {
74 | config.parseLayerNames(getText(path));
75 | }, [
76 | "Specifies a file to load layer names from (one per line)",
77 | ], "Customization"),
78 | new CommandLineOption("key-labels", ["path"], function(path) {
79 | config.parseKeyOverrides(getText(path));
80 | }, [
81 | "Specifies a file to load key label overrides from. One per line, formatted as",
82 | "layer number, row, col => new key text",
83 | "For example,",
84 | "0,1,2 => COOL",
85 | "Would replace the label on third key in second row in first layer by COOL.",
86 | "Custom labels, KC_ names, and lean JSON objects {h:'Hold', t:'Tap'} are supported."
87 | ]),
88 | new CommandLineOption("mark-unused", ["style"], function(cn) {
89 | config.markNonKeysAs = cn;
90 | }, [
91 | "Shows keys that are set to KC_NO or KC_TRNS on all layers in a different way.",
92 | "Supported options are:",
93 | "ghost : Default keymap-drawer style for layout-optional keys",
94 | "unused : Shows a dashed outline for the keys, without fill",
95 | "hidden : Hides the keys completely",
96 | ]),
97 |
98 | new CommandLineOption("half-after-half", [], function() {
99 | config.halfAfterHalf = true;
100 | }, [
101 | "Specifies that keys are stored half-by-half and row-by-row",
102 | "(L1, L2, L3, R1, R2, R3 instead of L1, R1, L2, R2, L3, R3)",
103 | ], "Tweaks"),
104 | new CommandLineOption("mirror-right-half", [], function() {
105 | config.mirrorRightHalf = true;
106 | }, [
107 | "Specifies that keys on the right half of keyboard are stored right-to-left",
108 | "Happens to keyboards where the PCB and software is identical for two halves",
109 | ]),
110 | new CommandLineOption("move-defs", ["path"], function(path) {
111 | config.parseMoveDefs(getText(path));
112 | }, [
113 | "Specifies a file to load key move definitions from.",
114 | "One per line, formatted as one of the folllowing:",
115 | "old row, old col => new row, new col",
116 | "old row, old col[count] => new row, new col",
117 | "old row, old start col - old end col => new row, new col",
118 | "For example,",
119 | "1,2 => 0,5",
120 | "Would move third key from second row to sixth position in the first row.",
121 | "Rows and columns are counted as they appear in .vil",
122 | ]),
123 | new CommandLineOption("encoder-defs", ["path"], function(path) {
124 | config.parseEncoderDefs(getText(path));
125 | }, [
126 | "Specifies a file to load encoder definitions from for Vial.",
127 | "One per line, formatted as following:",
128 | "row, col => index",
129 | "For example,",
130 | "1,2 => 0",
131 | "Would attach first encoder's mappings to third key from second row.",
132 | "Rows and columns are counted as they appear in .vil",
133 | ]),
134 | new CommandLineOption("key-ranges", ["path"], function(path) {
135 | config.parseRangeDefs(getText(path));
136 | }, [
137 | "Specifies a file to load key ranges from.",
138 | "One per line, formatted as one of the folllowing:",
139 | "row, start col - end col",
140 | "row, col",
141 | "This overrides all of the above tweaks and can be used if the exported layout",
142 | "is wildly different from the QMK layout.",
143 | ]),
144 | new CommandLineOption("show-key-row-col", [], function(path) {
145 | config.parseRangeDefs(getText(path));
146 | }, [
147 | "Shows row,column for each key inside the 'Shift' state.",
148 | "Can make it easier to figure out key ranges.",
149 | ]),
150 | new CommandLineOption("omit-m1", [], function() {
151 | config.omitM1 = true;
152 | }, [
153 | "Omits keys are set to -1 on all layers",
154 | "Occasionally used for optional keys in Vial builds.",
155 | ]),
156 | new CommandLineOption("omit-no", ["layer-count"], function(ns) {
157 | var n = Std.parseInt(ns);
158 | if (n == null) throw '"$ns" is not a valid number.';
159 | config.omitNonKeys = n;
160 | }, [
161 | "Omit keys that are set to KC_NO on the given number of layers.",
162 | "Occasionally used for optional keys in VIA builds.",
163 | "Set to -1 to check all layers.",
164 | ]),
165 | ];
166 |
167 | static function help() {
168 | CommandLineOption.print(cliOpts);
169 | Sys.exit(0);
170 | }
171 | public static function main() {
172 | var args = Sys.args().copy();
173 | if (args.length == 0) help();
174 |
175 | try {
176 | CommandLineOption.run(args, cliOpts);
177 | } catch (x:Dynamic) {
178 | error("Error processing arguments: " + x);
179 | }
180 |
181 | if (args.length < 1) {
182 | error("Expected a .yaml path apart of the options");
183 | } else if (args.length > 1) {
184 | error('Multiple candidates for YAML path: ["' + args.join('","') + '"]');
185 | }
186 | var yamlPath = args[0];
187 |
188 | Sys.println("Converting...");
189 | var yamlTxt = VilToDrawer.runTxt(config);
190 | Sys.println('Writing to "$yamlPath"...');
191 | File.saveContent(args[0], yamlTxt);
192 | Sys.println("OK!");
193 | }
194 | }
--------------------------------------------------------------------------------
/src/VilToDrawer.hx:
--------------------------------------------------------------------------------
1 | package ;
2 | import drawer.DrawerKeymap;
3 | import haxe.DynamicAccess;
4 | import haxe.Json;
5 | import tools.ERegTools;
6 | import vial.VialKeyNames;
7 | import vial.VialKeymap;
8 | import vial.VialKey;
9 | using StringTools;
10 |
11 | /**
12 | * ...
13 | * @author YellowAfterlife
14 | */
15 | class VilToDrawer {
16 | #if js
17 | public static inline var needsHxOrder:Bool = false;
18 | #else
19 | public static inline var needsHxOrder:Bool = true;
20 | #end
21 | static function keysToInfos(keys:Array, layer:Int, row:Int, ind:Int):Array {
22 | var infos:Array = [];
23 | for (i => key in keys) {
24 | infos.push({
25 | layer: layer,
26 | row: row,
27 | col: i,
28 | ind: ind + i,
29 | key: key,
30 | });
31 | }
32 | return infos;
33 | }
34 | static function procViaLayers(opt:VilToDrawerOpt) {
35 | var vLayers:Array> = [];
36 | for (l => layer in opt.root.layers) {
37 | var keys:Array = keysToInfos(layer, l, 0, 0);
38 | function checkPos(row:Int, col:Int, rule:String) {
39 | if (row != 0) {
40 | opt.warn('Row should be 0 is VIA layouts (found $row in "$rule")');
41 | }
42 | if (col < 0 || col >= keys.length) {
43 | opt.error('Column $col out of bounds for "$rule"');
44 | return true;
45 | }
46 | return false;
47 | }
48 | if (opt.rangeDefs.length > 0) {
49 | var nkeys = [];
50 | for (rdef in opt.rangeDefs) {
51 | if (checkPos(rdef.row, rdef.col, rdef.rule)) continue;
52 | nkeys = nkeys.concat(keys.slice(rdef.col, rdef.col + rdef.count));
53 | }
54 | keys = nkeys;
55 | }
56 | else for (def in opt.moveDefs) {
57 | if (checkPos(def.srcRow, def.srcCol, def.rule)) continue;
58 | var sub = keys.splice(def.srcCol, def.count);
59 | sub.reverse();
60 | for (key in sub) keys.insert(def.dstCol, key);
61 | }
62 | vLayers.push(keys);
63 | }
64 | return vLayers;
65 | }
66 | static function procVialLayers(opt:VilToDrawerOpt) {
67 | var vLayers:Array> = [];
68 | for (i => layer in opt.root.layout) { // Vial
69 | var rows = [];
70 | var infId = 0;
71 | for (k => row in layer) {
72 | var infos = keysToInfos(row, i, k, infId);
73 | rows.push(infos);
74 | infId += infos.length;
75 | }
76 | function checkPos(row:Int, col:Int, rule:String) {
77 | if (row < 0 || row >= rows.length) {
78 | opt.error('Row $row out of bounds for "$rule"');
79 | return true;
80 | }
81 | if (col < 0 || col >= rows[row].length) {
82 | opt.error('Column $col out of bounds for "$rule"');
83 | return true;
84 | }
85 | return false;
86 | }
87 | //
88 | for (ko in opt.keyOverrides) if (ko.layer == i) {
89 | if (checkPos(ko.row, ko.col, ko.rule)) continue;
90 | rows[ko.row][ko.col].key = ko.key;
91 | }
92 | //
93 | var keys = [];
94 | if (opt.rangeDefs.length > 0) for (rd in opt.rangeDefs) {
95 | if (checkPos(rd.row, rd.col, rd.rule)) continue;
96 | keys = keys.concat(rows[rd.row].slice(rd.col, rd.count));
97 | }
98 | else {
99 | //
100 | for (moveDef in opt.moveDefs) {
101 | if (checkPos(moveDef.srcRow, moveDef.srcCol, moveDef.rule)) continue;
102 | var sub = rows[moveDef.srcRow].splice(moveDef.srcCol, moveDef.count);
103 | sub.reverse();
104 | for (key in sub) rows[moveDef.dstRow].insert(moveDef.dstCol, key);
105 | }
106 | //
107 | var rowCount = layer.length;
108 | var halfRowCount = rowCount >> 1;
109 | var newRows = [for (_ in 0 ... rowCount) null];
110 | for (rk => row in rows) {
111 | var dk = rk;
112 | if (opt.halfAfterHalf) {
113 | if (dk >= halfRowCount) {
114 | dk = (dk - halfRowCount) * 2 + 1;
115 | if (opt.mirrorRightHalf) row.reverse();
116 | } else {
117 | dk *= 2;
118 | }
119 | }
120 | newRows[dk] = row;
121 | }
122 | //
123 | for (row in newRows) for (key in row) keys.push(key);
124 | }
125 | vLayers.push(keys);
126 | }
127 | return vLayers;
128 | }
129 | static function postProcLayers(vLayers:Array>, opt:VilToDrawerOpt) {
130 | for (encDef in opt.encoderDefs) {
131 | for (vLayer in vLayers) {
132 | for (ki in vLayer.filter(ki -> ki.col == encDef.col && ki.row == encDef.row)) {
133 | ki.encoder = encDef.index;
134 | }
135 | }
136 | }
137 | var omitNonKeys = opt.omitNonKeys;
138 | if (omitNonKeys != 0) {
139 | var k = vLayers[0].length;
140 | while (--k >= 0) {
141 | if (vLayers[0][k].key != "KC_NO") continue;
142 | var isNon = true;
143 | for (l2 in 1 ... vLayers.length) {
144 | if (omitNonKeys > 0 && l2 >= omitNonKeys) continue;
145 | if (vLayers[l2][k].key == "KC_NO") continue;
146 | isNon = false;
147 | break;
148 | }
149 | if (isNon) for (vkeys in vLayers) vkeys.splice(k, 1);
150 | }
151 | }
152 | if (opt.omitM1) {
153 | var k = vLayers[0].length;
154 | while (--k >= 0) {
155 | if (!vLayers[0][k].key.isM1()) continue;
156 | var isM1 = true;
157 | for (l in 0 ... vLayers.length) {
158 | if (vLayers[l][k].key.isM1()) continue;
159 | isM1 = false;
160 | break;
161 | }
162 | if (!isM1) continue;
163 | for (l in 0 ... vLayers.length) {
164 | vLayers[l].splice(k, 1);
165 | }
166 | }
167 | }
168 | }
169 | public static function runTxt(opt:VilToDrawerOpt):String {
170 | var vkm = opt.root;
171 | //
172 | inline function getLayerName(li:Int) {
173 | return opt.getLayerName(li, true);
174 | }
175 | var isVial = !opt.isVIA;
176 |
177 | // layer -> keys[]
178 | var vLayers:Array> = isVial ? procVialLayers(opt) : procViaLayers(opt);
179 | postProcLayers(vLayers, opt);
180 | if (opt.outKeys != null) {
181 | for (vLayer in vLayers) {
182 | for (kp in vLayer) {
183 | opt.outKeys.push(kp);
184 | }
185 | }
186 | }
187 |
188 | //
189 | var dkLayers:DynamicAccess = new DynamicAccess();
190 | var dkLayerList:Array = [];
191 | var layerNames = [];
192 | var usesEncoders = false;
193 | for (li => vkeys in vLayers) {
194 | if (opt.includeLayers.length > 0 && !opt.includeLayers.contains(li)) continue;
195 | var dkeys = [];
196 | for (k => kc in vkeys) {
197 | var dk:DrawerKey = kc.key.toDrawerKey(opt, kc.encoder != null);
198 | dk = dk.postproc(opt);
199 | // is this a held key?
200 | var mo = "MO(" + li + ")";
201 | var lts = "LT" + li + "(";
202 | var held = false;
203 | for (vkeys2 in vLayers) {
204 | if (vkeys == vkeys2) continue;
205 | var kc2 = vkeys2[k].key;
206 | if (kc2 == null) continue;
207 | if (kc2 == mo || (kc2:String).startsWith(lts)) {
208 | held = true;
209 | break;
210 | }
211 | }
212 |
213 | if (held) {
214 | var dkx = dk.toExt();
215 | dkx.type = "held";
216 | if (dkx.t == VialKeyNames.map["KC_TRNS"]) dkx.t = "";
217 | dk = dkx;
218 | }
219 |
220 | if (kc.encoder != null && isVial) {
221 | var encPair = opt.root.encoder_layout[kc.layer][kc.encoder];
222 | var encDn = encPair[0].toDrawerKey(opt, true).postproc(opt).toFlat(DrawerKeyAction.Tap);
223 | var encUp = encPair[1].toDrawerKey(opt, true).postproc(opt).toFlat(DrawerKeyAction.Tap);
224 | var dkx = dk.toExt();
225 | dkx.s = "↑" + encUp;
226 | dkx.h = "↓" + encDn;
227 | dkx.type = "encoder";
228 | usesEncoders = true;
229 | dk = dkx;
230 | }
231 |
232 | if (opt.showKeyPos) {
233 | var dkx = dk.toExt();
234 | dkx.s = kc.row + "," + kc.col;
235 | dk = dkx;
236 | }
237 |
238 | dkeys.push(dk);
239 | }
240 | var ln = getLayerName(li);
241 | dkLayers[ln] = dkeys;
242 | dkLayerList.push(dkeys);
243 | layerNames.push(ln);
244 | }
245 | if (needsHxOrder) dkLayers["__hxOrder__"] = cast layerNames;
246 | //
247 | var extraStyle:Array = [];
248 | if (usesEncoders) {
249 | //trace("enc!");
250 | /*extraStyle = extraStyle.concat([
251 | "rect.encoder {",
252 | "\t" + "rx: 1024;",
253 | "\t" + "ry: 1024;",
254 | "\t" + "transform: scale(1.2);",
255 | "}",
256 | ]);*/
257 | }
258 | if (opt.markNonKeysAs != null) {
259 | var found = false;
260 | var trns = VialKeyNames.map["KC_TRNS"];
261 | function isNon(dk:DrawerKey) {
262 | return dk == null || dk == trns;
263 | }
264 | for (k in 0 ... dkLayerList[0].length) {
265 | if (!isNon(dkLayerList[0][k])) continue;
266 | if (dkLayerList.filter(dkeys -> !isNon(dkeys[k])).length != 0) continue;
267 | for (dkeys in dkLayerList) {
268 | var dk = dkeys[k].toExt();
269 | dk.t = "";
270 | dk.type = opt.markNonKeysAs;
271 | dkeys[k] = dk;
272 | found = true;
273 | }
274 | }
275 | if (found) switch (opt.markNonKeysAs) {
276 | case "unused": extraStyle = extraStyle.concat([
277 | "rect.unused, rect.combo.unused {",
278 | "\t" + "fill: transparent;",
279 | "\t" + "stroke-dasharray: 4, 6;",
280 | "\t" + "stroke-width: 2;",
281 | "}",
282 | ]);
283 | case "hidden": extraStyle = extraStyle.concat([
284 | "rect.hidden, rect.combo.hidden {",
285 | "\t" + "fill: transparent;",
286 | "\t" + "stroke-width: 0;",
287 | "}",
288 | ]);
289 | }
290 | }
291 | //
292 | var dCombos:Array = [];
293 | if (vkm.combo != null && opt.combos) for (vCombo in vkm.combo) {
294 | var iResult = vCombo.length - 1;
295 | var inKeys = [];
296 | for (i in 0 ... iResult) if (vCombo[i].isValid()) inKeys.push(vCombo[i]);
297 | if (inKeys.length < 2) continue;
298 | var cResult = vCombo[iResult];
299 | if (!cResult.isValid()) continue;
300 |
301 | for (li => vKeys in vLayers) {
302 | var ln = getLayerName(li);
303 | if (!dkLayers.exists(ln)) continue;
304 | var keyPos = [];
305 | for (key in inKeys) {
306 | var kp = -1;
307 | for (kid => ki in vKeys) {
308 | if (ki.key == key) {
309 | kp = kid;
310 | break;
311 | }
312 | }
313 | if (kp >= 0) keyPos.push(kp); else break;
314 | }
315 | if (keyPos.length < inKeys.length) continue;
316 | var dk = cResult.toDrawerKey(opt);
317 | dk = dk.postproc(opt);
318 | dCombos.push({
319 | p: keyPos,
320 | k: dk,
321 | l: [ln],
322 | });
323 | }
324 | }
325 | //
326 | var dkm:DrawerKeymap = {
327 | layout: {
328 | qmk_keyboard: opt.qmkKeyboard
329 | },
330 | layers: dkLayers,
331 | combos: dCombos,
332 | };
333 | if (extraStyle.length > 0) {
334 | dkm.draw_config = {
335 | svg_extra_style: extraStyle.join("\n"),
336 | };
337 | }
338 | if (opt.qmkLayout != null && opt.qmkLayout != "") dkm.layout.qmk_layout = opt.qmkLayout;
339 |
340 | if (opt.yamlLike) {
341 | var rxId = ~/^[_a-zA-Z][_a-zA-Z0-9]*$/;
342 | function idOrJson(val:Any) {
343 | if (val is String && rxId.match(val)) return val;
344 | return Json.stringify(val);
345 | }
346 | var yb = new StringBuf();
347 | yb.add('layout: ' + Json.stringify(dkm.layout) + '\n');
348 | yb.add('layers:\n');
349 | for (ln in layerNames) {
350 | yb.add(' ' + idOrJson(ln) + ':\n');
351 | for (key in dkm.layers[ln]) {
352 | yb.add(' - ' + idOrJson(key) + '\n');
353 | }
354 | }
355 | if (dkm.combos != null && dkm.combos.length > 0) {
356 | yb.add('combos:\n');
357 | for (combo in dkm.combos) {
358 | yb.add(' - ' + idOrJson(combo) + '\n');
359 | }
360 | }
361 | for (fd in Reflect.fields(dkm)) {
362 | switch (fd) {
363 | case "layout", "layers", "combos": continue;
364 | }
365 | yb.add(fd + ': ' + Json.stringify(Reflect.field(dkm, fd)) + '\n');
366 | }
367 | return yb.toString();
368 | } else {
369 | if (needsHxOrder) {
370 | return tools.JsonPrinterWithOrder.print(dkm, null, " ");
371 | } else {
372 | return Json.stringify(dkm, null, " ");
373 | }
374 | }
375 | }
376 | }
377 | typedef VialKeyInfo = {
378 | layer:Int,
379 | row:Int,
380 | col:Int,
381 | ind:Int,
382 | ?encoder:Int,
383 | key:VialKey
384 | };
--------------------------------------------------------------------------------
/src/VilToDrawerOpt.hx:
--------------------------------------------------------------------------------
1 | package ;
2 | import drawer.DrawerKeymap;
3 | import haxe.DynamicAccess;
4 | import haxe.Json;
5 | import tools.ERegTools;
6 | import vial.VialKeymap;
7 | import vial.VialKey;
8 | import VilToDrawer;
9 | using StringTools;
10 | using tools.ERegTools;
11 |
12 | /**
13 | * ...
14 | * @author YellowAfterlife
15 | */
16 | class VilToDrawerOpt {
17 | public var root:VialKeymap;
18 | public var isVIA:Bool;
19 | public var qmkKeyboard:String = null;
20 | public var qmkLayout:String = null;
21 | public var halfAfterHalf = false;
22 | public var mirrorRightHalf = false;
23 | public var omitNonKeys = 0;
24 | public var markNonKeysAs:String = null;
25 | public var omitM1 = false;
26 | public var includeLayers:Array = [];
27 | public var layerNames:Array = [];
28 | public var moveDefs:Array = [];
29 | public var rangeDefs:Array = [];
30 | public var encoderDefs:Array = [];
31 | public var keyOverrides:Array = [];
32 | /** */
33 | public var keyNames:Map = new Map();
34 | public var showKeyPos = false;
35 | public var yamlLike = true;
36 |
37 | public var combos = true;
38 | public var outKeys:Array = null;
39 |
40 | public function new() {}
41 |
42 | public dynamic function log(level:String, v:Any) {
43 | trace('[$level]', v);
44 | }
45 | public dynamic function info(v:Any) {
46 | log("info", v);
47 | }
48 | public dynamic function warn(v:Any) {
49 | log("warn", v);
50 | }
51 | public dynamic function error(v:Any) {
52 | log("error", v);
53 | }
54 |
55 | public function getLayerName(i:Int, long:Bool) {
56 | if (i < layerNames.length) {
57 | var l = layerNames[i];
58 | return long ? l.long : l.short;
59 | } else return "L" + i;
60 | }
61 |
62 | public function parseVil(txt:String) {
63 | root = Json.parse(txt);
64 | isVIA = root.layers != null;
65 | }
66 |
67 | static var rxLayerShortLong = ~/^(\S{1,6})(?::.*|\s+\(.*\))$/;
68 | public function parseLayerNames(txt:String) {
69 | txt = txt.replace("\r", "").trim();
70 | if (txt == "") return;
71 | var rx = rxLayerShortLong;
72 | for (line in txt.split("\n")) {
73 | line = line.trim();
74 | if (rx.match(line)) {
75 | layerNames.push({
76 | short: rx.matched(1),
77 | long: rx.matched(0),
78 | });
79 | } else if (line.length <= 6) {
80 | layerNames.push({
81 | short: line,
82 | long: line,
83 | });
84 | } else {
85 | var lis = "L" + layerNames.length;
86 | layerNames.push({
87 | short: lis,
88 | long: lis + ": " + line,
89 | });
90 | }
91 | }
92 | }
93 |
94 | static var rxMoveDef = new EReg("^\\s*"
95 | + "(\\d+),\\s*"
96 | + "(\\d+)\\s*"
97 | + "(?:"
98 | + "\\[\\s*" + "(\\d+)" + "\\s*\\]\\s*" + "|" // row,col[n]
99 | + "\\-\\s*" + "(\\d+)" + "\\s*" // row,col-col2
100 | + ")?"
101 | + "=>\\s*"
102 | + "(\\d+),\\s*"
103 | + "(\\d+)"
104 | + "\\s*$", "gm");
105 | public function parseMoveDefs(txt:String) {
106 | ERegTools.each(rxMoveDef, txt, function(rx:EReg) {
107 | var srcCol = Std.parseInt(rx.matched(2));
108 | var ns = rx.matched(3);
109 | var n:Int;
110 | if (ns != null) {
111 | n = Std.parseInt(ns);
112 | } else if ((ns = rx.matched(4)) != null) {
113 | n = Std.parseInt(ns) + 1 - srcCol;
114 | } else n = 1;
115 | var at = 5;
116 | moveDefs.push({
117 | srcRow: Std.parseInt(rx.matched(1)),
118 | srcCol: srcCol,
119 | count: n,
120 | dstRow: Std.parseInt(rx.matched(at)),
121 | dstCol: Std.parseInt(rx.matched(at + 1)),
122 | rule: rx.matched(0),
123 | });
124 | });
125 | }
126 |
127 | static var rxRangeDef = new EReg("^\\s*"
128 | + "(\\d+),\\s*"
129 | + "(\\d+)\\s*"
130 | + "(?:" + "\\-\\s*" + "(\\d+)" + "\\s*" + ")?"
131 | + "$", "gm");
132 | public function parseRangeDefs(txt:String) {
133 | ERegTools.each(rxRangeDef, txt, function(rx:EReg) {
134 | var col = Std.parseInt(rx.matched(2));
135 | var tillStr = rx.matched(3);
136 | var count:Int;
137 | if (tillStr != null) {
138 | var till = Std.parseInt(rx.matched(3));
139 | count = till + 1 - col;
140 | } else count = 1;
141 | rangeDefs.push({
142 | row: Std.parseInt(rx.matched(1)),
143 | col: col,
144 | count: count,
145 | rule: rx.matched(0),
146 | });
147 | });
148 | }
149 |
150 | public function parseIncludeLayers(txt:String) {
151 | ~/\d+/g.each(txt, function(rx:EReg) {
152 | var s = rx.matched(0);
153 | var i = Std.parseInt(s);
154 | if (i == null) {
155 | error('"$s" is not a valid layer number');
156 | } else includeLayers.push(i);
157 | });
158 | }
159 |
160 | static var rxKeyOverride = ~/^\s*(\d+),\s*(\d+),\s*(\d+)\s*=>\s*(.+)/gm;
161 | static var rxKeyOverrideName = ~/^\s*(".*?[^\\]")\s*=>\s*(.+)/gm;
162 | public function parseKeyOverrides(txt:String) {
163 | ERegTools.each(rxKeyOverride, txt, function(rx:EReg) {
164 | keyOverrides.push({
165 | layer: Std.parseInt(rx.matched(1)),
166 | row: Std.parseInt(rx.matched(2)),
167 | col: Std.parseInt(rx.matched(3)),
168 | key: rx.matched(4),
169 | rule: rx.matched(0),
170 | });
171 | });
172 | ERegTools.each(rxKeyOverrideName, txt, function(rx:EReg) {
173 | var key = try {
174 | Json.parse(rx.matched(1));
175 | } catch (x:Dynamic) {
176 | error("Couldn't parse " + rx.matched(1) + ": " + x);
177 | return;
178 | }
179 | keyNames[key.toLowerCase()] = rx.matched(2);
180 | });
181 | }
182 |
183 | static var rxEncoderDef = ~/^\s*(\d+),\s*(\d+)\s*=>\s*(\d+)/gm;
184 | public function parseEncoderDefs(txt:String) {
185 | ERegTools.each(rxEncoderDef, txt, function(rx:EReg) {
186 | encoderDefs.push({
187 | row: Std.parseInt(rx.matched(1)),
188 | col: Std.parseInt(rx.matched(2)),
189 | index: Std.parseInt(rx.matched(3)),
190 | rule: rx.matched(0),
191 | });
192 | });
193 | }
194 | }
195 | typedef VilToDrawerKeyOverride = {
196 | var layer:Int;
197 | var row:Int;
198 | var col:Int;
199 | var key:String;
200 | var rule:String;
201 | }
202 | typedef VilToDrawerLayerName = {
203 | var short:String;
204 | var long:String;
205 | };
206 | typedef VilToDrawerMoveDef = {
207 | var srcRow:Int;
208 | var srcCol:Int;
209 | var dstRow:Int;
210 | var dstCol:Int;
211 | var count:Int;
212 | var rule:String;
213 | };
214 | typedef VilToDrawerEncoderDef = {
215 | var row:Int;
216 | var col:Int;
217 | var index:Int;
218 | var rule:String;
219 | };
220 | typedef VilToDrawerRangeDef = {
221 | var row:Int;
222 | var col:Int;
223 | var count:Int;
224 | var rule:String;
225 | };
--------------------------------------------------------------------------------
/src/drawer/DrawerKeymap.hx:
--------------------------------------------------------------------------------
1 | package drawer;
2 | import haxe.extern.EitherType;
3 | import haxe.DynamicAccess;
4 |
5 | /**
6 | * @author YellowAfterlife
7 | */
8 | typedef DrawerKeymap = {
9 | var layout:DrawerKeymapLayout;
10 | var layers:DynamicAccess;
11 | var?combos:Array;
12 | var?draw_config:DrawerConfig;
13 | }
14 | typedef DrawerKeymapLayout = {
15 | var qmk_keyboard:String;
16 | var?qmk_layout:String;
17 | };
18 | typedef DrawerConfig = {
19 | var svg_extra_style:String;
20 | }
21 | typedef DrawerCombo = {
22 | var p:Array;
23 | var k:DrawerKey;
24 | var l:Array;
25 | };
26 | typedef DrawerKeyExt = {
27 | var?t:String;
28 | var?h:String;
29 | var?s:String;
30 | var?type:String;
31 | };
32 | typedef DrawerKeyFlat = String;
33 | abstract DrawerKey(Dynamic)
34 | from DrawerKeyFlat from DrawerKeyExt
35 | to DrawerKeyFlat to DrawerKeyExt
36 | {
37 | public function isFlat():Bool {
38 | return this == null || (this is String);
39 | }
40 | public function toExt():DrawerKeyExt {
41 | if (this == null) return { t: "" };
42 | return isFlat() ? { t: this } : this;
43 | }
44 | public function toFlat(a:DrawerKeyAction):DrawerKeyFlat {
45 | if (isFlat()) return this;
46 | return Reflect.field(this, a);
47 | }
48 | function postproc_1(s:String, opt:VilToDrawerOpt) {
49 | if (s == null) return null;
50 | var lqs = s.toLowerCase();
51 | return opt.keyNames.exists(lqs) ? opt.keyNames[lqs] : s;
52 | }
53 | public function postproc(opt:VilToDrawerOpt):DrawerKey {
54 | if (isFlat()) return postproc_1(this, opt);
55 | var ext:DrawerKeyExt = this;
56 | if (ext.s != null && StringTools.endsWith(ext.s, "+")) {
57 | var lqs = (ext.s + ext.t).toLowerCase();
58 | if (opt.keyNames.exists(lqs)) {
59 | ext.t = opt.keyNames[lqs];
60 | ext.s = "";
61 | return ext;
62 | }
63 | }
64 | if (ext.t != null) ext.t = postproc_1(ext.t, opt);
65 | if (ext.h != null) ext.h = postproc_1(ext.h, opt);
66 | if (ext.s != null) ext.s = postproc_1(ext.s, opt);
67 | return ext;
68 | }
69 | }
70 | enum abstract DrawerKeyAction(String) to String {
71 | var Tap = "t";
72 | var Hold = "h";
73 | var Shift = "s";
74 | }
75 | typedef DrawerLayer = Array;
76 |
--------------------------------------------------------------------------------
/src/tools/BuildDate.hx:
--------------------------------------------------------------------------------
1 | package tools;
2 |
3 | /**
4 | * ...
5 | * @author YellowAfterlife
6 | */
7 | class BuildDate {
8 | public static macro function asString() {
9 | return macro $v{DateTools.format(Date.now(), "%Y-%m-%d--%H-%M-%S")};
10 | }
11 | }
--------------------------------------------------------------------------------
/src/tools/ERegTools.hx:
--------------------------------------------------------------------------------
1 | package tools;
2 |
3 | /**
4 | * ...
5 | * @author YellowAfterlife
6 | */
7 | class ERegTools {
8 | private static var escapeRx_1:EReg = new EReg("([.*+?^${}()|[\\]\\/\\\\])", 'g');
9 | public static function escapeRx(s:String):String {
10 | return escapeRx_1.replace(s, "\\$1");
11 | }
12 |
13 | public static function each(r:EReg, s:String, f:EReg->Void) {
14 | var i:Int = 0;
15 | while (r.matchSub(s, i)) {
16 | var p = r.matchedPos();
17 | //if (p.pos < i) break; // bug on --run..?
18 | //trace(p, i);
19 | f(r);
20 | i = p.pos + p.len;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/tools/JsonParserWithComments.hx:
--------------------------------------------------------------------------------
1 | package tools;
2 |
3 | /**
4 | A version of Haxe's standard library JSON parser that will also accept comments,
5 | fields without quotes, fields in single quotes, trailing commas...
6 |
7 | @see https://haxe.org/manual/std-Json-parsing.html
8 | **/
9 | class JsonParserWithComments {
10 | /**
11 | Parses given JSON-encoded `str` and returns the resulting object.
12 |
13 | JSON objects are parsed into anonymous structures and JSON arrays
14 | are parsed into `Array`.
15 |
16 | If given `str` is not valid JSON, an exception will be thrown.
17 |
18 | If `str` is null, the result is unspecified.
19 | **/
20 | static public inline function parse(str:String):Dynamic {
21 | return new JsonParserWithComments(str).doParse();
22 | }
23 |
24 | var str:String;
25 | var pos:Int;
26 |
27 | function new(str:String) {
28 | this.str = str;
29 | this.pos = 0;
30 | }
31 |
32 | function doParse():Dynamic {
33 | var result = parseRec();
34 | var c;
35 | while (!StringTools.isEof(c = nextChar())) {
36 | switch (c) {
37 | case ' '.code, '\r'.code, '\n'.code, '\t'.code:
38 | // allow trailing whitespace
39 | default:
40 | invalidChar();
41 | }
42 | }
43 | return result;
44 | }
45 |
46 | function parseRec():Dynamic {
47 | while (true) {
48 | var c = nextChar();
49 | switch (c) {
50 | case ' '.code, '\r'.code, '\n'.code, '\t'.code:
51 | case '/'.code if (peekChar() == '/'.code): {
52 | pos++;
53 | while (pos < str.length) {
54 | c = peekChar();
55 | if (c == '\r'.code || c == '\n'.code) {
56 | break;
57 | } else pos++;
58 | }
59 | }
60 | case '/'.code if (peekChar() == '*'.code): {
61 | pos++;
62 | while (pos < str.length) {
63 | c = peekChar();
64 | if (c == '*'.code && peekChar(1) == '/'.code) {
65 | pos += 2;
66 | break;
67 | } else pos += 1;
68 | }
69 | };
70 | // loop
71 | case '{'.code:
72 | var obj = {}, field = null, comma:Null = null;
73 | while (true) {
74 | var c = nextChar();
75 | switch (c) {
76 | case ' '.code, '\r'.code, '\n'.code, '\t'.code:
77 | // loop
78 | case '}'.code:
79 | if (field != null/* || comma == false*/)
80 | invalidChar();
81 | return obj;
82 | case ':'.code:
83 | if (field == null)
84 | invalidChar();
85 | Reflect.setField(obj, field, parseRec());
86 | field = null;
87 | comma = true;
88 | case ','.code:
89 | if (comma) comma = false else invalidChar();
90 | case '"'.code:
91 | if (field != null || comma) invalidChar();
92 | field = parseString();
93 | case _ if (c == "_".code
94 | || c >= "a".code && c <= "z".code
95 | || c >= "A".code && c <= "Z".code
96 | ):
97 | if (field != null || comma) invalidChar();
98 | var start = pos - 1;
99 | while (pos < str.length) {
100 | c = peekChar();
101 | if (c == "_".code
102 | || c >= "a".code && c <= "z".code
103 | || c >= "A".code && c <= "Z".code
104 | || c >= "0".code && c <= "9".code
105 | ) {
106 | pos++;
107 | } else break;
108 | }
109 | field = str.substring(start, pos);
110 | default:
111 | invalidChar();
112 | }
113 | }
114 | case '['.code:
115 | var arr = [], comma:Null = null;
116 | while (true) {
117 | var c = nextChar();
118 | switch (c) {
119 | case ' '.code, '\r'.code, '\n'.code, '\t'.code:
120 | // loop
121 | case ']'.code:
122 | //if (comma == false) invalidChar();
123 | return arr;
124 | case ','.code:
125 | if (comma) comma = false else invalidChar();
126 | default:
127 | if (comma) invalidChar();
128 | pos--;
129 | arr.push(parseRec());
130 | comma = true;
131 | }
132 | }
133 | case 't'.code:
134 | var save = pos;
135 | if (nextChar() != 'r'.code || nextChar() != 'u'.code || nextChar() != 'e'.code) {
136 | pos = save;
137 | invalidChar();
138 | }
139 | return true;
140 | case 'f'.code:
141 | var save = pos;
142 | if (nextChar() != 'a'.code || nextChar() != 'l'.code || nextChar() != 's'.code || nextChar() != 'e'.code) {
143 | pos = save;
144 | invalidChar();
145 | }
146 | return false;
147 | case 'n'.code:
148 | var save = pos;
149 | if (nextChar() != 'u'.code || nextChar() != 'l'.code || nextChar() != 'l'.code) {
150 | pos = save;
151 | invalidChar();
152 | }
153 | return null;
154 | case '"'.code, "'".code:
155 | return parseString(c);
156 | case '0'.code, '1'.code, '2'.code, '3'.code, '4'.code, '5'.code, '6'.code, '7'.code, '8'.code, '9'.code, '-'.code:
157 | return parseNumber(c);
158 | default:
159 | invalidChar();
160 | }
161 | }
162 | }
163 |
164 | function parseString(quoteChar:Int = '"'.code) {
165 | var start = pos;
166 | var buf:StringBuf = null;
167 | #if target.unicode
168 | var prev = -1;
169 | inline function cancelSurrogate() {
170 | // invalid high surrogate (not followed by low surrogate)
171 | buf.addChar(0xFFFD);
172 | prev = -1;
173 | }
174 | #end
175 | while (true) {
176 | var c = nextChar();
177 | if (c == quoteChar)
178 | break;
179 | if (c == '\\'.code) {
180 | if (buf == null) {
181 | buf = new StringBuf();
182 | }
183 | buf.addSub(str, start, pos - start - 1);
184 | c = nextChar();
185 | #if target.unicode
186 | if (c != "u".code && prev != -1)
187 | cancelSurrogate();
188 | #end
189 | switch (c) {
190 | case "r".code:
191 | buf.addChar("\r".code);
192 | case "n".code:
193 | buf.addChar("\n".code);
194 | case "t".code:
195 | buf.addChar("\t".code);
196 | case "b".code:
197 | buf.addChar(8);
198 | case "f".code:
199 | buf.addChar(12);
200 | case "/".code, '\\'.code, '"'.code, "'".code:
201 | buf.addChar(c);
202 | case 'u'.code:
203 | var uc:Int = Std.parseInt("0x" + str.substr(pos, 4));
204 | pos += 4;
205 | #if !target.unicode
206 | if (uc <= 0x7F)
207 | buf.addChar(uc);
208 | else if (uc <= 0x7FF) {
209 | buf.addChar(0xC0 | (uc >> 6));
210 | buf.addChar(0x80 | (uc & 63));
211 | } else if (uc <= 0xFFFF) {
212 | buf.addChar(0xE0 | (uc >> 12));
213 | buf.addChar(0x80 | ((uc >> 6) & 63));
214 | buf.addChar(0x80 | (uc & 63));
215 | } else {
216 | buf.addChar(0xF0 | (uc >> 18));
217 | buf.addChar(0x80 | ((uc >> 12) & 63));
218 | buf.addChar(0x80 | ((uc >> 6) & 63));
219 | buf.addChar(0x80 | (uc & 63));
220 | }
221 | #else
222 | if (prev != -1) {
223 | if (uc < 0xDC00 || uc > 0xDFFF)
224 | cancelSurrogate();
225 | else {
226 | buf.addChar(((prev - 0xD800) << 10) + (uc - 0xDC00) + 0x10000);
227 | prev = -1;
228 | }
229 | } else if (uc >= 0xD800 && uc <= 0xDBFF)
230 | prev = uc;
231 | else
232 | buf.addChar(uc);
233 | #end
234 | default:
235 | throw "Invalid escape sequence \\" + String.fromCharCode(c) + " at position " + (pos - 1);
236 | }
237 | start = pos;
238 | }
239 | #if !(target.unicode)
240 | // ensure utf8 chars are not cut
241 | else if (c >= 0x80) {
242 | pos++;
243 | if (c >= 0xFC)
244 | pos += 4;
245 | else if (c >= 0xF8)
246 | pos += 3;
247 | else if (c >= 0xF0)
248 | pos += 2;
249 | else if (c >= 0xE0)
250 | pos++;
251 | }
252 | #end
253 | else if (StringTools.isEof(c))
254 | throw "Unclosed string";
255 | }
256 | #if target.unicode
257 | if (prev != -1)
258 | cancelSurrogate();
259 | #end
260 | if (buf == null) {
261 | return str.substr(start, pos - start - 1);
262 | } else {
263 | buf.addSub(str, start, pos - start - 1);
264 | return buf.toString();
265 | }
266 | }
267 |
268 | inline function parseNumber(c:Int):Dynamic {
269 | var start = pos - 1;
270 | var minus = c == '-'.code, digit = !minus, zero = c == '0'.code;
271 | var point = false, e = false, pm = false, end = false;
272 | while (true) {
273 | c = nextChar();
274 | switch (c) {
275 | case '0'.code:
276 | if (zero && !point)
277 | invalidNumber(start);
278 | if (minus) {
279 | minus = false;
280 | zero = true;
281 | }
282 | digit = true;
283 | case '1'.code, '2'.code, '3'.code, '4'.code, '5'.code, '6'.code, '7'.code, '8'.code, '9'.code:
284 | if (zero && !point)
285 | invalidNumber(start);
286 | if (minus)
287 | minus = false;
288 | digit = true;
289 | zero = false;
290 | case '.'.code:
291 | if (minus || point || e)
292 | invalidNumber(start);
293 | digit = false;
294 | point = true;
295 | case 'e'.code, 'E'.code:
296 | if (minus || zero || e)
297 | invalidNumber(start);
298 | digit = false;
299 | e = true;
300 | case '+'.code, '-'.code:
301 | if (!e || pm)
302 | invalidNumber(start);
303 | digit = false;
304 | pm = true;
305 | default:
306 | if (!digit)
307 | invalidNumber(start);
308 | pos--;
309 | end = true;
310 | }
311 | if (end)
312 | break;
313 | }
314 |
315 | var f = Std.parseFloat(str.substr(start, pos - start));
316 | if(point) {
317 | return f;
318 | } else {
319 | var i = Std.int(f);
320 | return if (i == f) i else f;
321 | }
322 | }
323 |
324 | inline function nextChar() {
325 | return StringTools.fastCodeAt(str, pos++);
326 | }
327 |
328 | inline function peekChar(offset:Int = 0) {
329 | return StringTools.fastCodeAt(str, pos + offset);
330 | }
331 |
332 | function invalidChar() {
333 | pos--; // rewind
334 | throw "Invalid char " + StringTools.fastCodeAt(str, pos) + " at position " + pos;
335 | }
336 |
337 | function invalidNumber(start:Int) {
338 | throw "Invalid number at position " + start + ": " + str.substr(start, pos - start);
339 | }
340 | }
341 |
--------------------------------------------------------------------------------
/src/tools/JsonPrinterWithOrder.hx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C)2005-2019 Haxe Foundation
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a
5 | * copy of this software and associated documentation files (the "Software"),
6 | * to deal in the Software without restriction, including without limitation
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 | * and/or sell copies of the Software, and to permit persons to whom the
9 | * Software is furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 | * DEALINGS IN THE SOFTWARE.
21 | */
22 |
23 | package tools;
24 |
25 | /**
26 | An implementation of JSON printer in Haxe.
27 |
28 | This class is used by `haxe.Json` when native JSON implementation
29 | is not available.
30 |
31 | @see https://haxe.org/manual/std-Json-encoding.html
32 | **/
33 | class JsonPrinterWithOrder {
34 | /**
35 | Encodes `o`'s value and returns the resulting JSON string.
36 |
37 | If `replacer` is given and is not null, it is used to retrieve
38 | actual object to be encoded. The `replacer` function takes two parameters,
39 | the key and the value being encoded. Initial key value is an empty string.
40 |
41 | If `space` is given and is not null, the result will be pretty-printed.
42 | Successive levels will be indented by this string.
43 | **/
44 | static public function print(o:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String):String {
45 | var printer = new JsonPrinterWithOrder(replacer, space);
46 | printer.write("", o);
47 | return printer.buf.toString();
48 | }
49 |
50 | var buf:#if flash flash.utils.ByteArray #else StringBuf #end;
51 | var replacer:(key:Dynamic, value:Dynamic) -> Dynamic;
52 | var indent:String;
53 | var pretty:Bool;
54 | var nind:Int;
55 |
56 | function new(replacer:(key:Dynamic, value:Dynamic) -> Dynamic, space:String) {
57 | this.replacer = replacer;
58 | this.indent = space;
59 | this.pretty = space != null;
60 | this.nind = 0;
61 |
62 | #if flash
63 | buf = new flash.utils.ByteArray();
64 | buf.endian = flash.utils.Endian.BIG_ENDIAN;
65 | buf.position = 0;
66 | #else
67 | buf = new StringBuf();
68 | #end
69 | }
70 |
71 | inline function ipad():Void {
72 | if (pretty)
73 | add(StringTools.lpad('', indent, nind * indent.length));
74 | }
75 |
76 | inline function newl():Void {
77 | if (pretty)
78 | addChar('\n'.code);
79 | }
80 |
81 | function write(k:Dynamic, v:Dynamic) {
82 | if (replacer != null)
83 | v = replacer(k, v);
84 | switch (Type.typeof(v)) {
85 | case TUnknown:
86 | add('"???"');
87 | case TObject:
88 | objString(v);
89 | case TInt:
90 | add(#if (jvm || hl) Std.string(v) #else v #end);
91 | case TFloat:
92 | add(Math.isFinite(v) ? Std.string(v) : 'null');
93 | case TFunction:
94 | add('""');
95 | case TClass(c):
96 | if (c == String)
97 | quote(v);
98 | else if (c == Array) {
99 | var v:Array = v;
100 | addChar('['.code);
101 |
102 | var len = v.length;
103 | var last = len - 1;
104 | for (i in 0...len) {
105 | if (i > 0)
106 | addChar(','.code)
107 | else
108 | nind++;
109 | newl();
110 | ipad();
111 | write(i, v[i]);
112 | if (i == last) {
113 | nind--;
114 | newl();
115 | ipad();
116 | }
117 | }
118 | addChar(']'.code);
119 | } else if (c == haxe.ds.StringMap) {
120 | var v:haxe.ds.StringMap = v;
121 | var o = {};
122 | for (k in v.keys())
123 | Reflect.setField(o, k, v.get(k));
124 | objString(o);
125 | } else if (c == Date) {
126 | var v:Date = v;
127 | quote(v.toString());
128 | } else
129 | classString(v);
130 | case TEnum(_):
131 | var i = Type.enumIndex(v);
132 | add(Std.string(i));
133 | case TBool:
134 | add(#if (php || jvm || hl) (v ? 'true' : 'false') #else v #end);
135 | case TNull:
136 | add('null');
137 | }
138 | }
139 |
140 | extern inline function addChar(c:Int) {
141 | #if flash
142 | buf.writeByte(c);
143 | #else
144 | buf.addChar(c);
145 | #end
146 | }
147 |
148 | extern inline function add(v:String) {
149 | #if flash
150 | // argument is not always a string but will be automatically casted
151 | buf.writeUTFBytes(v);
152 | #else
153 | buf.add(v);
154 | #end
155 | }
156 |
157 | function classString(v:Dynamic) {
158 | fieldsString(v, Type.getInstanceFields(Type.getClass(v)));
159 | }
160 |
161 | inline function objString(v:Dynamic) {
162 | //{ +y:
163 | var fields = Reflect.fields(v);
164 | if (fields.remove("__hxOrder__")) {
165 | fields = Reflect.field(v, "__hxOrder__");
166 | }
167 | //}
168 | fieldsString(v, fields);
169 | }
170 |
171 | function fieldsString(v:Dynamic, fields:Array) {
172 | addChar('{'.code);
173 | var len = fields.length;
174 | var last = len - 1;
175 | var first = true;
176 | for (i in 0...len) {
177 | var f = fields[i];
178 | var value = Reflect.field(v, f);
179 | if (Reflect.isFunction(value))
180 | continue;
181 | if (first) {
182 | nind++;
183 | first = false;
184 | } else
185 | addChar(','.code);
186 | newl();
187 | ipad();
188 | quote(f);
189 | addChar(':'.code);
190 | if (pretty)
191 | addChar(' '.code);
192 | write(f, value);
193 | if (i == last) {
194 | nind--;
195 | newl();
196 | ipad();
197 | }
198 | }
199 | addChar('}'.code);
200 | }
201 |
202 | function quote(s:String) {
203 | #if neko
204 | if (s.length != neko.Utf8.length(s)) {
205 | quoteUtf8(s);
206 | return;
207 | }
208 | #end
209 | addChar('"'.code);
210 | var i = 0;
211 | var length = s.length;
212 | #if hl
213 | var prev = -1;
214 | #end
215 | while (i < length) {
216 | var c = StringTools.unsafeCodeAt(s, i++);
217 | switch (c) {
218 | case '"'.code:
219 | add('\\"');
220 | case '\\'.code:
221 | add('\\\\');
222 | case '\n'.code:
223 | add('\\n');
224 | case '\r'.code:
225 | add('\\r');
226 | case '\t'.code:
227 | add('\\t');
228 | case 8:
229 | add('\\b');
230 | case 12:
231 | add('\\f');
232 | default:
233 | #if flash
234 | if (c >= 128)
235 | add(String.fromCharCode(c))
236 | else
237 | addChar(c);
238 | #elseif hl
239 | if (prev >= 0) {
240 | if (c >= 0xD800 && c <= 0xDFFF) {
241 | addChar((((prev - 0xD800) << 10) | (c - 0xDC00)) + 0x10000);
242 | prev = -1;
243 | } else {
244 | addChar("□".code);
245 | prev = c;
246 | }
247 | } else {
248 | if (c >= 0xD800 && c <= 0xDFFF)
249 | prev = c;
250 | else
251 | addChar(c);
252 | }
253 | #else
254 | addChar(c);
255 | #end
256 | }
257 | }
258 | #if hl
259 | if (prev >= 0)
260 | addChar("□".code);
261 | #end
262 | addChar('"'.code);
263 | }
264 |
265 | #if neko
266 | function quoteUtf8(s:String) {
267 | var u = new neko.Utf8();
268 | neko.Utf8.iter(s, function(c) {
269 | switch (c) {
270 | case '\\'.code, '"'.code:
271 | u.addChar('\\'.code);
272 | u.addChar(c);
273 | case '\n'.code:
274 | u.addChar('\\'.code);
275 | u.addChar('n'.code);
276 | case '\r'.code:
277 | u.addChar('\\'.code);
278 | u.addChar('r'.code);
279 | case '\t'.code:
280 | u.addChar('\\'.code);
281 | u.addChar('t'.code);
282 | case 8:
283 | u.addChar('\\'.code);
284 | u.addChar('b'.code);
285 | case 12:
286 | u.addChar('\\'.code);
287 | u.addChar('f'.code);
288 | default:
289 | u.addChar(c);
290 | }
291 | });
292 | buf.add('"');
293 | buf.add(u.toString());
294 | buf.add('"');
295 | }
296 | #end
297 | }
298 |
--------------------------------------------------------------------------------
/src/via/ViaKeyNames.hx:
--------------------------------------------------------------------------------
1 | package via;
2 | /**
3 | * Generated by ViaKeyNamesGen.hx
4 | * from the-via/app: src/utils/key.ts
5 | */
6 | class ViaKeyNames {
7 | public static var map:Map = [
8 | "KC_NO" => "",
9 | "KC_TRNS" => "▽",
10 | "KC_ESC" => "Esc",
11 | "KC_A" => "A",
12 | "KC_B" => "B",
13 | "KC_C" => "C",
14 | "KC_D" => "D",
15 | "KC_E" => "E",
16 | "KC_F" => "F",
17 | "KC_G" => "G",
18 | "KC_H" => "H",
19 | "KC_I" => "I",
20 | "KC_J" => "J",
21 | "KC_K" => "K",
22 | "KC_L" => "L",
23 | "KC_M" => "M",
24 | "KC_N" => "N",
25 | "KC_O" => "O",
26 | "KC_P" => "P",
27 | "KC_Q" => "Q",
28 | "KC_R" => "R",
29 | "KC_S" => "S",
30 | "KC_T" => "T",
31 | "KC_U" => "U",
32 | "KC_V" => "V",
33 | "KC_W" => "W",
34 | "KC_X" => "X",
35 | "KC_Y" => "Y",
36 | "KC_Z" => "Z",
37 | "KC_1" => "!\n1",
38 | "KC_2" => "@\n2",
39 | "KC_3" => "#\n3",
40 | "KC_4" => "$\n4",
41 | "KC_5" => "%\n5",
42 | "KC_6" => "^\n6",
43 | "KC_7" => "&\n7",
44 | "KC_8" => "*\n8",
45 | "KC_9" => "(\n9",
46 | "KC_0" => ")\n0",
47 | "KC_MINS" => "_\n-",
48 | "KC_EQL" => "+\n=",
49 | "KC_GRV" => "~\n`",
50 | "KC_LBRC" => "{\n[",
51 | "KC_RBRC" => "}\n]",
52 | "KC_BSLS" => "|\n\\",
53 | "KC_SCLN" => ":\n;",
54 | "KC_QUOT" => "\"\n'",
55 | "KC_COMM" => "<\n,",
56 | "KC_DOT" => ">\n.",
57 | "KC_SLSH" => "?\n/",
58 | "KC_PEQL" => "=",
59 | "KC_PCMM" => ",",
60 | "KC_F1" => "F1",
61 | "KC_F2" => "F2",
62 | "KC_F3" => "F3",
63 | "KC_F4" => "F4",
64 | "KC_F5" => "F5",
65 | "KC_F6" => "F6",
66 | "KC_F7" => "F7",
67 | "KC_F8" => "F8",
68 | "KC_F9" => "F9",
69 | "KC_F10" => "F10",
70 | "KC_F11" => "F11",
71 | "KC_F12" => "F12",
72 | "KC_PSCR" => "Print Screen",
73 | "KC_SLCK" => "Scroll Lock",
74 | "KC_PAUS" => "Pause",
75 | "KC_TAB" => "Tab",
76 | "KC_BSPC" => "Backspace",
77 | "KC_INS" => "Insert",
78 | "KC_DEL" => "Del",
79 | "KC_HOME" => "Home",
80 | "KC_END" => "End",
81 | "KC_PGUP" => "Page Up",
82 | "KC_PGDN" => "Page Down",
83 | "KC_NLCK" => "Num\nLock",
84 | "KC_CAPS" => "Caps Lock",
85 | "KC_ENT" => "Enter",
86 | "KC_P1" => "1",
87 | "KC_P2" => "2",
88 | "KC_P3" => "3",
89 | "KC_P4" => "4",
90 | "KC_P5" => "5",
91 | "KC_P6" => "6",
92 | "KC_P7" => "7",
93 | "KC_P8" => "8",
94 | "KC_P9" => "9",
95 | "KC_P0" => "0",
96 | "KC_PSLS" => "÷",
97 | "KC_PAST" => "×",
98 | "KC_PMNS" => "-",
99 | "KC_PPLS" => "+",
100 | "KC_PDOT" => ".",
101 | "KC_PENT" => "Num\nEnter",
102 | "KC_LSFT" => "Left Shift",
103 | "KC_RSFT" => "Right Shift",
104 | "KC_LCTL" => "Left Ctrl",
105 | "KC_RCTL" => "Right Ctrl",
106 | "KC_LGUI" => "Left Win",
107 | "KC_RGUI" => "Right Win",
108 | "KC_LALT" => "Left Alt",
109 | "KC_RALT" => "Right Alt",
110 | "KC_SPC" => "Space",
111 | "KC_APP" => "Menu",
112 | "KC_LEFT" => "Left",
113 | "KC_DOWN" => "Down",
114 | "KC_UP" => "Up",
115 | "KC_RGHT" => "Right",
116 | "BR_DEC" => "Bright -",
117 | "BR_INC" => "Bright +",
118 | "EF_DEC" => "Effect -",
119 | "EF_INC" => "Effect +",
120 | "ES_DEC" => "Effect Speed -",
121 | "ES_INC" => "Effect Speed +",
122 | "H1_DEC" => "Color1 Hue -",
123 | "H1_INC" => "Color1 Hue +",
124 | "H2_DEC" => "Color2 Hue -",
125 | "H2_INC" => "Color2 Hue +",
126 | "S1_DEC" => "Color1 Sat -",
127 | "S1_INC" => "Color1 Sat +",
128 | "S2_DEC" => "Color2 Sat -",
129 | "S2_INC" => "Color2 Sat +",
130 | "KC_VOLD" => "Vol -",
131 | "KC_VOLU" => "Vol +",
132 | "KC_MUTE" => "Mute",
133 | "KC_MPLY" => "Play",
134 | "KC_MSTP" => "Media Stop",
135 | "KC_MPRV" => "Previous",
136 | "KC_MNXT" => "Next",
137 | "KC_MRWD" => "Rewind",
138 | "KC_MFFD" => "Fast Forward",
139 | "KC_MSEL" => "Select",
140 | "KC_EJCT" => "Eject",
141 | "MACRO(0)" => "M0",
142 | "MACRO(1)" => "M1",
143 | "MACRO(2)" => "M2",
144 | "MACRO(3)" => "M3",
145 | "MACRO(4)" => "M4",
146 | "MACRO(5)" => "M5",
147 | "MACRO(6)" => "M6",
148 | "MACRO(7)" => "M7",
149 | "MACRO(8)" => "M8",
150 | "MACRO(9)" => "M9",
151 | "MACRO(10)" => "M10",
152 | "MACRO(11)" => "M11",
153 | "MACRO(12)" => "M12",
154 | "MACRO(13)" => "M13",
155 | "MACRO(14)" => "M14",
156 | "MACRO(15)" => "M15",
157 | "S(KC_GRV)" => "~",
158 | "S(KC_1)" => "!",
159 | "S(KC_2)" => "@",
160 | "S(KC_3)" => "#",
161 | "S(KC_4)" => "$",
162 | "S(KC_5)" => "%",
163 | "S(KC_6)" => "^",
164 | "S(KC_7)" => "&",
165 | "S(KC_8)" => "*",
166 | "S(KC_9)" => "(",
167 | "S(KC_0)" => ")",
168 | "S(KC_MINS)" => "_",
169 | "S(KC_EQL)" => "+",
170 | "S(KC_LBRC)" => "{",
171 | "S(KC_RBRC)" => "}",
172 | "S(KC_BSLS)" => "|",
173 | "S(KC_SCLN)" => ":",
174 | "S(KC_QUOT)" => "\"",
175 | "S(KC_COMM)" => "<",
176 | "S(KC_DOT)" => ">",
177 | "S(KC_SLSH)" => "?",
178 | "KC_NUHS" => "NUHS",
179 | "KC_NUBS" => "NUBS",
180 | "KC_RO" => "Ro",
181 | "KC_JYEN" => "¥",
182 | "KC_MHEN" => "無変換",
183 | "KC_HANJ" => "漢字",
184 | "KC_HAEN" => "한영",
185 | "KC_HENK" => "変換",
186 | "KC_KANA" => "かな",
187 | "KC_GESC" => "Esc `",
188 | "KC_LSPO" => "LS (",
189 | "KC_RSPC" => "RS )",
190 | "KC_LCPO" => "LC (",
191 | "KC_RCPC" => "RC )",
192 | "KC_LAPO" => "LA (",
193 | "KC_RAPC" => "RA )",
194 | "KC_SFTENT" => "SftEnt",
195 | "RESET" => "Reset",
196 | "DEBUG" => "Debug",
197 | "MAGIC_TOGGLE_NKRO" => "Toggle NKRO",
198 | "KC_LNUM" => "Locking Num Lock",
199 | "KC_LCAP" => "Locking Caps Lock",
200 | "KC_LSCR" => "Locking Scroll Lock",
201 | "KC_PWR" => "Power",
202 | "KC_POWER" => "Power OSX",
203 | "KC_SLEP" => "Sleep",
204 | "KC_WAKE" => "Wake",
205 | "KC_CALC" => "Calc",
206 | "KC_MAIL" => "Mail",
207 | "KC_HELP" => "Help",
208 | "KC_STOP" => "Stop",
209 | "KC_ERAS" => "Alt Erase",
210 | "KC_AGAIN" => "Again",
211 | "KC_MENU" => "Menu",
212 | "KC_UNDO" => "Undo",
213 | "KC_SELECT" => "Select",
214 | "KC_EXECUTE" => "Exec",
215 | "KC_CUT" => "Cut",
216 | "KC_COPY" => "Copy",
217 | "KC_PASTE" => "Paste",
218 | "KC_FIND" => "Find",
219 | "KC_MYCM" => "My Comp",
220 | "KC_WWW_HOME" => "Home",
221 | "KC_WWW_BACK" => "Back",
222 | "KC_WWW_FORWARD" => "Forward",
223 | "KC_WWW_STOP" => "Stop",
224 | "KC_WWW_REFRESH" => "Refresh",
225 | "KC_WWW_FAVORITES" => "Favorites",
226 | "KC_WWW_SEARCH" => "Search",
227 | "KC_BRIU" => "Screen +",
228 | "KC_BRID" => "Screen -",
229 | "KC_F13" => "F13",
230 | "KC_F14" => "F14",
231 | "KC_F15" => "F15",
232 | "KC_F16" => "F16",
233 | "KC_F17" => "F17",
234 | "KC_F18" => "F18",
235 | "KC_F19" => "F19",
236 | "KC_F20" => "F20",
237 | "KC_F21" => "F21",
238 | "KC_F22" => "F22",
239 | "KC_F23" => "F23",
240 | "KC_F24" => "F24",
241 | "KC_MS_UP" => "Mouse ↑",
242 | "KC_MS_DOWN" => "Mouse ↓",
243 | "KC_MS_LEFT" => "Mouse ←",
244 | "KC_MS_RIGHT" => "Mouse →",
245 | "KC_MS_BTN1" => "Mouse Btn1",
246 | "KC_MS_BTN2" => "Mouse Btn2",
247 | "KC_MS_BTN3" => "Mouse Btn3",
248 | "KC_MS_BTN4" => "Mouse Btn4",
249 | "KC_MS_BTN5" => "Mouse Btn5",
250 | "KC_MS_BTN6" => "Mouse Btn6",
251 | "KC_MS_BTN7" => "Mouse Btn7",
252 | "KC_MS_BTN8" => "Mouse Btn8",
253 | "KC_MS_WH_UP" => "Mouse Wh ↑",
254 | "KC_MS_WH_DOWN" => "Mouse Wh ↓",
255 | "KC_MS_WH_LEFT" => "Mouse Wh ←",
256 | "KC_MS_WH_RIGHT" => "Mouse Wh →",
257 | "KC_MS_ACCEL0" => "Mouse Acc0",
258 | "KC_MS_ACCEL1" => "Mouse Acc1",
259 | "KC_MS_ACCEL2" => "Mouse Acc2",
260 | "AU_ON" => "Audio On",
261 | "AU_OFF" => "Audio Off",
262 | "AU_TOG" => "Audio Toggle",
263 | "CLICKY_TOGGLE" => "Clicky Toggle",
264 | "CLICKY_ENABLE" => "Clicky Enable",
265 | "CLICKY_DISABLE" => "Clicky Disable",
266 | "CLICKY_UP" => "Clicky Up",
267 | "CLICKY_DOWN" => "Clicky Down",
268 | "CLICKY_RESET" => "Clicky Reset",
269 | "MU_ON" => "Music On",
270 | "MU_OFF" => "Music Off",
271 | "MU_TOG" => "Music Toggle",
272 | "MU_MOD" => "Music Mode",
273 | "BL_TOGG" => "BL Toggle",
274 | "BL_ON" => "BL On",
275 | "BL_OFF" => "BL Off",
276 | "BL_DEC" => "BL -",
277 | "BL_INC" => "BL +",
278 | "BL_STEP" => "BL Cycle",
279 | "BL_BRTG" => "BR Toggle",
280 | "RGB_TOG" => "RGB Toggle",
281 | "RGB_RMOD" => "RGB Mode -",
282 | "RGB_MOD" => "RGB Mode +",
283 | "RGB_HUD" => "Hue -",
284 | "RGB_HUI" => "Hue +",
285 | "RGB_SAD" => "Sat -",
286 | "RGB_SAI" => "Sat +",
287 | "RGB_VAD" => "Bright -",
288 | "RGB_VAI" => "Bright +",
289 | "RGB_SPD" => "Effect Speed-",
290 | "RGB_SPI" => "Effect Speed+",
291 | "RGB_M_P" => "RGB Mode P",
292 | "RGB_M_B" => "RGB Mode B",
293 | "RGB_M_R" => "RGB Mode R",
294 | "RGB_M_SW" => "RGB Mode SW",
295 | "RGB_M_SN" => "RGB Mode SN",
296 | "RGB_M_K" => "RGB Mode K",
297 | "RGB_M_X" => "RGB Mode X",
298 | "RGB_M_G" => "RGB Mode G",
299 | "CUSTOM(0)" => "CUSTOM(0)",
300 | "CUSTOM(1)" => "CUSTOM(1)",
301 | "CUSTOM(2)" => "CUSTOM(2)",
302 | "CUSTOM(3)" => "CUSTOM(3)",
303 | "CUSTOM(4)" => "CUSTOM(4)",
304 | "CUSTOM(5)" => "CUSTOM(5)",
305 | "CUSTOM(6)" => "CUSTOM(6)",
306 | "CUSTOM(7)" => "CUSTOM(7)",
307 | "CUSTOM(8)" => "CUSTOM(8)",
308 | "CUSTOM(9)" => "CUSTOM(9)",
309 | "CUSTOM(10)" => "CUSTOM(10)",
310 | "CUSTOM(11)" => "CUSTOM(11)",
311 | "CUSTOM(12)" => "CUSTOM(12)",
312 | "CUSTOM(13)" => "CUSTOM(13)",
313 | "CUSTOM(14)" => "CUSTOM(14)",
314 | "CUSTOM(15)" => "CUSTOM(15)",
315 | ];
316 | }
317 |
--------------------------------------------------------------------------------
/src/via/ViaKeyNamesGen.hx:
--------------------------------------------------------------------------------
1 | package via;
2 | import haxe.Json;
3 | import sys.io.File;
4 | import tools.ERegTools;
5 | import tools.JsonParserWithComments;
6 |
7 | /**
8 | * How to run (from `src/`):
9 | * haxe --neko via/ViaKeyNamesGen.n -main via.ViaKeyNamesGen
10 | * neko via/ViaKeyNamesGen.n
11 | * @author YellowAfterlife
12 | */
13 | class ViaKeyNamesGen {
14 | public static function main() {
15 | var ts = File.getContent("via/key.ts");
16 | var b = new StringBuf();
17 | for (line in [
18 | "package via;",
19 | "/** ",
20 | " * Generated by ViaKeyNamesGen.hx",
21 | " * from the-via/app: src/utils/key.ts",
22 | " */",
23 | "class ViaKeyNames {",
24 | " public static var map:Map = ["
25 | ]) b.add(line + "\r\n");
26 |
27 | // You know what time it is? That's right,
28 | // it's time to parse TypeScript with a modified JSON parser.
29 | var rxGetKeyCodes = new EReg("\nexport function getKeycodes"
30 | + ".+?" + "\\{\r?\n[ \t]*" + "return\\s*"
31 | + "(\\[[\\s\\S]+?\\]);\r?\n"
32 | + "\\}", "");
33 | rxGetKeyCodes.match(ts);
34 | var expr = rxGetKeyCodes.matched(1);
35 | expr = StringTools.replace(expr, "buildLayerMenu(),", "");
36 | var menus:Array = JsonParserWithComments.parse(expr);
37 | for (menu in menus) {
38 | for (key in menu.keycodes) {
39 | b.add("\t\t" + Json.stringify(key.code) + " => " + Json.stringify(key.name) + ",\r\n");
40 | }
41 | }
42 | b.add("\t];\r\n");
43 | b.add("}\r\n");
44 | File.saveContent("via/ViaKeyNames.hx", b.toString());
45 | Sys.println("OK!");
46 | }
47 | }
48 | typedef IKeycodeMenu = {
49 | id:String,
50 | label:String,
51 | keycodes:Array,
52 | };
53 | typedef IKeycodeItem = {
54 | name:String,
55 | code:String,
56 | ?keys:String,
57 | ?title:String,
58 | };
--------------------------------------------------------------------------------
/src/via/key.ts:
--------------------------------------------------------------------------------
1 | import {
2 | advancedKeycodeToString,
3 | advancedStringToKeycode,
4 | } from './advanced-keys';
5 | import {
6 | BuiltInKeycodeModule,
7 | VIADefinitionV3,
8 | VIADefinitionV2,
9 | getLightingDefinition,
10 | KeycodeType,
11 | } from '@the-via/reader';
12 |
13 | export interface IKeycode {
14 | name: string;
15 | code: string;
16 | title?: string;
17 | shortName?: string;
18 | keys?: string;
19 | width?: number;
20 | type?: 'container' | 'text' | 'layer';
21 | layer?: number;
22 | }
23 |
24 | export interface IKeycodeMenu {
25 | id: string;
26 | label: string;
27 | keycodes: IKeycode[];
28 | width?: 'label';
29 | detailed?: string;
30 | }
31 |
32 | // Tests if label is an alpha
33 | export function isAlpha(label: string) {
34 | return /[A-Za-z]/.test(label) && label.length === 1;
35 | }
36 |
37 | // Test if label is a numpad number
38 | export function isNumpadNumber(label: string) {
39 | return /['0-9]/.test(label) && label.length === 1;
40 | }
41 |
42 | export function isArrowKey(label: string) {
43 | return /[🠗🠕🠖🠔←↑→↓]$/.test(label);
44 | }
45 |
46 | export function isNumpadSymbol(label: string) {
47 | const centeredSymbol = '-+.÷×'.split('');
48 | return label.length === 1 && centeredSymbol.includes(label[0]);
49 | }
50 |
51 | // Test if label is a multi-legend, e.g. "!\n1"
52 | export function isMultiLegend(label: string) {
53 | const topLegend = '~!@#$%^&*()_+|{}:"<>?'.split('');
54 | return label.length !== 1 && topLegend.includes(label[0]);
55 | }
56 |
57 | // Tests if label is a number
58 | export function isNumericOrShiftedSymbol(label: string) {
59 | const numbersTop = '!@#$%^&*()_+|~{}:"<>?1234567890'.split('');
60 | return label.length === 1 && numbersTop.includes(label[0]);
61 | }
62 |
63 | // Tests if label is a number
64 | export function isNumericSymbol(label: string) {
65 | const numbersTop = '!@#$%^&*()_+|~{}:"<>?'.split('');
66 | return label.length !== 1 && numbersTop.includes(label[0]);
67 | }
68 |
69 | // Maps the byte value to the keycode
70 | export function getByteForCode(
71 | code: string,
72 | basicKeyToByte: Record,
73 | ) {
74 | const byte: number | undefined = basicKeyToByte[code];
75 | if (byte !== undefined) {
76 | return byte;
77 | } else if (isLayerCode(code)) {
78 | return getByteForLayerCode(code, basicKeyToByte);
79 | } else if (advancedStringToKeycode(code, basicKeyToByte) !== null) {
80 | return advancedStringToKeycode(code, basicKeyToByte);
81 | }
82 | throw `Could not find byte for ${code}`;
83 | }
84 |
85 | function isLayerCode(code: string) {
86 | return /([A-Za-z]+)\((\d+)\)/.test(code);
87 | }
88 |
89 | function getByteForLayerCode(
90 | keycode: string,
91 | basicKeyToByte: Record,
92 | ): number {
93 | const keycodeMatch = keycode.match(/([A-Za-z]+)\((\d+)\)/);
94 | if (keycodeMatch) {
95 | const [, code, layer] = keycodeMatch;
96 | const numLayer = parseInt(layer);
97 | switch (code) {
98 | case 'TO': {
99 | return Math.min(
100 | basicKeyToByte._QK_TO + numLayer,
101 | basicKeyToByte._QK_TO_MAX,
102 | );
103 | }
104 | case 'MO': {
105 | return Math.min(
106 | basicKeyToByte._QK_MOMENTARY + numLayer,
107 | basicKeyToByte._QK_MOMENTARY_MAX,
108 | );
109 | }
110 | case 'DF': {
111 | return Math.min(
112 | basicKeyToByte._QK_DEF_LAYER + numLayer,
113 | basicKeyToByte._QK_DEF_LAYER_MAX,
114 | );
115 | }
116 | case 'TG': {
117 | return Math.min(
118 | basicKeyToByte._QK_TOGGLE_LAYER + numLayer,
119 | basicKeyToByte._QK_TOGGLE_LAYER_MAX,
120 | );
121 | }
122 | case 'OSL': {
123 | return Math.min(
124 | basicKeyToByte._QK_ONE_SHOT_LAYER + numLayer,
125 | basicKeyToByte._QK_ONE_SHOT_LAYER_MAX,
126 | );
127 | }
128 | case 'TT': {
129 | return Math.min(
130 | basicKeyToByte._QK_LAYER_TAP_TOGGLE + numLayer,
131 | basicKeyToByte._QK_LAYER_TAP_TOGGLE_MAX,
132 | );
133 | }
134 | case 'CUSTOM': {
135 | return Math.min(
136 | basicKeyToByte._QK_KB + numLayer,
137 | basicKeyToByte._QK_KB_MAX,
138 | );
139 | }
140 | case 'MACRO': {
141 | return Math.min(
142 | basicKeyToByte._QK_MACRO + numLayer,
143 | basicKeyToByte._QK_MACRO_MAX,
144 | );
145 | }
146 | default: {
147 | throw new Error('Incorrect code');
148 | }
149 | }
150 | }
151 | throw new Error('No match found');
152 | }
153 |
154 | function getCodeForLayerByte(
155 | byte: number,
156 | basicKeyToByte: Record,
157 | ) {
158 | if (basicKeyToByte._QK_TO <= byte && basicKeyToByte._QK_TO_MAX >= byte) {
159 | const layer = byte - basicKeyToByte._QK_TO;
160 | return `TO(${layer})`;
161 | } else if (
162 | basicKeyToByte._QK_MOMENTARY <= byte &&
163 | basicKeyToByte._QK_MOMENTARY_MAX >= byte
164 | ) {
165 | const layer = byte - basicKeyToByte._QK_MOMENTARY;
166 | return `MO(${layer})`;
167 | } else if (
168 | basicKeyToByte._QK_DEF_LAYER <= byte &&
169 | basicKeyToByte._QK_DEF_LAYER_MAX >= byte
170 | ) {
171 | const layer = byte - basicKeyToByte._QK_DEF_LAYER;
172 | return `DF(${layer})`;
173 | } else if (
174 | basicKeyToByte._QK_TOGGLE_LAYER <= byte &&
175 | basicKeyToByte._QK_TOGGLE_LAYER_MAX >= byte
176 | ) {
177 | const layer = byte - basicKeyToByte._QK_TOGGLE_LAYER;
178 | return `TG(${layer})`;
179 | } else if (
180 | basicKeyToByte._QK_ONE_SHOT_LAYER <= byte &&
181 | basicKeyToByte._QK_ONE_SHOT_LAYER_MAX >= byte
182 | ) {
183 | const layer = byte - basicKeyToByte._QK_ONE_SHOT_LAYER;
184 | return `OSL(${layer})`;
185 | } else if (
186 | basicKeyToByte._QK_LAYER_TAP_TOGGLE <= byte &&
187 | basicKeyToByte._QK_LAYER_TAP_TOGGLE_MAX >= byte
188 | ) {
189 | const layer = byte - basicKeyToByte._QK_LAYER_TAP_TOGGLE;
190 | return `TT(${layer})`;
191 | } else if (
192 | basicKeyToByte._QK_KB <= byte &&
193 | basicKeyToByte._QK_KB_MAX >= byte
194 | ) {
195 | const n = byte - basicKeyToByte._QK_KB;
196 | return `CUSTOM(${n})`;
197 | } else if (
198 | basicKeyToByte._QK_MACRO <= byte &&
199 | basicKeyToByte._QK_MACRO_MAX >= byte
200 | ) {
201 | const n = byte - basicKeyToByte._QK_MACRO;
202 | return `MACRO(${n})`;
203 | }
204 | }
205 |
206 | export const keycodesList = getKeycodes().reduce(
207 | (p, n) => p.concat(n.keycodes),
208 | [],
209 | );
210 |
211 | export const getByteToKey = (basicKeyToByte: Record) =>
212 | Object.keys(basicKeyToByte).reduce((p, n) => {
213 | const key = basicKeyToByte[n];
214 | if (key in p) {
215 | const basicKeycode = keycodesList.find(({code}) => code === n);
216 | if (basicKeycode) {
217 | return {...p, [key]: basicKeycode.code};
218 | }
219 | return p;
220 | }
221 | return {...p, [key]: n};
222 | }, {} as {[key: number]: string});
223 |
224 | function isLayerKey(byte: number, basicKeyToByte: Record) {
225 | return [
226 | [basicKeyToByte._QK_TO, basicKeyToByte._QK_TO_MAX],
227 | [basicKeyToByte._QK_MOMENTARY, basicKeyToByte._QK_MOMENTARY_MAX],
228 | [basicKeyToByte._QK_DEF_LAYER, basicKeyToByte._QK_DEF_LAYER_MAX],
229 | [basicKeyToByte._QK_TOGGLE_LAYER, basicKeyToByte._QK_TOGGLE_LAYER_MAX],
230 | [basicKeyToByte._QK_ONE_SHOT_LAYER, basicKeyToByte._QK_ONE_SHOT_LAYER_MAX],
231 | [
232 | basicKeyToByte._QK_LAYER_TAP_TOGGLE,
233 | basicKeyToByte._QK_LAYER_TAP_TOGGLE_MAX,
234 | ],
235 | [basicKeyToByte._QK_KB, basicKeyToByte._QK_KB_MAX],
236 | [basicKeyToByte._QK_MACRO, basicKeyToByte._QK_MACRO_MAX],
237 | ].some((code) => byte >= code[0] && byte <= code[1]);
238 | }
239 |
240 | export function getCodeForByte(
241 | byte: number,
242 | basicKeyToByte: Record,
243 | byteToKey: Record,
244 | ) {
245 | const keycode = byteToKey[byte];
246 | if (keycode && !keycode.startsWith('_QK')) {
247 | return keycode;
248 | } else if (isLayerKey(byte, basicKeyToByte)) {
249 | return getCodeForLayerByte(byte, basicKeyToByte);
250 | } else if (
251 | advancedKeycodeToString(byte, basicKeyToByte, byteToKey) !== null
252 | ) {
253 | return advancedKeycodeToString(byte, basicKeyToByte, byteToKey);
254 | } else {
255 | return '0x' + Number(byte).toString(16);
256 | }
257 | }
258 |
259 | export function keycodeInMaster(
260 | keycode: string,
261 | basicKeyToByte: Record,
262 | ) {
263 | return (
264 | keycode in basicKeyToByte ||
265 | isLayerCode(keycode) ||
266 | advancedStringToKeycode(keycode, basicKeyToByte) !== null
267 | );
268 | }
269 |
270 | function shorten(str: string) {
271 | return str
272 | .split(' ')
273 | .map((word) => word.slice(0, 1) + word.slice(1).replace(/[aeiou ]/gi, ''))
274 | .join('');
275 | }
276 |
277 | export function isCustomKeycodeByte(
278 | byte: number,
279 | basicKeyToByte: Record,
280 | ) {
281 | return byte >= basicKeyToByte._QK_KB && byte <= basicKeyToByte._QK_KB_MAX;
282 | }
283 |
284 | export function getCustomKeycodeIndex(
285 | byte: number,
286 | basicKeyToByte: Record,
287 | ) {
288 | return byte - basicKeyToByte._QK_KB;
289 | }
290 |
291 | export function isMacroKeycodeByte(
292 | byte: number,
293 | basicKeyToByte: Record,
294 | ) {
295 | return (
296 | byte >= basicKeyToByte._QK_MACRO && byte <= basicKeyToByte._QK_MACRO_MAX
297 | );
298 | }
299 |
300 | export function getMacroKeycodeIndex(
301 | byte: number,
302 | basicKeyToByte: Record,
303 | ) {
304 | return byte - basicKeyToByte._QK_MACRO;
305 | }
306 |
307 | export function getLabelForByte(
308 | byte: number,
309 | size = 100,
310 | basicKeyToByte: Record,
311 | byteToKey: Record,
312 | ) {
313 | const keycode = getCodeForByte(byte, basicKeyToByte, byteToKey);
314 | const basicKeycode = keycodesList.find(({code}) => code === keycode);
315 | if (!basicKeycode) {
316 | return keycode;
317 | }
318 | return getShortNameForKeycode(basicKeycode, size);
319 | }
320 |
321 | export function getShortNameForKeycode(keycode: IKeycode, size = 100) {
322 | const {code, name, shortName} = keycode;
323 | if (size <= 150 && shortName) {
324 | return shortName;
325 | }
326 | if (size === 100 && name.length > 5) {
327 | const shortenedName = shorten(name);
328 | if (!!code) {
329 | const shortCode = code.replace('KC_', '').replace('_', ' ');
330 | return shortenedName.length > 4 && shortCode.length < shortenedName.length
331 | ? shortCode
332 | : shortenedName;
333 | }
334 | return shortenedName;
335 | }
336 | return name;
337 | }
338 |
339 | export function getOtherMenu(
340 | basicKeyToByte: Record,
341 | ): IKeycodeMenu {
342 | const keycodes = Object.keys(basicKeyToByte)
343 | .filter((key) => !key.startsWith('_QK'))
344 | .filter((key) => !keycodesList.map(({code}) => code).includes(key))
345 | .map((code) => ({
346 | name: code.replace('KC_', '').replace(/_/g, ' '),
347 | code: code,
348 | }));
349 |
350 | return {
351 | id: 'other',
352 | label: 'Other',
353 | keycodes,
354 | };
355 | }
356 |
357 | function buildLayerMenu(): IKeycodeMenu {
358 | const hardCodedKeycodes: IKeycode[] = [
359 | {
360 | name: 'Fn1\n(Fn3)',
361 | code: 'FN_MO13',
362 | title: 'Hold = Layer 1, Hold with Fn2 = Layer 3',
363 | shortName: 'Fn1(3)',
364 | },
365 | {
366 | name: 'Fn2\n(Fn3)',
367 | code: 'FN_MO23',
368 | title: 'Hold = Layer 2, Hold with Fn1 = Layer 3',
369 | shortName: 'Fn2(3)',
370 | },
371 | {
372 | name: 'Space Fn1',
373 | code: 'LT(1,KC_SPC)',
374 | title: 'Hold = Layer 1, Tap = Space',
375 | shortName: 'Spc Fn1',
376 | },
377 | {
378 | name: 'Space Fn2',
379 | code: 'LT(2,KC_SPC)',
380 | title: 'Hold = Layer 2, Tap = Space',
381 | shortName: 'Spc Fn2',
382 | },
383 | {
384 | name: 'Space Fn3',
385 | code: 'LT(3,KC_SPC)',
386 | title: 'Hold = Layer 3, Tap = Space',
387 | shortName: 'Spc Fn3',
388 | },
389 | ];
390 |
391 | const menu: IKeycodeMenu = {
392 | id: 'layers',
393 | label: 'Layers',
394 | width: 'label',
395 | keycodes: [
396 | {
397 | name: 'MO',
398 | code: 'MO(layer)',
399 | type: 'layer',
400 | layer: 0,
401 | title: 'Momentary turn layer on',
402 | },
403 | {
404 | name: 'TG',
405 | code: 'TG(layer)',
406 | type: 'layer',
407 | layer: 0,
408 | title: 'Toggle layer on/off',
409 | },
410 | {
411 | name: 'TT',
412 | code: 'TT(layer)',
413 | type: 'layer',
414 | layer: 0,
415 | title:
416 | "Normally acts like MO unless it's tapped multple times which toggles layer on",
417 | },
418 | {
419 | name: 'OSL',
420 | code: 'OSL(layer)',
421 | type: 'layer',
422 | layer: 0,
423 | title: 'Switch to layer for one keypress',
424 | },
425 | {
426 | name: 'TO',
427 | code: 'TO(layer)',
428 | type: 'layer',
429 | layer: 0,
430 | title: 'Turn on layer when pressed',
431 | },
432 | {
433 | name: 'DF',
434 | code: 'DF(layer)',
435 | type: 'layer',
436 | layer: 0,
437 | title: 'Sets the default layer',
438 | },
439 | ],
440 | };
441 |
442 | // Statically generate layer codes from 0-9 instead of making it an input
443 | return {
444 | ...menu,
445 | keycodes: [
446 | ...hardCodedKeycodes,
447 | ...menu.keycodes.flatMap((keycode) => {
448 | let res: IKeycode[] = [];
449 | for (let idx = 0; idx < 10; idx++) {
450 | const newTitle = (keycode.title || '').replace(
451 | 'layer',
452 | `layer ${idx}`,
453 | );
454 | const newCode = keycode.code.replace('layer', `${idx}`);
455 | const newName = keycode.name + `(${idx})`;
456 | res = [
457 | ...res,
458 | {...keycode, name: newName, title: newTitle, code: newCode},
459 | ];
460 | }
461 | return res;
462 | }),
463 | ],
464 | };
465 | }
466 |
467 | export function getKeycodes(): IKeycodeMenu[] {
468 | return [
469 | {
470 | id: 'basic',
471 | label: 'Basic',
472 | keycodes: [
473 | {name: '', code: 'KC_NO', title: 'Nothing'},
474 | {name: '▽', code: 'KC_TRNS', title: 'Pass-through'},
475 | // TODO: remove "shortName" when multiline keycap labels are working
476 | {name: 'Esc', code: 'KC_ESC', keys: 'esc'},
477 | {name: 'A', code: 'KC_A', keys: 'a'},
478 | {name: 'B', code: 'KC_B', keys: 'b'},
479 | {name: 'C', code: 'KC_C', keys: 'c'},
480 | {name: 'D', code: 'KC_D', keys: 'd'},
481 | {name: 'E', code: 'KC_E', keys: 'e'},
482 | {name: 'F', code: 'KC_F', keys: 'f'},
483 | {name: 'G', code: 'KC_G', keys: 'g'},
484 | {name: 'H', code: 'KC_H', keys: 'h'},
485 | {name: 'I', code: 'KC_I', keys: 'i'},
486 | {name: 'J', code: 'KC_J', keys: 'j'},
487 | {name: 'K', code: 'KC_K', keys: 'k'},
488 | {name: 'L', code: 'KC_L', keys: 'l'},
489 | {name: 'M', code: 'KC_M', keys: 'm'},
490 | {name: 'N', code: 'KC_N', keys: 'n'},
491 | {name: 'O', code: 'KC_O', keys: 'o'},
492 | {name: 'P', code: 'KC_P', keys: 'p'},
493 | {name: 'Q', code: 'KC_Q', keys: 'q'},
494 | {name: 'R', code: 'KC_R', keys: 'r'},
495 | {name: 'S', code: 'KC_S', keys: 's'},
496 | {name: 'T', code: 'KC_T', keys: 't'},
497 | {name: 'U', code: 'KC_U', keys: 'u'},
498 | {name: 'V', code: 'KC_V', keys: 'v'},
499 | {name: 'W', code: 'KC_W', keys: 'w'},
500 | {name: 'X', code: 'KC_X', keys: 'x'},
501 | {name: 'Y', code: 'KC_Y', keys: 'y'},
502 | {name: 'Z', code: 'KC_Z', keys: 'z'},
503 | {name: '!\n1', code: 'KC_1', keys: '1'},
504 | {name: '@\n2', code: 'KC_2', keys: '2'},
505 | {name: '#\n3', code: 'KC_3', keys: '3'},
506 | {name: '$\n4', code: 'KC_4', keys: '4'},
507 | {name: '%\n5', code: 'KC_5', keys: '5'},
508 | {name: '^\n6', code: 'KC_6', keys: '6'},
509 | {name: '&\n7', code: 'KC_7', keys: '7'},
510 | {name: '*\n8', code: 'KC_8', keys: '8'},
511 | {name: '(\n9', code: 'KC_9', keys: '9'},
512 | {name: ')\n0', code: 'KC_0', keys: '0'},
513 | {name: '_\n-', code: 'KC_MINS', keys: '-'},
514 | {name: '+\n=', code: 'KC_EQL', keys: '='},
515 | {name: '~\n`', code: 'KC_GRV', keys: '`'},
516 | {name: '{\n[', code: 'KC_LBRC', keys: '['},
517 | {name: '}\n]', code: 'KC_RBRC', keys: ']'},
518 | {name: '|\n\\', code: 'KC_BSLS', keys: '\\', width: 1500},
519 | {name: ':\n;', code: 'KC_SCLN', keys: ';'},
520 | {name: '"\n\'', code: 'KC_QUOT', keys: "'"},
521 | {name: '<\n,', code: 'KC_COMM', keys: ','},
522 | {name: '>\n.', code: 'KC_DOT', keys: '.'},
523 | {name: '?\n/', code: 'KC_SLSH', keys: '/'},
524 | {name: '=', code: 'KC_PEQL'},
525 | {name: ',', code: 'KC_PCMM'},
526 | {name: 'F1', code: 'KC_F1'},
527 | {name: 'F2', code: 'KC_F2'},
528 | {name: 'F3', code: 'KC_F3'},
529 | {name: 'F4', code: 'KC_F4'},
530 | {name: 'F5', code: 'KC_F5'},
531 | {name: 'F6', code: 'KC_F6'},
532 | {name: 'F7', code: 'KC_F7'},
533 | {name: 'F8', code: 'KC_F8'},
534 | {name: 'F9', code: 'KC_F9'},
535 | {name: 'F10', code: 'KC_F10'},
536 | {name: 'F11', code: 'KC_F11'},
537 | {name: 'F12', code: 'KC_F12'},
538 | {name: 'Print Screen', code: 'KC_PSCR', shortName: 'Print'},
539 | {name: 'Scroll Lock', code: 'KC_SLCK', shortName: 'Scroll'},
540 | {name: 'Pause', code: 'KC_PAUS'},
541 | {name: 'Tab', code: 'KC_TAB', keys: 'tab', width: 1500},
542 | {
543 | name: 'Backspace',
544 | code: 'KC_BSPC',
545 | keys: 'backspace',
546 | width: 2000,
547 | shortName: 'Bksp',
548 | },
549 | {name: 'Insert', code: 'KC_INS', keys: 'insert', shortName: 'Ins'},
550 | {name: 'Del', code: 'KC_DEL', keys: 'delete'},
551 | {name: 'Home', code: 'KC_HOME', keys: 'home'},
552 | {name: 'End', code: 'KC_END', keys: 'end'},
553 | {name: 'Page Up', code: 'KC_PGUP', keys: 'pageup', shortName: 'PgUp'},
554 | {
555 | name: 'Page Down',
556 | code: 'KC_PGDN',
557 | keys: 'pagedown',
558 | shortName: 'PgDn',
559 | },
560 | {name: 'Num\nLock', code: 'KC_NLCK', keys: 'num', shortName: 'N.Lck'},
561 | {name: 'Caps Lock', code: 'KC_CAPS', keys: 'caps_lock', width: 1750},
562 | {name: 'Enter', code: 'KC_ENT', keys: 'enter', width: 2250},
563 | {name: '1', code: 'KC_P1', keys: 'num_1', title: 'Numpad 1'},
564 | {name: '2', code: 'KC_P2', keys: 'num_2', title: 'Numpad 2'},
565 | {name: '3', code: 'KC_P3', keys: 'num_3', title: 'Numpad 3'},
566 | {name: '4', code: 'KC_P4', keys: 'num_4', title: 'Numpad 4'},
567 | {name: '5', code: 'KC_P5', keys: 'num_5', title: 'Numpad 5'},
568 | {name: '6', code: 'KC_P6', keys: 'num_6', title: 'Numpad 6'},
569 | {name: '7', code: 'KC_P7', keys: 'num_7', title: 'Numpad 7'},
570 | {name: '8', code: 'KC_P8', keys: 'num_8', title: 'Numpad 8'},
571 | {name: '9', code: 'KC_P9', keys: 'num_9', title: 'Numpad 9'},
572 | {
573 | name: '0',
574 | code: 'KC_P0',
575 | width: 2000,
576 | keys: 'num_0',
577 | title: 'Numpad 0',
578 | },
579 | {name: '÷', code: 'KC_PSLS', keys: 'num_divide', title: 'Numpad ÷'},
580 | {name: '×', code: 'KC_PAST', keys: 'num_multiply', title: 'Numpad ×'},
581 | {name: '-', code: 'KC_PMNS', keys: 'num_subtract', title: 'Numpad -'},
582 | {name: '+', code: 'KC_PPLS', keys: 'num_add', title: 'Numpad +'},
583 | {name: '.', code: 'KC_PDOT', keys: 'num_decimal', title: 'Numpad .'},
584 | {
585 | name: 'Num\nEnter',
586 | code: 'KC_PENT',
587 | shortName: 'N.Ent',
588 | title: 'Numpad Enter',
589 | },
590 | {
591 | name: 'Left Shift',
592 | code: 'KC_LSFT',
593 | keys: 'shift',
594 | width: 2250,
595 | shortName: 'LShft',
596 | },
597 | {name: 'Right Shift', code: 'KC_RSFT', width: 2750, shortName: 'RShft'},
598 | {name: 'Left Ctrl', code: 'KC_LCTL', keys: 'ctrl', width: 1250},
599 | {name: 'Right Ctrl', code: 'KC_RCTL', width: 1250, shortName: 'RCtl'},
600 | {
601 | name: 'Left Win',
602 | code: 'KC_LGUI',
603 | keys: 'cmd',
604 | width: 1250,
605 | shortName: 'LWin',
606 | },
607 | {name: 'Right Win', code: 'KC_RGUI', width: 1250, shortName: 'RWin'},
608 | {
609 | name: 'Left Alt',
610 | code: 'KC_LALT',
611 | keys: 'alt',
612 | width: 1250,
613 | shortName: 'LAlt',
614 | },
615 | {name: 'Right Alt', code: 'KC_RALT', width: 1250, shortName: 'RAlt'},
616 | {name: 'Space', code: 'KC_SPC', keys: 'space', width: 6250},
617 | {name: 'Menu', code: 'KC_APP', width: 1250, shortName: 'RApp'},
618 | {name: 'Left', code: 'KC_LEFT', keys: 'left', shortName: '←'},
619 | {name: 'Down', code: 'KC_DOWN', keys: 'down', shortName: '↓'},
620 | {name: 'Up', code: 'KC_UP', keys: 'up', shortName: '↑'},
621 | {name: 'Right', code: 'KC_RGHT', keys: 'right', shortName: '→'},
622 | ],
623 | },
624 | {
625 | id: 'wt_lighting',
626 | label: 'Lighting',
627 | width: 'label',
628 | keycodes: [
629 | {
630 | name: 'Bright -',
631 | code: 'BR_DEC',
632 | title: 'Brightness -',
633 | shortName: 'BR -',
634 | },
635 | {
636 | name: 'Bright +',
637 | code: 'BR_INC',
638 | title: 'Brightness +',
639 | shortName: 'BR +',
640 | },
641 | {
642 | name: 'Effect -',
643 | code: 'EF_DEC',
644 | title: 'Effect -',
645 | shortName: 'EF -',
646 | },
647 | {
648 | name: 'Effect +',
649 | code: 'EF_INC',
650 | title: 'Effect +',
651 | shortName: 'EF +',
652 | },
653 | {
654 | name: 'Effect Speed -',
655 | code: 'ES_DEC',
656 | title: 'Effect Speed -',
657 | shortName: 'ES -',
658 | },
659 | {
660 | name: 'Effect Speed +',
661 | code: 'ES_INC',
662 | title: 'Effect Speed +',
663 | shortName: 'ES +',
664 | },
665 | {
666 | name: 'Color1 Hue -',
667 | code: 'H1_DEC',
668 | title: 'Color1 Hue -',
669 | shortName: 'H1 -',
670 | },
671 | {
672 | name: 'Color1 Hue +',
673 | code: 'H1_INC',
674 | title: 'Color1 Hue +',
675 | shortName: 'H1 +',
676 | },
677 | {
678 | name: 'Color2 Hue -',
679 | code: 'H2_DEC',
680 | title: 'Color2 Hue -',
681 | shortName: 'H2 -',
682 | },
683 | {
684 | name: 'Color2 Hue +',
685 | code: 'H2_INC',
686 | title: 'Color2 Hue +',
687 | shortName: 'H2 +',
688 | },
689 | {
690 | name: 'Color1 Sat -',
691 | code: 'S1_DEC',
692 | title: 'Color1 Sat -',
693 | shortName: 'S1 -',
694 | },
695 | {
696 | name: 'Color1 Sat +',
697 | code: 'S1_INC',
698 | title: 'Color1 Sat +',
699 | shortName: 'S1 +',
700 | },
701 | {
702 | name: 'Color2 Sat -',
703 | code: 'S2_DEC',
704 | title: 'Color2 Sat -',
705 | shortName: 'S2 -',
706 | },
707 | {
708 | name: 'Color2 Sat +',
709 | code: 'S2_INC',
710 | title: 'Color2 Sat +',
711 | shortName: 'S2 +',
712 | },
713 | ],
714 | },
715 | {
716 | id: 'media',
717 | label: 'Media',
718 | width: 'label',
719 | keycodes: [
720 | {name: 'Vol -', code: 'KC_VOLD', title: 'Volume Down'},
721 | {name: 'Vol +', code: 'KC_VOLU', title: 'Volume Up'},
722 | {name: 'Mute', code: 'KC_MUTE', title: 'Mute Audio'},
723 | {name: 'Play', code: 'KC_MPLY', title: 'Play/Pause'},
724 | {name: 'Media Stop', code: 'KC_MSTP', title: 'Media Stop'},
725 | {name: 'Previous', code: 'KC_MPRV', title: 'Media Previous'},
726 | {name: 'Next', code: 'KC_MNXT', title: 'Media Next'},
727 | {name: 'Rewind', code: 'KC_MRWD', title: 'Rewind'},
728 | {name: 'Fast Forward', code: 'KC_MFFD', title: 'Fast Forward'},
729 | {name: 'Select', code: 'KC_MSEL', title: 'Media Select'},
730 | {name: 'Eject', code: 'KC_EJCT', title: 'Media Eject'},
731 | ],
732 | },
733 | {
734 | id: 'macro',
735 | label: 'Macro',
736 | width: 'label',
737 | keycodes: [
738 | {name: 'M0', code: 'MACRO(0)', title: 'Macro 0'},
739 | {name: 'M1', code: 'MACRO(1)', title: 'Macro 1'},
740 | {name: 'M2', code: 'MACRO(2)', title: 'Macro 2'},
741 | {name: 'M3', code: 'MACRO(3)', title: 'Macro 3'},
742 | {name: 'M4', code: 'MACRO(4)', title: 'Macro 4'},
743 | {name: 'M5', code: 'MACRO(5)', title: 'Macro 5'},
744 | {name: 'M6', code: 'MACRO(6)', title: 'Macro 6'},
745 | {name: 'M7', code: 'MACRO(7)', title: 'Macro 7'},
746 | {name: 'M8', code: 'MACRO(8)', title: 'Macro 8'},
747 | {name: 'M9', code: 'MACRO(9)', title: 'Macro 9'},
748 | {name: 'M10', code: 'MACRO(10)', title: 'Macro 10'},
749 | {name: 'M11', code: 'MACRO(11)', title: 'Macro 11'},
750 | {name: 'M12', code: 'MACRO(12)', title: 'Macro 12'},
751 | {name: 'M13', code: 'MACRO(13)', title: 'Macro 13'},
752 | {name: 'M14', code: 'MACRO(14)', title: 'Macro 14'},
753 | {name: 'M15', code: 'MACRO(15)', title: 'Macro 15'},
754 | ],
755 | },
756 | buildLayerMenu(),
757 | {
758 | id: 'special',
759 | label: 'Special',
760 | width: 'label',
761 | keycodes: [
762 | {name: '~', code: 'S(KC_GRV)', keys: '`', title: 'Shift + `'},
763 | {name: '!', code: 'S(KC_1)', keys: '!', title: 'Shift + 1'},
764 | {name: '@', code: 'S(KC_2)', keys: '@', title: 'Shift + 2'},
765 | {name: '#', code: 'S(KC_3)', keys: '#', title: 'Shift + 3'},
766 | {name: '$', code: 'S(KC_4)', keys: '$', title: 'Shift + 4'},
767 | {name: '%', code: 'S(KC_5)', keys: '%', title: 'Shift + 5'},
768 | {name: '^', code: 'S(KC_6)', keys: '^', title: 'Shift + 6'},
769 | {name: '&', code: 'S(KC_7)', keys: '&', title: 'Shift + 7'},
770 | {name: '*', code: 'S(KC_8)', keys: '*', title: 'Shift + 8'},
771 | {name: '(', code: 'S(KC_9)', keys: '(', title: 'Shift + 9'},
772 | {name: ')', code: 'S(KC_0)', keys: ')', title: 'Shift + 0'},
773 | {name: '_', code: 'S(KC_MINS)', keys: '_', title: 'Shift + -'},
774 | {name: '+', code: 'S(KC_EQL)', keys: '+', title: 'Shift + ='},
775 | {name: '{', code: 'S(KC_LBRC)', keys: '{', title: 'Shift + ['},
776 | {name: '}', code: 'S(KC_RBRC)', keys: '}', title: 'Shift + ]'},
777 | {name: '|', code: 'S(KC_BSLS)', keys: '|', title: 'Shift + \\'},
778 | {name: ':', code: 'S(KC_SCLN)', keys: ':', title: 'Shift + /'},
779 | {name: '"', code: 'S(KC_QUOT)', keys: '"', title: "Shift + '"},
780 | {name: '<', code: 'S(KC_COMM)', keys: '<', title: 'Shift + ,'},
781 | {name: '>', code: 'S(KC_DOT)', keys: '>', title: 'Shift + .'},
782 | {name: '?', code: 'S(KC_SLSH)', keys: '?', title: 'Shift + /'},
783 | {name: 'NUHS', code: 'KC_NUHS', title: 'Non-US # and ~'},
784 | {name: 'NUBS', code: 'KC_NUBS', title: 'Non-US \\ and |'},
785 | {name: 'Ro', code: 'KC_RO', title: 'JIS \\ and |'},
786 | {name: '¥', code: 'KC_JYEN', title: 'JPN Yen'},
787 | {name: '無変換', code: 'KC_MHEN', title: 'JIS Muhenkan'},
788 | {name: '漢字', code: 'KC_HANJ', title: 'Hanja'},
789 | {name: '한영', code: 'KC_HAEN', title: 'HanYeong'},
790 | {name: '変換', code: 'KC_HENK', title: 'JIS Henkan'},
791 | {name: 'かな', code: 'KC_KANA', title: 'JIS Katakana/Hiragana'},
792 | {
793 | name: 'Esc `',
794 | code: 'KC_GESC',
795 | title: 'Esc normally, but ` when Shift or Win is pressed',
796 | },
797 | {
798 | name: 'LS (',
799 | code: 'KC_LSPO',
800 | title: 'Left Shift when held, ( when tapped',
801 | },
802 | {
803 | name: 'RS )',
804 | code: 'KC_RSPC',
805 | title: 'Right Shift when held, ) when tapped',
806 | },
807 | {
808 | name: 'LC (',
809 | code: 'KC_LCPO',
810 | title: 'Left Control when held, ( when tapped',
811 | },
812 | {
813 | name: 'RC )',
814 | code: 'KC_RCPC',
815 | title: 'Right Control when held, ) when tapped',
816 | },
817 | {
818 | name: 'LA (',
819 | code: 'KC_LAPO',
820 | title: 'Left Alt when held, ( when tapped',
821 | },
822 | {
823 | name: 'RA )',
824 | code: 'KC_RAPC',
825 | title: 'Right Alt when held, ) when tapped',
826 | },
827 | {
828 | name: 'SftEnt',
829 | code: 'KC_SFTENT',
830 | title: 'Right Shift when held, Enter when tapped',
831 | },
832 | {name: 'Reset', code: 'RESET', title: 'Reset the keyboard'},
833 | {name: 'Debug', code: 'DEBUG', title: 'Toggle debug mode'},
834 | {
835 | name: 'Toggle NKRO',
836 | code: 'MAGIC_TOGGLE_NKRO',
837 | shortName: 'NKRO',
838 | title: 'Toggle NKRO',
839 | },
840 | // I don't even think the locking stuff is enabled...
841 | {name: 'Locking Num Lock', code: 'KC_LNUM'},
842 | {name: 'Locking Caps Lock', code: 'KC_LCAP'},
843 | {name: 'Locking Scroll Lock', code: 'KC_LSCR'},
844 | {name: 'Power', code: 'KC_PWR'},
845 | {name: 'Power OSX', code: 'KC_POWER'},
846 | {name: 'Sleep', code: 'KC_SLEP'},
847 | {name: 'Wake', code: 'KC_WAKE'},
848 | {name: 'Calc', code: 'KC_CALC'},
849 | {name: 'Mail', code: 'KC_MAIL'},
850 | {name: 'Help', code: 'KC_HELP'},
851 | {name: 'Stop', code: 'KC_STOP'},
852 | {name: 'Alt Erase', code: 'KC_ERAS'},
853 | {name: 'Again', code: 'KC_AGAIN'},
854 | {name: 'Menu', code: 'KC_MENU'},
855 | {name: 'Undo', code: 'KC_UNDO'},
856 | {name: 'Select', code: 'KC_SELECT'},
857 | {name: 'Exec', code: 'KC_EXECUTE'},
858 | {name: 'Cut', code: 'KC_CUT'},
859 | {name: 'Copy', code: 'KC_COPY'},
860 | {name: 'Paste', code: 'KC_PASTE'},
861 | {name: 'Find', code: 'KC_FIND'},
862 | {name: 'My Comp', code: 'KC_MYCM'},
863 | {name: 'Home', code: 'KC_WWW_HOME'},
864 | {name: 'Back', code: 'KC_WWW_BACK'},
865 | {name: 'Forward', code: 'KC_WWW_FORWARD'},
866 | {name: 'Stop', code: 'KC_WWW_STOP'},
867 | {name: 'Refresh', code: 'KC_WWW_REFRESH'},
868 | {name: 'Favorites', code: 'KC_WWW_FAVORITES'},
869 | {name: 'Search', code: 'KC_WWW_SEARCH'},
870 | {
871 | name: 'Screen +',
872 | code: 'KC_BRIU',
873 | shortName: 'Scr +',
874 | title: 'Screen Brightness Up',
875 | },
876 | {
877 | name: 'Screen -',
878 | code: 'KC_BRID',
879 | shortName: 'Scr -',
880 | title: 'Screen Brightness Down',
881 | },
882 | {name: 'F13', code: 'KC_F13'},
883 | {name: 'F14', code: 'KC_F14'},
884 | {name: 'F15', code: 'KC_F15'},
885 | {name: 'F16', code: 'KC_F16'},
886 | {name: 'F17', code: 'KC_F17'},
887 | {name: 'F18', code: 'KC_F18'},
888 | {name: 'F19', code: 'KC_F19'},
889 | {name: 'F20', code: 'KC_F20'},
890 | {name: 'F21', code: 'KC_F21'},
891 | {name: 'F22', code: 'KC_F22'},
892 | {name: 'F23', code: 'KC_F23'},
893 | {name: 'F24', code: 'KC_F24'},
894 |
895 | // TODO: move these to a new group
896 | {name: 'Mouse ↑', code: 'KC_MS_UP'},
897 | {name: 'Mouse ↓', code: 'KC_MS_DOWN'},
898 | {name: 'Mouse ←', code: 'KC_MS_LEFT'},
899 | {name: 'Mouse →', code: 'KC_MS_RIGHT'},
900 | {name: 'Mouse Btn1', code: 'KC_MS_BTN1'},
901 | {name: 'Mouse Btn2', code: 'KC_MS_BTN2'},
902 | {name: 'Mouse Btn3', code: 'KC_MS_BTN3'},
903 | {name: 'Mouse Btn4', code: 'KC_MS_BTN4'},
904 | {name: 'Mouse Btn5', code: 'KC_MS_BTN5'},
905 | {name: 'Mouse Btn6', code: 'KC_MS_BTN6'},
906 | {name: 'Mouse Btn7', code: 'KC_MS_BTN7'},
907 | {name: 'Mouse Btn8', code: 'KC_MS_BTN8'},
908 | {name: 'Mouse Wh ↑', code: 'KC_MS_WH_UP'},
909 | {name: 'Mouse Wh ↓', code: 'KC_MS_WH_DOWN'},
910 | {name: 'Mouse Wh ←', code: 'KC_MS_WH_LEFT'},
911 | {name: 'Mouse Wh →', code: 'KC_MS_WH_RIGHT'},
912 | {name: 'Mouse Acc0', code: 'KC_MS_ACCEL0'},
913 | {name: 'Mouse Acc1', code: 'KC_MS_ACCEL1'},
914 | {name: 'Mouse Acc2', code: 'KC_MS_ACCEL2'},
915 |
916 | // TODO: move these to a new group
917 | {name: 'Audio On', code: 'AU_ON'},
918 | {name: 'Audio Off', code: 'AU_OFF'},
919 | {name: 'Audio Toggle', code: 'AU_TOG'},
920 | {name: 'Clicky Toggle', code: 'CLICKY_TOGGLE'},
921 | {name: 'Clicky Enable', code: 'CLICKY_ENABLE'},
922 | {name: 'Clicky Disable', code: 'CLICKY_DISABLE'},
923 | {name: 'Clicky Up', code: 'CLICKY_UP'},
924 | {name: 'Clicky Down', code: 'CLICKY_DOWN'},
925 | {name: 'Clicky Reset', code: 'CLICKY_RESET'},
926 | {name: 'Music On', code: 'MU_ON'},
927 | {name: 'Music Off', code: 'MU_OFF'},
928 | {name: 'Music Toggle', code: 'MU_TOG'},
929 | {name: 'Music Mode', code: 'MU_MOD'},
930 | ],
931 | },
932 | /* These are for controlling the original backlighting and bottom RGB. */
933 | {
934 | id: 'qmk_lighting',
935 | label: 'Lighting',
936 | width: 'label',
937 | keycodes: [
938 | {name: 'BL Toggle', code: 'BL_TOGG'},
939 | {name: 'BL On', code: 'BL_ON'},
940 | {name: 'BL Off', code: 'BL_OFF', shortName: 'BL Off'},
941 | {name: 'BL -', code: 'BL_DEC'},
942 | {name: 'BL +', code: 'BL_INC'},
943 | {name: 'BL Cycle', code: 'BL_STEP'},
944 | {name: 'BR Toggle', code: 'BL_BRTG'},
945 | {name: 'RGB Toggle', code: 'RGB_TOG'},
946 | {name: 'RGB Mode -', code: 'RGB_RMOD'},
947 | {name: 'RGB Mode +', code: 'RGB_MOD'},
948 | {name: 'Hue -', code: 'RGB_HUD'},
949 | {name: 'Hue +', code: 'RGB_HUI'},
950 | {name: 'Sat -', code: 'RGB_SAD'},
951 | {name: 'Sat +', code: 'RGB_SAI'},
952 | {name: 'Bright -', code: 'RGB_VAD'},
953 | {name: 'Bright +', code: 'RGB_VAI'},
954 | {name: 'Effect Speed-', code: 'RGB_SPD'},
955 | {name: 'Effect Speed+', code: 'RGB_SPI'},
956 | {name: 'RGB Mode P', code: 'RGB_M_P', title: 'Plain'},
957 | {name: 'RGB Mode B', code: 'RGB_M_B', title: 'Breathe'},
958 | {name: 'RGB Mode R', code: 'RGB_M_R', title: 'Rainbow'},
959 | {name: 'RGB Mode SW', code: 'RGB_M_SW', title: 'Swirl'},
960 | {name: 'RGB Mode SN', code: 'RGB_M_SN', title: 'Snake'},
961 | {name: 'RGB Mode K', code: 'RGB_M_K', title: 'Knight'},
962 | {name: 'RGB Mode X', code: 'RGB_M_X', title: 'Xmas'},
963 | {name: 'RGB Mode G', code: 'RGB_M_G', title: 'Gradient'},
964 | ],
965 | },
966 | /*
967 | These custom keycodes always exist and should be filtered out if necessary
968 | Name and Title should be replaced with the correct ones from the keyboard json
969 | */
970 | {
971 | id: 'custom',
972 | label: 'Custom',
973 | width: 'label',
974 | keycodes: [
975 | {name: 'CUSTOM(0)', code: 'CUSTOM(0)', title: 'Custom Keycode 0'},
976 | {name: 'CUSTOM(1)', code: 'CUSTOM(1)', title: 'Custom Keycode 1'},
977 | {name: 'CUSTOM(2)', code: 'CUSTOM(2)', title: 'Custom Keycode 2'},
978 | {name: 'CUSTOM(3)', code: 'CUSTOM(3)', title: 'Custom Keycode 3'},
979 | {name: 'CUSTOM(4)', code: 'CUSTOM(4)', title: 'Custom Keycode 4'},
980 | {name: 'CUSTOM(5)', code: 'CUSTOM(5)', title: 'Custom Keycode 5'},
981 | {name: 'CUSTOM(6)', code: 'CUSTOM(6)', title: 'Custom Keycode 6'},
982 | {name: 'CUSTOM(7)', code: 'CUSTOM(7)', title: 'Custom Keycode 7'},
983 | {name: 'CUSTOM(8)', code: 'CUSTOM(8)', title: 'Custom Keycode 8'},
984 | {name: 'CUSTOM(9)', code: 'CUSTOM(9)', title: 'Custom Keycode 9'},
985 | {name: 'CUSTOM(10)', code: 'CUSTOM(10)', title: 'Custom Keycode 10'},
986 | {name: 'CUSTOM(11)', code: 'CUSTOM(11)', title: 'Custom Keycode 11'},
987 | {name: 'CUSTOM(12)', code: 'CUSTOM(12)', title: 'Custom Keycode 12'},
988 | {name: 'CUSTOM(13)', code: 'CUSTOM(13)', title: 'Custom Keycode 13'},
989 | {name: 'CUSTOM(14)', code: 'CUSTOM(14)', title: 'Custom Keycode 14'},
990 | {name: 'CUSTOM(15)', code: 'CUSTOM(15)', title: 'Custom Keycode 15'},
991 | ],
992 | },
993 | ];
994 | }
995 |
996 | export const categoriesForKeycodeModule = (
997 | keycodeModule: BuiltInKeycodeModule | 'default',
998 | ) =>
999 | ({
1000 | default: ['basic', 'media', 'macro', 'layers', 'special'],
1001 | [BuiltInKeycodeModule.WTLighting]: ['wt_lighting'],
1002 | [BuiltInKeycodeModule.QMKLighting]: ['qmk_lighting'],
1003 | }[keycodeModule]);
1004 |
1005 | export const getKeycodesForKeyboard = (
1006 | definition: VIADefinitionV3 | VIADefinitionV2,
1007 | ) => {
1008 | // v2
1009 | let includeList: string[] = [];
1010 | if ('lighting' in definition) {
1011 | const {keycodes} = getLightingDefinition(definition.lighting);
1012 | includeList = categoriesForKeycodeModule('default').concat(
1013 | keycodes === KeycodeType.None
1014 | ? []
1015 | : keycodes === KeycodeType.QMK
1016 | ? categoriesForKeycodeModule(BuiltInKeycodeModule.QMKLighting)
1017 | : categoriesForKeycodeModule(BuiltInKeycodeModule.WTLighting),
1018 | );
1019 | } else {
1020 | const {keycodes} = definition;
1021 | includeList = keycodes.flatMap(categoriesForKeycodeModule);
1022 | }
1023 | return getKeycodes()
1024 | .flatMap((keycodeMenu) =>
1025 | includeList.includes(keycodeMenu.id) ? keycodeMenu.keycodes : [],
1026 | )
1027 | .sort((a, b) => {
1028 | if (a.code <= b.code) {
1029 | return -1;
1030 | } else {
1031 | return 1;
1032 | }
1033 | });
1034 | };
1035 |
--------------------------------------------------------------------------------
/src/vial/VialKey.hx:
--------------------------------------------------------------------------------
1 | package vial;
2 | import haxe.Json;
3 | import tools.JsonParserWithComments;
4 | import via.ViaKeyNames;
5 | import vial.VialKeyNames;
6 | import drawer.DrawerKeymap;
7 | import vial.VialKeysWithShiftState;
8 | using StringTools;
9 |
10 | /**
11 | * ...
12 | * @author YellowAfterlife
13 | */
14 | abstract VialKey(String) from String to String {
15 | public function toDrawerKey(opt:VilToDrawerOpt, oneLine:Bool = false):DrawerKey {
16 | var kc:String = this;
17 | if (kc == null || kc == "" || kc == "KC_NO") return null;
18 |
19 | // `LSft_T(kc)` and alike
20 | static var rx_modTap = new EReg("^"
21 | + "(\\w+)_T"
22 | + "\\(" + "(.+)" + "\\)",
23 | "");
24 | if (rx_modTap.match(kc)) {
25 | var key = rx_modTap.matched(1);
26 | var t:VialKey = rx_modTap.matched(2);
27 | var dk = t.toDrawerKey(opt).toExt();
28 | dk.h = key;
29 | return dk;
30 | }
31 |
32 | // show "MO " if appropriate
33 | static var rx_layer = new EReg("^(MO|DF|TG|TT|OSL|TO)" + "\\(" + "(\\d+)" + "\\)$", "");
34 | if (rx_layer.match(kc)) {
35 | var li = Std.parseInt(rx_layer.matched(2));
36 | return rx_layer.matched(1) + " " + opt.getLayerName(li, false);
37 | }
38 |
39 | // `TD(x)`
40 | static var rx_td = new EReg("^TD" + "\\(" + "(\\d+)" + "\\)$", "");
41 | if (rx_td.match(kc)) {
42 | var ti = Std.parseInt(rx_td.matched(1));
43 | var td = opt.root.tap_dance[ti];
44 | if (td != null) {
45 | return {
46 | s: kc,
47 | t: td.tap.toDrawerKey(opt, true).toFlat(Tap),
48 | h: td.hold.toDrawerKey(opt, true).toFlat(Tap),
49 | };
50 | }
51 | }
52 |
53 | // `LT2(kc)` -> { t: kc, h: MO(2) }
54 | static var rx_lt = new EReg("^"
55 | + "(?:"
56 | + "LT(\\d)\\(" + "|"
57 | + "LT\\((\\d+),\\s*"
58 | + ")" + "(.+)" + "\\)"
59 | + "$", "");
60 | if (rx_lt.match(kc)) {
61 | var t:VialKey = rx_lt.matched(3);
62 | var lts = rx_lt.matched(1) ?? rx_lt.matched(2);
63 | var h:VialKey = "MO(" + lts + ")";
64 | var dk = t.toDrawerKey(opt).toExt();
65 | dk.h = h.toDrawerKey(opt).toFlat(Tap);
66 | return dk;
67 | }
68 |
69 | // `LSFT(kc)` (special handling - replaces tap-state by shift-state)
70 | static var rx_shift = new EReg("^([LR]SFT)" + "\\(" + "(.+)" + "\\)$", "");
71 | if (rx_shift.match(kc)) {
72 | var shift = rx_shift.matched(1) + "+";
73 | var key:VialKey = rx_shift.matched(2);
74 | var dk = key.toDrawerKey(opt).toExt();
75 | if (dk.s != null) {
76 | dk.t = dk.s;
77 | }
78 | dk.s = shift;
79 | return dk;
80 | }
81 |
82 | // other `MOD(kc)` keys
83 | static var rx_pair = new EReg("^(\\w+)" + "\\(" + "(.+)" + "\\)$", "");
84 | if (rx_pair.match(kc)) {
85 | var f:String = rx_pair.matched(1);
86 | switch (f) {
87 | case "C": f = "Ctrl";
88 | case "S": f = "Shift";
89 | case "A": f = "Alt";
90 | case "G": f = "Gui";
91 | }
92 | var k:VialKey = rx_pair.matched(2);
93 | var dk = k.toDrawerKey(opt).toExt();
94 | if (dk.s != null) {
95 | dk.t = dk.s + "\n" + dk.t;
96 | }
97 | dk.s = f + "+";
98 | return dk;
99 | }
100 |
101 | // {"t":"A", "s":"B"} or "key"
102 | static var rx_json = new EReg("^(?:" + [
103 | "\\{" + ".+" + "\\}",
104 | "\"" + ".+" + "\"",
105 | ].join("|") + ")\\s*$", "");
106 | if (rx_json.match(kc)) {
107 | try {
108 | return JsonParserWithComments.parse(kc);
109 | } catch (x:Dynamic) {
110 | trace('Error parsing JSON "$kc":', x);
111 | return kc;
112 | }
113 | }
114 |
115 | var fullName = opt.isVIA ? ViaKeyNames.map[kc] : VialKeyNames.map[kc];
116 | if (fullName != null) {
117 | if (oneLine) {
118 | return fullName.replace("\n", " ");
119 | }
120 | if (VialKeysWithShiftState.map.exists(kc)) {
121 | var parts = fullName.split("\n");
122 | if (parts.length > 1) {
123 | return { s: parts[0], t: parts[1] };
124 | }
125 | }
126 | return fullName;
127 | }
128 |
129 | if (kc.startsWith("KC_")) return kc.substr(3);
130 | return kc;
131 | }
132 |
133 | public function isValid() {
134 | if (this is Float && (cast this:Float) == -1) return false;
135 | return switch (this) {
136 | case null, "", "KC_NO": false;
137 | default: true;
138 | }
139 | }
140 | public function isM1() {
141 | return (this is Float) && (cast this:Float) == -1;
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/vial/VialKeyNames.hx:
--------------------------------------------------------------------------------
1 | package vial;
2 | /**
3 | * Generated by VialKeyNamesGen.hx
4 | * from vial-kb/vial-gui: src/main/python/keycodes/keycodes.py
5 | */
6 | class VialKeyNames {
7 | public static var map:Map = [
8 | "KC_TRNS" => "▽",
9 | "KC_NUMLOCK" => "Num\nLock",
10 | "KC_KP_SLASH" => "/",
11 | "KC_KP_ASTERISK" => "*",
12 | "KC_KP_MINUS" => "-",
13 | "KC_KP_PLUS" => "+",
14 | "KC_KP_ENTER" => "Num\nEnter",
15 | "KC_KP_1" => "1",
16 | "KC_KP_2" => "2",
17 | "KC_KP_3" => "3",
18 | "KC_KP_4" => "4",
19 | "KC_KP_5" => "5",
20 | "KC_KP_6" => "6",
21 | "KC_KP_7" => "7",
22 | "KC_KP_8" => "8",
23 | "KC_KP_9" => "9",
24 | "KC_KP_0" => "0",
25 | "KC_KP_DOT" => ".",
26 | "KC_KP_EQUAL" => "=",
27 | "KC_KP_COMMA" => ",",
28 | "KC_PSCREEN" => "Print\nScreen",
29 | "KC_SCROLLLOCK" => "Scroll\nLock",
30 | "KC_PAUSE" => "Pause",
31 | "KC_INSERT" => "Insert",
32 | "KC_HOME" => "Home",
33 | "KC_PGUP" => "Page\nUp",
34 | "KC_DELETE" => "Del",
35 | "KC_END" => "End",
36 | "KC_PGDOWN" => "Page\nDown",
37 | "KC_RIGHT" => "Right",
38 | "KC_LEFT" => "Left",
39 | "KC_DOWN" => "Down",
40 | "KC_UP" => "Up",
41 | "KC_A" => "A",
42 | "KC_B" => "B",
43 | "KC_C" => "C",
44 | "KC_D" => "D",
45 | "KC_E" => "E",
46 | "KC_F" => "F",
47 | "KC_G" => "G",
48 | "KC_H" => "H",
49 | "KC_I" => "I",
50 | "KC_J" => "J",
51 | "KC_K" => "K",
52 | "KC_L" => "L",
53 | "KC_M" => "M",
54 | "KC_N" => "N",
55 | "KC_O" => "O",
56 | "KC_P" => "P",
57 | "KC_Q" => "Q",
58 | "KC_R" => "R",
59 | "KC_S" => "S",
60 | "KC_T" => "T",
61 | "KC_U" => "U",
62 | "KC_V" => "V",
63 | "KC_W" => "W",
64 | "KC_X" => "X",
65 | "KC_Y" => "Y",
66 | "KC_Z" => "Z",
67 | "KC_1" => "!\n1",
68 | "KC_2" => "@\n2",
69 | "KC_3" => "#\n3",
70 | "KC_4" => "$\n4",
71 | "KC_5" => "%\n5",
72 | "KC_6" => "^\n6",
73 | "KC_7" => "&\n7",
74 | "KC_8" => "*\n8",
75 | "KC_9" => "(\n9",
76 | "KC_0" => ")\n0",
77 | "KC_ENTER" => "Enter",
78 | "KC_ESCAPE" => "Esc",
79 | "KC_BSPACE" => "Bksp",
80 | "KC_TAB" => "Tab",
81 | "KC_SPACE" => "Space",
82 | "KC_MINUS" => "_\n-",
83 | "KC_EQUAL" => "+\n=",
84 | "KC_LBRACKET" => "{\n[",
85 | "KC_RBRACKET" => "}\n]",
86 | "KC_BSLASH" => "|\n\\",
87 | "KC_SCOLON" => ":\n;",
88 | "KC_QUOTE" => "\"\n'",
89 | "KC_GRAVE" => "~\n`",
90 | "KC_COMMA" => "<\n,",
91 | "KC_DOT" => ">\n.",
92 | "KC_SLASH" => "?\n/",
93 | "KC_CAPSLOCK" => "Caps\nLock",
94 | "KC_F1" => "F1",
95 | "KC_F2" => "F2",
96 | "KC_F3" => "F3",
97 | "KC_F4" => "F4",
98 | "KC_F5" => "F5",
99 | "KC_F6" => "F6",
100 | "KC_F7" => "F7",
101 | "KC_F8" => "F8",
102 | "KC_F9" => "F9",
103 | "KC_F10" => "F10",
104 | "KC_F11" => "F11",
105 | "KC_F12" => "F12",
106 | "KC_APPLICATION" => "Menu",
107 | "KC_LCTRL" => "LCtrl",
108 | "KC_LSHIFT" => "LShift",
109 | "KC_LALT" => "LAlt",
110 | "KC_LGUI" => "LGui",
111 | "KC_RCTRL" => "RCtrl",
112 | "KC_RSHIFT" => "RShift",
113 | "KC_RALT" => "RAlt",
114 | "KC_RGUI" => "RGui",
115 | "KC_NONUS_HASH" => "~\n#",
116 | "KC_NONUS_BSLASH" => "|\n\\",
117 | "KC_RO" => "_\n\\",
118 | "KC_KANA" => "カタカナ\nひらがな",
119 | "KC_JYEN" => "|\n¥",
120 | "KC_HENK" => "変換",
121 | "KC_MHEN" => "無変換",
122 | "KC_LANG1" => "한영\nかな",
123 | "KC_LANG2" => "漢字\n英数",
124 | "RESET" => "Reset",
125 | "OSM(MOD_LSFT)" => "OSM\nLSft",
126 | "OSM(MOD_LCTL)" => "OSM\nLCtl",
127 | "OSM(MOD_LALT)" => "OSM\nLAlt",
128 | "OSM(MOD_LGUI)" => "OSM\nLGUI",
129 | "OSM(MOD_RSFT)" => "OSM\nRSft",
130 | "OSM(MOD_RCTL)" => "OSM\nRCtl",
131 | "OSM(MOD_RALT)" => "OSM\nRAlt",
132 | "OSM(MOD_RGUI)" => "OSM\nRGUI",
133 | "OSM(MOD_LCTL|MOD_LSFT)" => "OSM\nCS",
134 | "OSM(MOD_LCTL|MOD_LALT)" => "OSM\nCA",
135 | "OSM(MOD_LCTL|MOD_LGUI)" => "OSM\nCG",
136 | "OSM(MOD_LSFT|MOD_LALT)" => "OSM\nSA",
137 | "OSM(MOD_LSFT|MOD_LGUI)" => "OSM\nSG",
138 | "OSM(MOD_LALT|MOD_LGUI)" => "OSM\nAG",
139 | "OSM(MOD_RCTL|MOD_RSFT)" => "OSM\nRCS",
140 | "OSM(MOD_RCTL|MOD_RALT)" => "OSM\nRCA",
141 | "OSM(MOD_RCTL|MOD_RGUI)" => "OSM\nRCG",
142 | "OSM(MOD_RSFT|MOD_RALT)" => "OSM\nRSA",
143 | "OSM(MOD_RSFT|MOD_RGUI)" => "OSM\nRSG",
144 | "OSM(MOD_RALT|MOD_RGUI)" => "OSM\nRAG",
145 | "OSM(MOD_LCTL|MOD_LSFT|MOD_LGUI)" => "OSM\nCSG",
146 | "OSM(MOD_LCTL|MOD_LALT|MOD_LGUI)" => "OSM\nCAG",
147 | "OSM(MOD_LSFT|MOD_LALT|MOD_LGUI)" => "OSM\nSAG",
148 | "OSM(MOD_RCTL|MOD_RSFT|MOD_RGUI)" => "OSM\nRCSG",
149 | "OSM(MOD_RCTL|MOD_RALT|MOD_RGUI)" => "OSM\nRCAG",
150 | "OSM(MOD_RSFT|MOD_RALT|MOD_RGUI)" => "OSM\nRSAG",
151 | "OSM(MOD_MEH)" => "OSM\nMeh",
152 | "OSM(MOD_HYPR)" => "OSM\nHyper",
153 | "OSM(MOD_RCTL|MOD_RSFT|MOD_RALT)" => "OSM\nRMeh",
154 | "OSM(MOD_RCTL|MOD_RSFT|MOD_RALT|MOD_RGUI)" => "OSM\nRHyp",
155 | "LSFT(kc)" => "LSft\n(kc)",
156 | "LCTL(kc)" => "LCtl\n(kc)",
157 | "LALT(kc)" => "LAlt\n(kc)",
158 | "LGUI(kc)" => "LGui\n(kc)",
159 | "RSFT(kc)" => "RSft\n(kc)",
160 | "RCTL(kc)" => "RCtl\n(kc)",
161 | "RALT(kc)" => "RAlt\n(kc)",
162 | "RGUI(kc)" => "RGui\n(kc)",
163 | "C_S(kc)" => "LCS\n(kc)",
164 | "LCA(kc)" => "LCA\n(kc)",
165 | "LCG(kc)" => "LCG\n(kc)",
166 | "LSA(kc)" => "LSA\n(kc)",
167 | "SGUI(kc)" => "LSG\n(kc)",
168 | "LCAG(kc)" => "LCAG\n(kc)",
169 | "RCG(kc)" => "RCG\n(kc)",
170 | "MEH(kc)" => "Meh\n(kc)",
171 | "HYPR(kc)" => "Hyper\n(kc)",
172 | "LSFT_T(kc)" => "LSft_T\n(kc)",
173 | "LCTL_T(kc)" => "LCtl_T\n(kc)",
174 | "LALT_T(kc)" => "LAlt_T\n(kc)",
175 | "LGUI_T(kc)" => "LGui_T\n(kc)",
176 | "RSFT_T(kc)" => "RSft_T\n(kc)",
177 | "RCTL_T(kc)" => "RCtl_T\n(kc)",
178 | "RALT_T(kc)" => "RAlt_T\n(kc)",
179 | "RGUI_T(kc)" => "RGui_T\n(kc)",
180 | "C_S_T(kc)" => "LCS_T\n(kc)",
181 | "LCA_T(kc)" => "LCA_T\n(kc)",
182 | "LCG_T(kc)" => "LCG_T\n(kc)",
183 | "LSA_T(kc)" => "LSA_T\n(kc)",
184 | "SGUI_T(kc)" => "LSG_T\n(kc)",
185 | "LCAG_T(kc)" => "LCAG_T\n(kc)",
186 | "RCG_T(kc)" => "RCG_T\n(kc)",
187 | "RCAG_T(kc)" => "RCAG_T\n(kc)",
188 | "MEH_T(kc)" => "Meh_T\n(kc)",
189 | "ALL_T(kc)" => "ALL_T\n(kc)",
190 | "KC_GESC" => "~\nEsc",
191 | "KC_LSPO" => "LS\n(",
192 | "KC_RSPC" => "RS\n)",
193 | "KC_LCPO" => "LC\n(",
194 | "KC_RCPC" => "RC\n)",
195 | "KC_LAPO" => "LA\n(",
196 | "KC_RAPC" => "RA\n)",
197 | "KC_SFTENT" => "RS\nEnter",
198 | "MAGIC_SWAP_CONTROL_CAPSLOCK" => "Swap\nCtrl\nCaps",
199 | "MAGIC_UNSWAP_CONTROL_CAPSLOCK" => "Unswap\nCtrl\nCaps",
200 | "MAGIC_CAPSLOCK_TO_CONTROL" => "Caps\nto\nCtrl",
201 | "MAGIC_UNCAPSLOCK_TO_CONTROL" => "Caps\nnot to\nCtrl",
202 | "MAGIC_SWAP_LCTL_LGUI" => "Swap\nLCtl\nLGui",
203 | "MAGIC_UNSWAP_LCTL_LGUI" => "Unswap\nLCtl\nLGui",
204 | "MAGIC_SWAP_RCTL_RGUI" => "Swap\nRCtl\nRGui",
205 | "MAGIC_UNSWAP_RCTL_RGUI" => "Unswap\nRCtl\nRGui",
206 | "MAGIC_SWAP_CTL_GUI" => "Swap\nCtl\nGui",
207 | "MAGIC_UNSWAP_CTL_GUI" => "Unswap\nCtl\nGui",
208 | "MAGIC_TOGGLE_CTL_GUI" => "Toggle\nCtl\nGui",
209 | "MAGIC_SWAP_LALT_LGUI" => "Swap\nLAlt\nLGui",
210 | "MAGIC_UNSWAP_LALT_LGUI" => "Unswap\nLAlt\nLGui",
211 | "MAGIC_SWAP_RALT_RGUI" => "Swap\nRAlt\nRGui",
212 | "MAGIC_UNSWAP_RALT_RGUI" => "Unswap\nRAlt\nRGui",
213 | "MAGIC_SWAP_ALT_GUI" => "Swap\nAlt\nGui",
214 | "MAGIC_UNSWAP_ALT_GUI" => "Unswap\nAlt\nGui",
215 | "MAGIC_TOGGLE_ALT_GUI" => "Toggle\nAlt\nGui",
216 | "MAGIC_NO_GUI" => "GUI\nOff",
217 | "MAGIC_UNNO_GUI" => "GUI\nOn",
218 | "MAGIC_SWAP_GRAVE_ESC" => "Swap\n`\nEsc",
219 | "MAGIC_UNSWAP_GRAVE_ESC" => "Unswap\n`\nEsc",
220 | "MAGIC_SWAP_BACKSLASH_BACKSPACE" => "Swap\n\\\nBS",
221 | "MAGIC_UNSWAP_BACKSLASH_BACKSPACE" => "Unswap\n\\\nBS",
222 | "MAGIC_HOST_NKRO" => "NKRO\nOn",
223 | "MAGIC_UNHOST_NKRO" => "NKRO\nOff",
224 | "MAGIC_TOGGLE_NKRO" => "NKRO\nToggle",
225 | "MAGIC_EE_HANDS_LEFT" => "EEH\nLeft",
226 | "MAGIC_EE_HANDS_RIGHT" => "EEH\nRight",
227 | "AU_ON" => "Audio\nON",
228 | "AU_OFF" => "Audio\nOFF",
229 | "AU_TOG" => "Audio\nToggle",
230 | "CLICKY_TOGGLE" => "Clicky\nToggle",
231 | "CLICKY_UP" => "Clicky\nUp",
232 | "CLICKY_DOWN" => "Clicky\nDown",
233 | "CLICKY_RESET" => "Clicky\nReset",
234 | "MU_ON" => "Music\nOn",
235 | "MU_OFF" => "Music\nOff",
236 | "MU_TOG" => "Music\nToggle",
237 | "MU_MOD" => "Music\nCycle",
238 | "HPT_ON" => "Haptic\nOn",
239 | "HPT_OFF" => "Haptic\nOff",
240 | "HPT_TOG" => "Haptic\nToggle",
241 | "HPT_RST" => "Haptic\nReset",
242 | "HPT_FBK" => "Haptic\nFeed\nback",
243 | "HPT_BUZ" => "Haptic\nBuzz",
244 | "HPT_MODI" => "Haptic\nNext",
245 | "HPT_MODD" => "Haptic\nPrev",
246 | "HPT_CONT" => "Haptic\nCont.",
247 | "HPT_CONI" => "Haptic\n+",
248 | "HPT_COND" => "Haptic\n-",
249 | "HPT_DWLI" => "Haptic\nDwell+",
250 | "HPT_DWLD" => "Haptic\nDwell-",
251 | "KC_ASDN" => "Auto-\nshift\nDown",
252 | "KC_ASUP" => "Auto-\nshift\nUp",
253 | "KC_ASRP" => "Auto-\nshift\nReport",
254 | "KC_ASON" => "Auto-\nshift\nOn",
255 | "KC_ASOFF" => "Auto-\nshift\nOff",
256 | "KC_ASTG" => "Auto-\nshift\nToggle",
257 | "CMB_ON" => "Combo\nOn",
258 | "CMB_OFF" => "Combo\nOff",
259 | "CMB_TOG" => "Combo\nToggle",
260 | "BL_TOGG" => "BL\nToggle",
261 | "BL_STEP" => "BL\nCycle",
262 | "BL_BRTG" => "BL\nBreath",
263 | "BL_ON" => "BL On",
264 | "BL_OFF" => "BL Off",
265 | "BL_INC" => "BL +",
266 | "BL_DEC" => "BL - ",
267 | "RGB_TOG" => "RGB\nToggle",
268 | "RGB_MOD" => "RGB\nMode +",
269 | "RGB_RMOD" => "RGB\nMode -",
270 | "RGB_HUI" => "Hue +",
271 | "RGB_HUD" => "Hue -",
272 | "RGB_SAI" => "Sat +",
273 | "RGB_SAD" => "Sat -",
274 | "RGB_VAI" => "Bright +",
275 | "RGB_VAD" => "Bright -",
276 | "RGB_SPI" => "Effect +",
277 | "RGB_SPD" => "Effect -",
278 | "RGB_M_P" => "RGB\nMode P",
279 | "RGB_M_B" => "RGB\nMode B",
280 | "RGB_M_R" => "RGB\nMode R",
281 | "RGB_M_SW" => "RGB\nMode SW",
282 | "RGB_M_SN" => "RGB\nMode SN",
283 | "RGB_M_K" => "RGB\nMode K",
284 | "RGB_M_X" => "RGB\nMode X",
285 | "RGB_M_G" => "RGB\nMode G",
286 | "RGB_M_T" => "RGB\nMode T",
287 | "KC_PWR" => "Power",
288 | "KC_SLEP" => "Sleep",
289 | "KC_WAKE" => "Wake",
290 | "KC_EXEC" => "Exec",
291 | "KC_SLCT" => "Select",
292 | "KC_AGIN" => "Again",
293 | "KC_PSTE" => "Paste",
294 | "KC_CALC" => "Calc",
295 | "KC_MAIL" => "Mail",
296 | "KC_MSEL" => "Media\nPlayer",
297 | "KC_MYCM" => "My\nPC",
298 | "KC_WSCH" => "Browser\nSearch",
299 | "KC_WHOM" => "Browser\nHome",
300 | "KC_WBAK" => "Browser\nBack",
301 | "KC_WFWD" => "Browser\nForward",
302 | "KC_WSTP" => "Browser\nStop",
303 | "KC_WREF" => "Browser\nRefresh",
304 | "KC_WFAV" => "Browser\nFav.",
305 | "KC_BRIU" => "Bright.\nUp",
306 | "KC_BRID" => "Bright.\nDown",
307 | "KC_MPRV" => "Media\nPrev",
308 | "KC_MNXT" => "Media\nNext",
309 | "KC_MUTE" => "Mute",
310 | "KC_VOLD" => "Vol -",
311 | "KC_VOLU" => "Vol +",
312 | "KC__VOLDOWN" => "Vol -\nAlt",
313 | "KC__VOLUP" => "Vol +\nAlt",
314 | "KC_MSTP" => "Media\nStop",
315 | "KC_MPLY" => "Media\nPlay",
316 | "KC_MRWD" => "Prev\nTrack\n(macOS)",
317 | "KC_MFFD" => "Next\nTrack\n(macOS)",
318 | "KC_EJCT" => "Eject",
319 | "KC_MS_U" => "Mouse\nUp",
320 | "KC_MS_D" => "Mouse\nDown",
321 | "KC_MS_L" => "Mouse\nLeft",
322 | "KC_MS_R" => "Mouse\nRight",
323 | "KC_BTN1" => "Mouse\n1",
324 | "KC_BTN2" => "Mouse\n2",
325 | "KC_BTN3" => "Mouse\n3",
326 | "KC_BTN4" => "Mouse\n4",
327 | "KC_BTN5" => "Mouse\n5",
328 | "KC_WH_U" => "Mouse\nWheel\nUp",
329 | "KC_WH_D" => "Mouse\nWheel\nDown",
330 | "KC_WH_L" => "Mouse\nWheel\nLeft",
331 | "KC_WH_R" => "Mouse\nWheel\nRight",
332 | "KC_ACL0" => "Mouse\nAccel\n0",
333 | "KC_ACL1" => "Mouse\nAccel\n1",
334 | "KC_ACL2" => "Mouse\nAccel\n2",
335 | "KC_LCAP" => "Locking\nCaps",
336 | "KC_LNUM" => "Locking\nNum",
337 | "KC_LSCR" => "Locking\nScroll",
338 | "DYN_REC_START1" => "DM1\nRec",
339 | "DYN_REC_START2" => "DM2\nRec",
340 | "DYN_REC_STOP" => "DM Rec\nStop",
341 | "DYN_MACRO_PLAY1" => "DM1\nPlay",
342 | "DYN_MACRO_PLAY2" => "DM2\nPlay",
343 | "MI_C" => "ᴹᴵᴰᴵ\nC",
344 | "MI_Cs" => "ᴹᴵᴰᴵ\nC#/Dᵇ",
345 | "MI_D" => "ᴹᴵᴰᴵ\nD",
346 | "MI_Ds" => "ᴹᴵᴰᴵ\nD#/Eᵇ",
347 | "MI_E" => "ᴹᴵᴰᴵ\nE",
348 | "MI_F" => "ᴹᴵᴰᴵ\nF",
349 | "MI_Fs" => "ᴹᴵᴰᴵ\nF#/Gᵇ",
350 | "MI_G" => "ᴹᴵᴰᴵ\nG",
351 | "MI_Gs" => "ᴹᴵᴰᴵ\nG#/Aᵇ",
352 | "MI_A" => "ᴹᴵᴰᴵ\nA",
353 | "MI_As" => "ᴹᴵᴰᴵ\nA#/Bᵇ",
354 | "MI_B" => "ᴹᴵᴰᴵ\nB",
355 | "MI_C_1" => "ᴹᴵᴰᴵ\nC₁",
356 | "MI_Cs_1" => "ᴹᴵᴰᴵ\nC#₁/Dᵇ₁",
357 | "MI_D_1" => "ᴹᴵᴰᴵ\nD₁",
358 | "MI_Ds_1" => "ᴹᴵᴰᴵ\nD#₁/Eᵇ₁",
359 | "MI_E_1" => "ᴹᴵᴰᴵ\nE₁",
360 | "MI_F_1" => "ᴹᴵᴰᴵ\nF₁",
361 | "MI_Fs_1" => "ᴹᴵᴰᴵ\nF#₁/Gᵇ₁",
362 | "MI_G_1" => "ᴹᴵᴰᴵ\nG₁",
363 | "MI_Gs_1" => "ᴹᴵᴰᴵ\nG#₁/Aᵇ₁",
364 | "MI_A_1" => "ᴹᴵᴰᴵ\nA₁",
365 | "MI_As_1" => "ᴹᴵᴰᴵ\nA#₁/Bᵇ₁",
366 | "MI_B_1" => "ᴹᴵᴰᴵ\nB₁",
367 | "MI_C_2" => "ᴹᴵᴰᴵ\nC₂",
368 | "MI_Cs_2" => "ᴹᴵᴰᴵ\nC#₂/Dᵇ₂",
369 | "MI_D_2" => "ᴹᴵᴰᴵ\nD₂",
370 | "MI_Ds_2" => "ᴹᴵᴰᴵ\nD#₂/Eᵇ₂",
371 | "MI_E_2" => "ᴹᴵᴰᴵ\nE₂",
372 | "MI_F_2" => "ᴹᴵᴰᴵ\nF₂",
373 | "MI_Fs_2" => "ᴹᴵᴰᴵ\nF#₂/Gᵇ₂",
374 | "MI_G_2" => "ᴹᴵᴰᴵ\nG₂",
375 | "MI_Gs_2" => "ᴹᴵᴰᴵ\nG#₂/Aᵇ₂",
376 | "MI_A_2" => "ᴹᴵᴰᴵ\nA₂",
377 | "MI_As_2" => "ᴹᴵᴰᴵ\nA#₂/Bᵇ₂",
378 | "MI_B_2" => "ᴹᴵᴰᴵ\nB₂",
379 | "MI_C_3" => "ᴹᴵᴰᴵ\nC₃",
380 | "MI_Cs_3" => "ᴹᴵᴰᴵ\nC#₃/Dᵇ₃",
381 | "MI_D_3" => "ᴹᴵᴰᴵ\nD₃",
382 | "MI_Ds_3" => "ᴹᴵᴰᴵ\nD#₃/Eᵇ₃",
383 | "MI_E_3" => "ᴹᴵᴰᴵ\nE₃",
384 | "MI_F_3" => "ᴹᴵᴰᴵ\nF₃",
385 | "MI_Fs_3" => "ᴹᴵᴰᴵ\nF#₃/Gᵇ₃",
386 | "MI_G_3" => "ᴹᴵᴰᴵ\nG₃",
387 | "MI_Gs_3" => "ᴹᴵᴰᴵ\nG#₃/Aᵇ₃",
388 | "MI_A_3" => "ᴹᴵᴰᴵ\nA₃",
389 | "MI_As_3" => "ᴹᴵᴰᴵ\nA#₃/Bᵇ₃",
390 | "MI_B_3" => "ᴹᴵᴰᴵ\nB₃",
391 | "MI_C_4" => "ᴹᴵᴰᴵ\nC₄",
392 | "MI_Cs_4" => "ᴹᴵᴰᴵ\nC#₄/Dᵇ₄",
393 | "MI_D_4" => "ᴹᴵᴰᴵ\nD₄",
394 | "MI_Ds_4" => "ᴹᴵᴰᴵ\nD#₄/Eᵇ₄",
395 | "MI_E_4" => "ᴹᴵᴰᴵ\nE₄",
396 | "MI_F_4" => "ᴹᴵᴰᴵ\nF₄",
397 | "MI_Fs_4" => "ᴹᴵᴰᴵ\nF#₄/Gᵇ₄",
398 | "MI_G_4" => "ᴹᴵᴰᴵ\nG₄",
399 | "MI_Gs_4" => "ᴹᴵᴰᴵ\nG#₄/Aᵇ₄",
400 | "MI_A_4" => "ᴹᴵᴰᴵ\nA₄",
401 | "MI_As_4" => "ᴹᴵᴰᴵ\nA#₄/Bᵇ₄",
402 | "MI_B_4" => "ᴹᴵᴰᴵ\nB₄",
403 | "MI_C_5" => "ᴹᴵᴰᴵ\nC₅",
404 | "MI_Cs_5" => "ᴹᴵᴰᴵ\nC#₅/Dᵇ₅",
405 | "MI_D_5" => "ᴹᴵᴰᴵ\nD₅",
406 | "MI_Ds_5" => "ᴹᴵᴰᴵ\nD#₅/Eᵇ₅",
407 | "MI_E_5" => "ᴹᴵᴰᴵ\nE₅",
408 | "MI_F_5" => "ᴹᴵᴰᴵ\nF₅",
409 | "MI_Fs_5" => "ᴹᴵᴰᴵ\nF#₅/Gᵇ₅",
410 | "MI_G_5" => "ᴹᴵᴰᴵ\nG₅",
411 | "MI_Gs_5" => "ᴹᴵᴰᴵ\nG#₅/Aᵇ₅",
412 | "MI_A_5" => "ᴹᴵᴰᴵ\nA₅",
413 | "MI_As_5" => "ᴹᴵᴰᴵ\nA#₅/Bᵇ₅",
414 | "MI_B_5" => "ᴹᴵᴰᴵ\nB₅",
415 | "MI_ALLOFF" => "ᴹᴵᴰᴵ\nNotesᵒᶠᶠ",
416 | "MI_OCT_N2" => "ᴹᴵᴰᴵ\nOct₋₂",
417 | "MI_OCT_N1" => "ᴹᴵᴰᴵ\nOct₋₁",
418 | "MI_OCT_0" => "ᴹᴵᴰᴵ\nOct₀",
419 | "MI_OCT_1" => "ᴹᴵᴰᴵ\nOct₊₁",
420 | "MI_OCT_2" => "ᴹᴵᴰᴵ\nOct₊₂",
421 | "MI_OCT_3" => "ᴹᴵᴰᴵ\nOct₊₃",
422 | "MI_OCT_4" => "ᴹᴵᴰᴵ\nOct₊₄",
423 | "MI_OCT_5" => "ᴹᴵᴰᴵ\nOct₊₅",
424 | "MI_OCT_6" => "ᴹᴵᴰᴵ\nOct₊₆",
425 | "MI_OCT_7" => "ᴹᴵᴰᴵ\nOct₊₇",
426 | "MI_OCTD" => "ᴹᴵᴰᴵ\nOctᴰᴺ",
427 | "MI_OCTU" => "ᴹᴵᴰᴵ\nOctᵁᴾ",
428 | "MI_TRNS_N6" => "ᴹᴵᴰᴵ\nTrans₋₆",
429 | "MI_TRNS_N5" => "ᴹᴵᴰᴵ\nTrans₋₅",
430 | "MI_TRNS_N4" => "ᴹᴵᴰᴵ\nTrans₋₄",
431 | "MI_TRNS_N3" => "ᴹᴵᴰᴵ\nTrans₋₃",
432 | "MI_TRNS_N2" => "ᴹᴵᴰᴵ\nTrans₋₂",
433 | "MI_TRNS_N1" => "ᴹᴵᴰᴵ\nTrans₋₁",
434 | "MI_TRNS_0" => "ᴹᴵᴰᴵ\nTrans₀",
435 | "MI_TRNS_1" => "ᴹᴵᴰᴵ\nTrans₊₁",
436 | "MI_TRNS_2" => "ᴹᴵᴰᴵ\nTrans₊₂",
437 | "MI_TRNS_3" => "ᴹᴵᴰᴵ\nTrans₊₃",
438 | "MI_TRNS_4" => "ᴹᴵᴰᴵ\nTrans₊₄",
439 | "MI_TRNS_5" => "ᴹᴵᴰᴵ\nTrans₊₅",
440 | "MI_TRNS_6" => "ᴹᴵᴰᴵ\nTrans₊₆",
441 | "MI_TRNSD" => "ᴹᴵᴰᴵ\nTransᴰᴺ",
442 | "MI_TRNSU" => "ᴹᴵᴰᴵ\nTransᵁᴾ",
443 | "MI_VEL_1" => "ᴹᴵᴰᴵ\nVel₁",
444 | "MI_VEL_2" => "ᴹᴵᴰᴵ\nVel₂",
445 | "MI_VEL_3" => "ᴹᴵᴰᴵ\nVel₃",
446 | "MI_VEL_4" => "ᴹᴵᴰᴵ\nVel₄",
447 | "MI_VEL_5" => "ᴹᴵᴰᴵ\nVel₅",
448 | "MI_VEL_6" => "ᴹᴵᴰᴵ\nVel₆",
449 | "MI_VEL_7" => "ᴹᴵᴰᴵ\nVel₇",
450 | "MI_VEL_8" => "ᴹᴵᴰᴵ\nVel₈",
451 | "MI_VEL_9" => "ᴹᴵᴰᴵ\nVel₉",
452 | "MI_VEL_10" => "ᴹᴵᴰᴵ\nVel₁₀",
453 | "MI_VELD" => "ᴹᴵᴰᴵ\nVelᴰᴺ",
454 | "MI_VELU" => "ᴹᴵᴰᴵ\nVelᵁᴾ",
455 | "MI_CH1" => "ᴹᴵᴰᴵ\nCH₁",
456 | "MI_CH2" => "ᴹᴵᴰᴵ\nCH₂",
457 | "MI_CH3" => "ᴹᴵᴰᴵ\nCH₃",
458 | "MI_CH4" => "ᴹᴵᴰᴵ\nCH₄",
459 | "MI_CH5" => "ᴹᴵᴰᴵ\nCH₅",
460 | "MI_CH6" => "ᴹᴵᴰᴵ\nCH₆",
461 | "MI_CH7" => "ᴹᴵᴰᴵ\nCH₇",
462 | "MI_CH8" => "ᴹᴵᴰᴵ\nCH₈",
463 | "MI_CH9" => "ᴹᴵᴰᴵ\nCH₉",
464 | "MI_CH10" => "ᴹᴵᴰᴵ\nCH₁₀",
465 | "MI_CH11" => "ᴹᴵᴰᴵ\nCH₁₁",
466 | "MI_CH12" => "ᴹᴵᴰᴵ\nCH₁₂",
467 | "MI_CH13" => "ᴹᴵᴰᴵ\nCH₁₃",
468 | "MI_CH14" => "ᴹᴵᴰᴵ\nCH₁₄",
469 | "MI_CH15" => "ᴹᴵᴰᴵ\nCH₁₅",
470 | "MI_CH16" => "ᴹᴵᴰᴵ\nCH₁₆",
471 | "MI_CHD" => "ᴹᴵᴰᴵ\nCHᴰᴺ",
472 | "MI_CHU" => "ᴹᴵᴰᴵ\nCHᵁᴾ",
473 | "MI_SUS" => "ᴹᴵᴰᴵ\nSust",
474 | "MI_PORT" => "ᴹᴵᴰᴵ\nPort",
475 | "MI_SOST" => "ᴹᴵᴰᴵ\nSost",
476 | "MI_SOFT" => "ᴹᴵᴰᴵ\nSPedal",
477 | "MI_LEG" => "ᴹᴵᴰᴵ\nLegat",
478 | "MI_MOD" => "ᴹᴵᴰᴵ\nModul",
479 | "MI_MODSD" => "ᴹᴵᴰᴵ\nModulᴰᴺ",
480 | "MI_MODSU" => "ᴹᴵᴰᴵ\nModulᵁᴾ",
481 | "MI_BENDD" => "ᴹᴵᴰᴵ\nBendᴰᴺ",
482 | "MI_BENDU" => "ᴹᴵᴰᴵ\nBendᵁᴾ",
483 | ];
484 | }
485 |
--------------------------------------------------------------------------------
/src/vial/VialKeyNamesGen.hx:
--------------------------------------------------------------------------------
1 | package vial;
2 | import sys.io.File;
3 | import tools.ERegTools;
4 |
5 | /**
6 | * How to run (from `src/`):
7 | * haxe --neko vial/VialKeyNamesGen.n -main vial.VialKeyNamesGen
8 | * neko vial/VialKeyNamesGen.n
9 | * @author YellowAfterlife
10 | */
11 | class VialKeyNamesGen {
12 | public static function main() {
13 | var py = File.getContent("vial/keycodes.py");
14 | var b = new StringBuf();
15 | for (line in [
16 | "package vial;",
17 | "/** ",
18 | " * Generated by VialKeyNamesGen.hx",
19 | " * from vial-kb/vial-gui: src/main/python/keycodes/keycodes.py",
20 | " */",
21 | "class VialKeyNames {",
22 | " public static var map:Map = ["
23 | ]) b.add(line + "\r\n");
24 | ERegTools.each(~/^\s*K\((".+?"), (".+?"),.+/gm, py, function(rx:EReg) {
25 | b.add("\t\t" + rx.matched(1) + " => " + rx.matched(2) + ",\r\n");
26 | });
27 | b.add("\t];\r\n");
28 | b.add("}\r\n");
29 | File.saveContent("vial/VialKeyNames.hx", b.toString());
30 | Sys.println("OK!");
31 | }
32 | }
--------------------------------------------------------------------------------
/src/vial/VialKeyNamesGen.n:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YAL-Tools/vial-to-keymap-drawer/7aa058f163f786e28a92e15446af9eb39ca80d56/src/vial/VialKeyNamesGen.n
--------------------------------------------------------------------------------
/src/vial/VialKeymap.hx:
--------------------------------------------------------------------------------
1 | package vial;
2 | import haxe.extern.EitherType;
3 | import drawer.DrawerKeymap;
4 | using StringTools;
5 |
6 | /**
7 | * @author YellowAfterlife
8 | */
9 | typedef VialKeymap = {
10 | // VIA:
11 | var layers:Array>;
12 | // VIAL:
13 | var version:Int;
14 | var uid:Int;
15 | var layout:Array>>;
16 | var encoder_layout:Array>>;
17 | var layout_options:Int;
18 | var vial_protocol:Int;
19 | var via_protocol:Int;
20 | var tap_dance:Array;
21 | var combo:Array>;
22 | var key_override:Array;
23 | var settings:Any;
24 | };
25 | abstract VialKeymapTapDance(Array) {
26 | public var tap(get, set):VialKey;
27 | inline function get_tap() return this[0];
28 | inline function set_tap(k) return this[0] = k;
29 |
30 | public var hold(get, set):VialKey;
31 | inline function get_hold() return this[1];
32 | inline function set_hold(k) return this[1] = k;
33 |
34 | public var doubleTap(get, set):VialKey;
35 | inline function get_doubleTap() return this[2];
36 | inline function set_doubleTap(k) return this[2] = k;
37 |
38 | public var tapHold(get, set):VialKey;
39 | inline function get_tapHold() return this[3];
40 | inline function set_tapHold(k) return this[3] = k;
41 |
42 | public var tapTerm(get, set):Float;
43 | inline function get_tapTerm() return this[4];
44 | inline function set_tapTerm(t) return this[4] = t;
45 | }
--------------------------------------------------------------------------------
/src/vial/VialKeysWithShiftState.hx:
--------------------------------------------------------------------------------
1 | package vial;
2 |
3 | /**
4 | * Maps keys that are labelled like
5 | * "!\n1" (shift output goes first)
6 | * @author YellowAfterlife
7 | */
8 | class VialKeysWithShiftState {
9 | public static var list:Array = [
10 | // digits (of course)
11 | "KC_1",
12 | "KC_2",
13 | "KC_3",
14 | "KC_4",
15 | "KC_5",
16 | "KC_6",
17 | "KC_7",
18 | "KC_8",
19 | "KC_9",
20 | "KC_0",
21 | // common symbols:
22 | "KC_MINUS",
23 | "KC_EQUAL",
24 | "KC_LBRACKET",
25 | "KC_RBRACKET",
26 | "KC_BSLASH",
27 | "KC_SCOLON",
28 | "KC_QUOTE",
29 | "KC_GRAVE",
30 | "KC_COMMA",
31 | "KC_DOT",
32 | "KC_SLASH",
33 | // international:
34 | "KC_NONUS_HASH",
35 | "KC_NONUS_BSLASH",
36 | "KC_RO",
37 | "KC_KANA",
38 | "KC_JYEN",
39 | "KC_HENK",
40 | "KC_MHEN",
41 | "KC_LANG1",
42 | "KC_LANG2",
43 | // anything else..?
44 | ];
45 | public static var map:Map = (function() {
46 | var m = new Map();
47 | for (key in list) m[key] = true;
48 | return m;
49 | })();
50 | }
--------------------------------------------------------------------------------
/src/web/KeyRangeCreator.hx:
--------------------------------------------------------------------------------
1 | package web;
2 | import Main.find;
3 | import js.Browser;
4 | import js.html.DivElement;
5 | import js.html.Element;
6 | import js.html.FileReader;
7 | import js.html.FormElement;
8 | import js.html.InputElement;
9 | import js.html.MouseEvent;
10 | import js.html.TextAreaElement;
11 | import VilToDrawer;
12 |
13 | /**
14 | * ...
15 | * @author YellowAfterlife
16 | */
17 | class KeyRangeCreator {
18 | public static var element:DivElement = find("key-range-editor");
19 | public static var svgCtr:DivElement = cast element.querySelector(".svg-ctr");
20 | public static var svgOld:DivElement = cast svgCtr.querySelector(".svg-old");
21 | public static var svgNew:DivElement = cast svgCtr.querySelector(".svg-new");
22 |
23 | public static var btOpen:InputElement = find("key-range-open");
24 | public static var btClose:InputElement = find("key-range-close");
25 |
26 | public static var btCopyYAML:InputElement = find("key-range-export");
27 | public static var btOpenSVG:InputElement = find("key-range-import");
28 | public static var fmOpenSVG:FormElement = find("key-range-svg-form");
29 | public static var fpOpenSVG:InputElement = find("key-range-svg-picker");
30 |
31 | public static var fdOut:TextAreaElement = find("key-range-preview");
32 |
33 | public static var btCopyYAML_text = btCopyYAML.value;
34 | static function btCopyYAML_show(next:String) {
35 | var orig = btCopyYAML.value;
36 | if (orig != next) {
37 | btCopyYAML.value = "Copied!";
38 | Browser.window.setTimeout(function() {
39 | btCopyYAML.value = btCopyYAML_text;
40 | }, 1000);
41 | }
42 | }
43 |
44 | static var keyInfo:Array = [];
45 | static var keyOrder:Array = [];
46 | static function update() {
47 | var newKeys = svgNew.querySelectorAll("g.key");
48 | var oldKeys = svgOld.querySelectorAll("g.key");
49 | for (i in 0 ... newKeys.length) {
50 | var ki = keyOrder[i];
51 | var okey:Element = ki != null ? cast oldKeys[ki] : null;
52 | var nkey:Element = cast newKeys[i];
53 | if (okey != null) {
54 | var rect = nkey.querySelector("rect").outerHTML;
55 | nkey.innerHTML = okey.innerHTML;
56 | nkey.querySelector("rect").outerHTML = rect;
57 | } else {
58 | var todo = [];
59 | for (c in nkey.children) if (c.tagName.toLowerCase() != "rect") todo.push(c);
60 | for (c in todo) nkey.removeChild(c);
61 | }
62 | }
63 |
64 | var ranges = [];
65 | var rangeStart:VialKeyInfo = null;
66 | var rangeEnd:VialKeyInfo = null;
67 | function flushRange() {
68 | var txt = rangeStart.row + "," + rangeStart.col;
69 | if (rangeEnd.col != rangeStart.col) txt += '-' + rangeEnd.col;
70 | ranges.push(txt);
71 | }
72 | for (i => ki in keyOrder) {
73 | var kp:VialKeyInfo = keyInfo[ki];
74 | if (rangeStart == null) {
75 | rangeStart = rangeEnd = kp;
76 | } else if (kp.row != rangeEnd.row || kp.col != rangeEnd.col + 1) {
77 | flushRange();
78 | rangeStart = rangeEnd = kp;
79 | } else rangeEnd = kp;
80 | }
81 | if (rangeStart != null) flushRange();
82 | //
83 | Main.fdKeyRanges.value = fdOut.value = ranges.join("\n");
84 | }
85 |
86 | static var svgMouseDown = false;
87 | static function svgReady(svg:String) {
88 | svgOld.innerHTML = svg;
89 | svgOld.onmousedown = function(e:MouseEvent) {
90 | svgMouseDown = true;
91 | var fn;
92 | fn = function(e:MouseEvent) {
93 | Browser.window.removeEventListener("mouseup", fn);
94 | svgMouseDown = false;
95 | Browser.console.log("up");
96 | }
97 | Browser.window.addEventListener("mouseup", fn);
98 | e.preventDefault();
99 | return false;
100 | }
101 | //
102 | var keyCount = 0;
103 | for (_key in svgOld.querySelectorAll("g.key")) {
104 | var key:Element = cast _key;
105 | var ki = keyCount++;
106 | key.onclick = function(_) {
107 | if (keyOrder.remove(ki)) {
108 | key.style.opacity = "1";
109 | } else {
110 | key.style.opacity = "0.3";
111 | keyOrder.push(ki);
112 | }
113 | update();
114 | }
115 | key.onmousemove = function(_) {
116 | if (svgMouseDown) {
117 | if (!keyOrder.contains(ki)) {
118 | keyOrder.push(ki);
119 | key.style.opacity = "0.3";
120 | update();
121 | }
122 | }
123 | }
124 | }
125 | //
126 | svgNew.innerHTML = svg;
127 | for (el in svgNew.querySelectorAll("g.key > text:not(.tap)")) {
128 | el.parentElement.removeChild(el);
129 | }
130 | //
131 | update();
132 | }
133 | public static function init() {
134 | btCopyYAML.onclick = function(_) {
135 | var yaml = Main.convert_impl(true);
136 | if (yaml != null) {
137 | keyInfo = Main.latestOpt.outKeys;
138 | Browser.navigator.clipboard.writeText(yaml).then(cast function() {
139 | btCopyYAML_show("Copied!");
140 | }).catchError(cast function() {
141 | btCopyYAML_show("Can't copy!");
142 | });
143 | } else {
144 | btCopyYAML_show("Error!");
145 | }
146 | }
147 | btOpenSVG.onclick = function() {
148 | fpOpenSVG.click();
149 | }
150 | fpOpenSVG.onchange = function() {
151 | var file = fpOpenSVG.files[0];
152 | if (file == null) return;
153 |
154 | var fileReader = new FileReader();
155 | fileReader.onloadend = function() {
156 | fmOpenSVG.reset();
157 | }
158 | fileReader.onload = function() {
159 | svgReady(fileReader.result);
160 | }
161 | fileReader.readAsText(file);
162 | }
163 |
164 | btOpen.onclick = function() {
165 | element.style.display = "";
166 | }
167 | btClose.onclick = function() {
168 | element.style.display = "none";
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------