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

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