Skip to content

Commit

Permalink
Yeah
Browse files Browse the repository at this point in the history
  • Loading branch information
YellowAfterlife committed Sep 8, 2023
0 parents commit e21ebde
Show file tree
Hide file tree
Showing 17 changed files with 2,014 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/bin/style.min.css
/bin/VialToKeymapDrawer.js
/bin/VialToKeymapDrawer.js.map
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Vial layout to Keymap Drawer converter

Takes your Vial `.vil` layouts and converts them to YAML that you can pass to
[keymap-drawer](https://github.com/caksoylar/keymap-drawer)
to render your layout to SVG/PNG images that you can show to people instead
of taking screenshots of Vial configurator.

## Inevitable caveats

Apparently the order in which Vial stores keys in `.vil` files does not necessarily match up
with how keys are defined in QMK, therefore the keys may appear out of order, depending on the keyboard.

For this I am giving you a couple checkboxes for common oddities and ability to move a key based on row-column.

Please accept my condolences in advance, but good news - you'll only need to do this once per keyboard.

## License and credits

Tool by YellowAfterlife

Written in [Haxe](https://haxe.org).

The displayed key names are based on parsing a file from
[Vial GUI](https://github.com/vial-kb/vial-gui),
so I guess this is now GPLv2, huh? Ah well
53 changes: 53 additions & 0 deletions VialToKeymapDrawer.hxproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<project version="2">
<!-- Output SWF options -->
<output>
<movie outputType="Application" />
<movie input="" />
<movie path="bin\VialToKeymapDrawer.js" />
<movie fps="0" />
<movie width="0" />
<movie height="0" />
<movie version="1" />
<movie minorVersion="0" />
<movie platform="JavaScript" />
<movie background="#FFFFFF" />
</output>
<!-- Other classes to be compiled into your SWF -->
<classpaths>
<class path="src" />
</classpaths>
<!-- Build options -->
<build>
<option directives="" />
<option flashStrict="False" />
<option noInlineOnDebug="False" />
<option mainClass="Main" />
<option enabledebug="False" />
<option additional="" />
</build>
<!-- haxelib libraries -->
<haxelib>
<!-- example: <library name="..." /> -->
</haxelib>
<!-- Class files to compile (other referenced classes will automatically be included) -->
<compileTargets>
<compile path="src\Main.hx" />
</compileTargets>
<!-- Paths to exclude from the Project Explorer tree -->
<hiddenPaths>
<hidden path="obj" />
</hiddenPaths>
<!-- Executed before build -->
<preBuildCommand />
<!-- Executed after build -->
<postBuildCommand alwaysRun="False" />
<!-- Other project options -->
<options>
<option showHiddenPaths="False" />
<option testMovie="Webserver" />
<option testMovieCommand="bin/index.html" />
</options>
<!-- Plugin storage -->
<storage />
</project>
39 changes: 39 additions & 0 deletions bin/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Vial To Keymap Drawer</title>
<meta name="description" content="" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main>
What kind of keyboard? (find it in <a href="https://config.qmk.fm/">Configurator</a>)
<input type="text" id="keyboard" value="splitkb/aurora/sofle_v2/rev1" placeholder="splitkb/aurora/sofle_v2/rev1" /><br/>
<details>
<summary>Quirks</summary>
Layout name (optional):
<input type="text" id="layout" value="" placeholder="e.g. LAYOUT_split_3x5_3" /><br/>
<input type="checkbox" checked id="half-after-half" /> One half after another (instead of row by row)<br/>
<input type="checkbox" checked id="mirror-right-half" /> Mirrored keys on right half<br/>
Key move definitions (1,0 => 0,5 to move first key from second row to be fifth key in first row):<br/>
<textarea id="move-defs" rows="4">4,5 => 3,6
9,5 => 8,6</textarea>
</details>
Layer names:<br/>
<textarea id="layer-names" rows="4">DEF
DIGITS (reverts digits to normal configuration)
NAV
MISC: F-keys and numpad</textarea>
Pick (or paste) your VIL layout:
<form id="vil-form">
<input type="file" id="vil-picker" accept=".vil" />
</form>
<textarea id="vil">{"version": 1, "uid": 12888985424971681029, "layout": [[["KC_GRAVE", "RSFT(KC_1)", "RSFT(KC_9)", "RSFT(KC_0)", "RSFT(KC_BSLASH)", "RSFT(KC_7)"], ["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", "MO(2)", "KC_ENTER", "KC_CAPSLOCK"], ["KC_BSPACE", "KC_EQUAL", "KC_MINUS", "KC_END", "KC_HOME", "RSFT(KC_8)"], ["KC_LBRACKET", "KC_P", "KC_O", "KC_I", "KC_U", "KC_Y"], ["KC_QUOTE", "KC_SCOLON", "KC_L", "KC_K", "KC_J", "KC_H"], ["RSFT_T(KC_RBRACKET)", "KC_SLASH", "KC_DOT", "KC_COMMA", "KC_M", "KC_N"], ["KC_DOWN", "KC_UP", "KC_RIGHT", "KC_LEFT", "LT3(KC_ESCAPE)", "KC_MUTE"]], [["KC_TRNS", "KC_1", "KC_2", "KC_3", "KC_4", "KC_5"], ["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"], ["LGUI_T(KC_ESCAPE)", "LALT_T(KC_BSLASH)", "KC_TRNS", "LT2(KC_MINUS)", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_0", "KC_9", "KC_8", "KC_7", "KC_6"], ["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", "LT3(KC_EQUAL)", "KC_TRNS"]], [["KC_ESCAPE", "KC_1", "KC_2", "KC_3", "KC_4", "KC_5"], ["KC_TRNS", "LCTL(KC_Z)", "LCTL(KC_X)", "LCTL(KC_C)", "LCTL(KC_V)", "TG(1)"], ["KC_TRNS", "KC_LGUI", "KC_LALT", "KC_LCTRL", "KC_LSHIFT", "KC_F22"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_CAPSLOCK"], ["KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_MPLY"], ["KC_TRNS", "KC_0", "KC_9", "KC_8", "KC_7", "KC_6"], ["KC_PSCREEN", "KC_ENTER", "KC_END", "KC_UP", "KC_HOME", "KC_DELETE"], ["KC_TRNS", "KC_SPACE", "KC_RIGHT", "KC_DOWN", "KC_LEFT", "KC_BSPACE"], ["KC_TRNS", "KC_BSLASH", "LCTL(KC_V)", "LCTL(KC_C)", "LCTL(KC_X)", "LCTL(KC_Z)"], ["KC_PGDOWN", "KC_PGUP", "KC_END", "KC_HOME", "KC_DELETE", "KC_TRNS"]], [["KC_F1", "KC_F2", "KC_F3", "KC_F4", "KC_F5", "KC_F6"], ["KC_KP_DOT", "KC_KP_7", "KC_KP_8", "KC_KP_9", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_KP_4", "KC_KP_5", "KC_KP_6", "KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_KP_1", "KC_KP_2", "KC_KP_3", "KC_TRNS", "KC_TRNS"], ["KC_KP_0", "KC_TRNS", "KC_TRNS", "KC_TRNS", "LALT(KC_F4)", "KC_TRNS"], ["KC_TRNS", "KC_F11", "KC_F10", "KC_F9", "KC_F8", "KC_F7"], ["KC_PSCREEN", "KC_F12", "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_PGDOWN", "KC_PGUP", "KC_END", "KC_HOME", "KC_TRNS", "KC_TRNS"]]], "encoder_layout": [[["KC_VOLD", "KC_VOLU"], ["KC_PGDOWN", "KC_PGUP"]], [["KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS"]], [["KC_MNXT", "KC_MPRV"], ["KC_TRNS", "KC_TRNS"]], [["KC_TRNS", "KC_TRNS"], ["KC_TRNS", "KC_TRNS"]]], "layout_options": -1, "macro": [[["tap", "KC_F19", "TG(1)"]], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []], "vial_protocol": 5, "via_protocol": 9, "tap_dance": [["LSFT(KC_9)", "LSFT(KC_2)", "KC_NO", "KC_NO", 200], ["LSFT(KC_0)", "LSFT(KC_3)", "KC_NO", "KC_NO", 200], ["LSFT(KC_BSLASH)", "KC_BSLASH", "KC_NO", "KC_NO", 160], ["LSFT(KC_7)", "LSFT(KC_5)", "KC_NO", "KC_NO", 160], ["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_9", "KC_0", "KC_NO", "KC_NO", "KC_EQUAL"], ["KC_8", "KC_9", "KC_NO", "KC_NO", "KC_MINUS"], ["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": {}}</textarea>
<input type="button" value="Convert!" id="convert" /><br/>
Output:
<textarea id="out"></textarea>
</main>
<script src="VialToKeymapDrawer.js"></script>
</body>
</html>
29 changes: 29 additions & 0 deletions bin/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
body {
background-color: #899FC6;
font: 15px sans-serif;
line-height: 1.5;
}
main {
background-color: white;
max-width: 640px;
margin: 0 auto;
padding: 0.5em;
}
main input[type="text"],
main input[type="file"],
main textarea {
width: 100%;
box-sizing: border-box;
}
#vil-form {
margin-bottom: 0.25em;
}
#convert {
padding: 0.4em 1em;
}
#vil {
height: 20vh;
}
#out {
height: 60vh;
}
22 changes: 22 additions & 0 deletions src/KcToKeyName.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ;
import vial.VialKeyNames;
using StringTools;

/**
* ...
* @author YellowAfterlife
*/
class KcToKeyName {
public static var map:Map<String, String> = [
"KC_TRNS" => "",
"KC_NO" => "",
];
public static function get(kc:String) {
var s = map[kc];
if (s != null) return s;
s = VialKeyNames.map[kc];
if (s != null) return s;
if (kc.startsWith("KC_")) return kc.substr(3);
return kc;
}
}
69 changes: 69 additions & 0 deletions src/Main.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package;

import haxe.DynamicAccess;
import haxe.Json;
import js.Browser;
import js.Lib;
import js.html.Element;
import js.html.Event;
import js.html.FileReader;
import js.html.FormElement;
import js.html.InputElement;
import js.html.TextAreaElement;
import drawer.DrawerKeymap;
import tools.ERegTools;
import vial.VialKeymap;
import VilToDrawer;
using StringTools;

/**
* ...
* @author YellowAfterlife
*/
class Main {
static inline function find<T:Element>(id:String, ?c:Class<T>):T {
return cast Browser.document.getElementById(id);
}
static var fdVil:TextAreaElement = find("vil");
static var fdOut:TextAreaElement = find("out");

static var fmVil:FormElement = find("vil-form");
static var ffVil:InputElement = find("vil-picker");

static var cbHalfAfterHalf:InputElement = find("half-after-half");
static var cbMirrorRightHalf:InputElement = find("mirror-right-half");
static var fdKeyboard:InputElement = find("keyboard");
static var fdLayout:InputElement = find("layout");
static var fdMoveDefs:TextAreaElement = find("move-defs");
static var fdLayerNames:TextAreaElement = find("layer-names");
static var btConvert:InputElement = find("convert");

static function convert() {
var opt = new VilToDrawerOpt();
opt.qmkKeyboard = fdKeyboard.value.trim();
opt.qmkLayout = fdLayout.value.trim();
opt.parseVil(fdVil.value);
opt.halfAfterHalf = cbHalfAfterHalf.checked;
opt.mirrorRightHalf = cbMirrorRightHalf.checked;
opt.parseLayerNames(fdLayerNames.value);
opt.parseMoveDefs(fdMoveDefs.value);
fdOut.value = VilToDrawer.runTxt(opt);
}
static function main() {
ffVil.onchange = function(e:Event) {
var file = ffVil.files[0];
if (file == null) return;

var fileReader = new FileReader();
fileReader.onloadend = function() fmVil.reset();
fileReader.onload = function() {
fdVil.value = fileReader.result;
}
fileReader.readAsText(file);
}

btConvert.onclick = function() convert();
convert();
}

}
125 changes: 125 additions & 0 deletions src/VilToDrawer.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package ;
import drawer.DrawerKeymap;
import haxe.DynamicAccess;
import haxe.Json;
import tools.ERegTools;
import vial.VialKeyNames;
import vial.VialKeymap;
import vial.VialKey;
using StringTools;

/**
* ...
* @author YellowAfterlife
*/
class VilToDrawer {
public static function run(opt:VilToDrawerOpt):DrawerKeymap {
var vkm = opt.vil;
var dkLayers:DynamicAccess<DrawerLayer> = new DynamicAccess();
//
inline function getLayerName(li:Int) {
return opt.getLayerName(li, true);
}

// flatten layers and fix key positions:
var vLayers:Array<Array<VialKey>> = [];
for (i => layer in vkm.layout) {
var rows = layer.copy();
for (i => row in rows) rows[i] = row.copy();
//
for (def in opt.moveDefs) {
var key = rows[def.srcRow].splice(def.srcCol, 1)[0];
rows[def.dstRow].insert(def.dstCol, key);
}
//
var rowCount = layer.length;
var halfRowCount = rowCount >> 1;
var newRows = [for (_ in 0 ... rowCount) null];
for (rk => row in rows) {
var dk = rk;
if (opt.halfAfterHalf) {
if (dk >= halfRowCount) {
dk = (dk - halfRowCount) * 2 + 1;
if (opt.mirrorRightHalf) row.reverse();
} else {
dk *= 2;
}
}
newRows[dk] = row;
}
//
var keys = [];
for (row in newRows) for (key in row) keys.push(key);
vLayers.push(keys);
}
//
for (li => vkeys in vLayers) {
var dkeys = [];
for (k => kc in vkeys) {
var dk = kc.toDrawerKey(opt);
// is this a held key?
var mo = "MO(" + li + ")";
var lts = "LT" + li + "(";
var held = false;
for (vkeys2 in vLayers) {
if (vkeys == vkeys2) continue;
var kc2 = vkeys2[k];
if (kc2 == null) continue;
if (kc2 == mo || (kc2:String).startsWith(lts)) {
held = true;
break;
}
}
if (held) {
var dkx:DrawerKeyExt;
if (dk is String) {
dkx = { t: dk };
} else dkx = dk;
dkx.type = "held";
if (dkx.t == VialKeyNames.map["KC_TRNS"]) dkx.t = "";
dk = dkx;
}
dkeys.push(dk);
}
dkLayers[getLayerName(li)] = dkeys;
}
//
var dCombos:Array<DrawerCombo> = [];
for (vCombo in vkm.combo) {
var iResult = vCombo.length - 1;
var inKeys = [];
for (i in 0 ... iResult) if (vCombo[i].isValid()) inKeys.push(vCombo[i]);
if (inKeys.length < 2) continue;
var cResult = vCombo[iResult];
if (!cResult.isValid()) continue;

for (li => vKeys in vLayers) {
var keyPos = [];
for (key in inKeys) {
var kp = vKeys.indexOf(key);
if (kp >= 0) keyPos.push(kp); else break;
}
if (keyPos.length < inKeys.length) continue;
dCombos.push({
p: keyPos,
k: cResult.toDrawerKey(opt),
l: [getLayerName(li)],
});
}
}
//
var dkm:DrawerKeymap = {
layout: {
qmk_keyboard: opt.qmkKeyboard
},
layers: dkLayers,
combos: dCombos,
};
if (opt.qmkLayout != null && opt.qmkLayout != "") dkm.layout.qmk_layout = opt.qmkLayout;
return dkm;
}
public static function runTxt(opt:VilToDrawerOpt) {
var dkm = run(opt);
return Json.stringify(dkm, null, " ");
}
}
Loading

0 comments on commit e21ebde

Please sign in to comment.