Skip to content

Instantly share code, notes, and snippets.

@folknor
Last active May 5, 2024 12:41
Show Gist options
  • Save folknor/35d406aa846a29a1e2bc0e0e5d0da4e0 to your computer and use it in GitHub Desktop.
Save folknor/35d406aa846a29a1e2bc0e0e5d0da4e0 to your computer and use it in GitHub Desktop.
Violentmonkey Script for KLE
// ==UserScript==
// @name KLE Floating Window
// @namespace folknor.KLE
// @version 1.2.2
// @author folk
// @description Quality of life fixes for KLE.
// @grant none
// @match *://www.keyboard-layout-editor.com/*
// @run-at document-idle
// @inject-into page
// @downloadURL https://gist.githubusercontent.com/folknor/35d406aa846a29a1e2bc0e0e5d0da4e0/raw/vm-kle.js
// @updateURL https://gist.githubusercontent.com/folknor/35d406aa846a29a1e2bc0e0e5d0da4e0/raw/vm-kle.js
// ==/UserScript==
const floatingWindow = document.createElement('div');
floatingWindow.style.position = 'fixed';
floatingWindow.style.top = '50px';
floatingWindow.style.left = '50px';
floatingWindow.style.width = '650px';
floatingWindow.style.height = '580px';
floatingWindow.style.background = 'white';
floatingWindow.style.border = '4px solid black';
floatingWindow.style.zIndex = '1000';
floatingWindow.style.overflow = 'hidden';
const dragHandle = document.createElement('div');
dragHandle.style.height = '12px';
dragHandle.style.background = 'gray';
dragHandle.style.cursor = 'move';
dragHandle.style.zIndex = '1600';
floatingWindow.appendChild(dragHandle);
const resizeHandle = document.createElement('div');
resizeHandle.style.width = '0';
resizeHandle.style.height = '0';
resizeHandle.style.borderLeft = '8px solid transparent';
resizeHandle.style.borderRight = '8px solid transparent';
resizeHandle.style.borderBottom = '8px solid black';
resizeHandle.style.cursor = 'nwse-resize';
resizeHandle.style.position = 'absolute';
resizeHandle.style.bottom = '-2px';
resizeHandle.style.right = '-6px';
resizeHandle.style.transform = 'rotate(135deg)';
resizeHandle.style.zIndex = '1600';
floatingWindow.appendChild(resizeHandle);
// Move the stuff
const element1 = document.querySelector('html.ng-scope body.ng-scope div#wrap div#body_all div.body ul.nav.nav-tabs');
const element2 = document.querySelector('html.ng-scope body.ng-scope div#wrap div#body_all div.body div#tab-content.col-md-12.col-lg-12.row');
const element3 = document.querySelector('html.ng-scope body.ng-scope div#wrap div#body_all div.body div.row');
floatingWindow.appendChild(element1);
floatingWindow.appendChild(element2);
floatingWindow.appendChild(element3);
setTimeout(function() {
// Remove some CSS classes from the key properties form to make it jump around less
const keyProps = document.querySelector('div#properties form');
keyProps.classList.remove("col-sm-8");
keyProps.classList.remove("col-md-5");
keyProps.classList.remove("col-lg-5");
keyProps.classList.add("col");
// Change to nav pills
let navEls = document.querySelectorAll("html.ng-scope body.ng-scope div ul.nav.nav-tabs");
navEls.forEach(el => {
el.classList.remove("nav-tabs");
el.classList.add("nav-pills");
el.classList.add("nav-justified");
el.querySelectorAll("a").forEach(anchor => {
anchor.style.paddingTop = "6px";
anchor.style.paddingBottom = "6px";
anchor.style.paddingLeft = "8px";
anchor.style.paddingRight = "8px";
})
});
let tabContent = document.getElementById("tab-content");
tabContent.style.borderBottom = "none";
tabContent.style.borderRight = "none";
tabContent.style.borderLeft = "none";
tabContent.style.paddingBottom = "0px";
}, 500);
document.body.appendChild(floatingWindow);
function isElementVisible(elm) {
const rect = elm.getBoundingClientRect();
return rect.top >= 0 && rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth);
}
let isDragging = false;
let offsetX, offsetY;
// Make sure the window is moved back if moved outside the bounds gg
setInterval(() => {
const dragHandleHidden = !isElementVisible(dragHandle);
if (dragHandleHidden) {
const rect = dragHandle.getBoundingClientRect();
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
if (rect.bottom < 0 || rect.top > windowHeight || rect.right < 0 || rect.left > windowWidth) {
floatingWindow.style.left = `${Math.max(0, Math.min(windowWidth - floatingWindow.offsetWidth, parseFloat(floatingWindow.style.left)))}px`;
floatingWindow.style.top = `${Math.max(0, Math.min(windowHeight - floatingWindow.offsetHeight, parseFloat(floatingWindow.style.top)))}px`;
}
}
}, 1000);
dragHandle.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - floatingWindow.getBoundingClientRect().left;
offsetY = e.clientY - floatingWindow.getBoundingClientRect().top;
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
floatingWindow.style.left = `${e.clientX - offsetX}px`;
floatingWindow.style.top = `${e.clientY - offsetY}px`;
}
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
let isResizing = false;
resizeHandle.addEventListener('mousedown', (e) => {
isResizing = true;
const rect = floatingWindow.getBoundingClientRect();
offsetX = e.clientX - rect.right;
offsetY = e.clientY - rect.bottom;
});
document.addEventListener('mousemove', (e) => {
if (isResizing) {
floatingWindow.style.width = `${e.clientX - floatingWindow.getBoundingClientRect().left + offsetX}px`;
floatingWindow.style.height = `${e.clientY - floatingWindow.getBoundingClientRect().top + offsetY}px`;
}
});
document.addEventListener('mouseup', () => {
isResizing = false;
});
document.addEventListener('keyup', function(event) {
if (event.key === 'Enter') {
var num = -1;
try {
num = parseInt(document.querySelector('.key.selected .keylabels').firstElementChild.className.match(/\d+/)[0]);
} catch (err) {
// noop
}
if (num == -1 || typeof num === 'undefined') {
num = 0;
}
const inputBox = document.getElementById(`labeleditor${num}`);
if (inputBox) {
event.stopPropagation();
event.stopImmediatePropagation();
// If we have focus already, then select the next or previous key
if (document.activeElement && document.activeElement == inputBox) {
var ev;
if (event.shiftKey) {
ev = new KeyboardEvent('keydown', {
key: 'j',
keyCode: 74,
bubbles: true
});
} else {
ev = new KeyboardEvent('keydown', {
key: 'k',
keyCode: 75,
bubbles: true
});
}
document.querySelector('.key.selected').dispatchEvent(ev);
} else {
inputBox.setSelectionRange(0, inputBox.value.length)
inputBox.focus();
}
}
}
});
@folknor
Copy link
Author

folknor commented Apr 27, 2024

Install instructions

  1. install violentmonkey https://violentmonkey.github.io/ in firefox or chrome, not tested in others
  2. open VM prefs and go to Installed Scripts section on the left
  3. click small plus icon in top left
  4. pick install from URL
  5. copy and paste https://gist.githubusercontent.com/folknor/35d406aa846a29a1e2bc0e0e5d0da4e0/raw/vm-kle.js into the violentmonkey input box
  6. a new tab/window will open in your browser where you need to click the green button on the left that says "Install (Ctrl-Enter)"

Then any time you open a KLE this script will run

@folknor
Copy link
Author

folknor commented Apr 27, 2024

vm-kle

@folknor
Copy link
Author

folknor commented Apr 27, 2024

v1.2.2 20240505 14:30 CET

  • Changed a few CSS styles so that things look a bit better
  • Window will now move itself back into the viewport if the drag handle ends up outside the viewport. This is just checked every 1 second.
  • Resize handle is now always above the other content, so you can click it
  • While a key is selected (red outline) in the KLE, pressing Enter will now select all text and focus input to the first legend input box that has any content.
  • Enter: If no legend is set in any of the 12 boxes, it will put focus to the top left legend.
  • Enter: While your keyboard focus is in one of the legend input boxes, if you hit Enter it will jump to the next legend or the next key in the KLE
  • Unless Shift is held, in which case it will jump to the previous key in the KLE
  • This might seen difficult to understand but to me with initial testing it feels quite intuitive

v1.2 20240427 19:36 CET

  • Attempt to make the script auto update in violentmonkey

v1.1 20240427 19:26 CET

  • Redesigned resize handle slightly
  • Fixed color pickers were below the "window"
  • Resizing the window doesn't instantly change the size so much, but still a little lulz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment