Like 0

mjaschen revised this gist 1 month ago · da29044

1 file changed, 47 insertions, 43 deletions

sudokupad-color-palette-cycle-keyboard.js
@@ -2,7 +2,7 @@
2 2 // @name Cycle through color palettes with dot key
3 3 // @namespace Violentmonkey Scripts
4 4 // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGoUlEQVR42rWXS2xdVxWGv7X3edx7bceJSRo7iRPioAQaoko8g6gYMMgAqQhVqpAqZqWDigESpYiZ1SktQ4QoyrQIkBgwYYJQhFRCE0vIzctpHo4d53HtWz/ie+7jnLP3YnDuTa7taxtR2NLS0ZH23utfa/3rsWWSSfM2b/vXjvxyZIC9P/UqryjuuKIGEP43SwXxgp01on9MWH33/MKby5NMGgF4Y/RXXwhs+U+BiT6faRuvjv/HMmIJJSb36Uzumi//+vGPbshrR347Enu9VLIDJ1o+yUADkMJy+fQuUN38J3nJDIQtl9xpG/laEHn/VmwHTrR8PQMJu1pFBJ97nNcdFewEUAFjDCYA9dAxLGz5ehbbgRPqkrcCkFcz31Yg6N6oTslbGaWRMqWyxfudlGihaTMqVYy1tBpt0jWPjQUM3b1BoVNeDUQY9zh56m+vGCucfPMs+75xhG+eqHB4b4jXp4HZsBpJA68bEXqvxHHE7N1ZpqamWLsRcu/POeq7d4h4HCKMB71eFBHyZsapn5zl+A/O0PykydV1OHo0pryNr6Xk8b0uUsVay/r6OjdmrxINWcbPGVDlzh8cQaUbjuK46XWbzz3RSJkDL47TWm5ivLKe5HzwcR3VwjJVNojLfI84XK7kmePiP/5JM2lhMKRrysgZS7QHvNtIHLOZNRIaRApXKVAKhfurOTPVNsZ0kkP6C0AcR1y5coVqtUocR6hqoVBAArbwxfSlbm88FeJA+NdCi1riCmDaL92UKIpYWFjg+vXrxHG8MTR97u4PABB0w27psPri3QaZ0y25p524J0nC5cuXMcb0Bdi3OPVTn/oYrxZBEVFUldAKqw3H1Hyzm2UbLzKGqakp6vU61lq896gWZ40xhGG4OwBVITAZx/bcohw0SH1E6gowqkophNtLbWZr6dNQqCpxHDMzM8P8/DxhGOK9x1pLFEWEYUir1eLBgwc455AuuTor2FzRBMeXDv4du6dC9ckYj5Jj1JpjNLJBPBZrMqbmGxwYMgzGliAIqFYfMz09TalUemp9kiTUajUWF5dYXVumve4Y8S9uqZxBP7dkPqZkUsaHbnN0z21aeYXl1nM8TI6x1DzMSmMPF256vnMmQFWZnp5GjNBoNKjValSrVVZWVmi324gINjCEYdw3BMF2JFSEzMeIQGAyxgbmODQwS8tVWMlHuVc/zNzaKW59eJELf/uARrPOerJGnmeggrWGKIo65PfbkjDYsdGoIgoqQmZjJBAC5zjYvMP+R9dpfHSBcGCM504r8x+1yVcdeQo2ELCybcruDqCTvhIKEoDmoCs52XxKPpuRP3Zo6jDPr/DC4PcZ+94oa99dYGmuyb2bS9y5WqU6/4RGPcVaQxibp0XsPwtBWRCjuMc5bi4ln8twtRxtKRgQY5BTTRj25IlQuXmGtYmHjE0MM35yH2fPnWDxwRPuXlvk7tUlFh+u0VxPGdTdAEhRf9qXG7Cckj7KIesoDQSpCGQCB9qwN4VU0FJGuX2Iz1RfYHF0iqwZgYWD48McntjH2XOfY6VW59alT7h2VSDbqRcIaKq0P2ySP8yK+l0WJOr08kyg7OBwk24HB8EHKcMrJxla/ywaZYgKWZrTqKfkmWf/oUG+8u0J4rLFe90BQG8IInnGh25lNgrHGsVXN9Z4lZwDj79M2B5CjUOkiLsIZKmjmaR9CWm2JaFuCo2TwvKBvMf6HgzGY/OYA4++2oP42ZyxHQnNrjOeALnASFrEPt9mUlXB24xKfYx9tdN4m4LKrgPkBgAikHt5NnAA6gSNPXqkifpOn9RtxIOzbfYuPU95fawA0XOfT7eOdaZ3YLYG6onh2sdlhgYd1oC1ij2eYEsOaxQbbJai1BZfi7UGGxhGl75OpBWIHJX9wtxfHc0lMBFbmpF2neMVolD5y4VhEDg90YLRFogiK2HvVPtsxRlIi978UlFMHlFOv8jSwSlu/t5x6Rdb5sFi6xuH3puzYsc9eYcuBZA0E4YGPDb2zw715VHcL5tRFIMl9W2aNU9QFsR2DVA1BDh19wPQ90MT/7zt8wwItcOFcqy0UoFWsMvbJ+t14pb5TsQQDRU86PFeHpo4dC55P0iNeUdc45WSHdzwNPMKRkCCXbpJd+Jkh+HOb36aDYYtl9xJjXnHnF94fVl99lLm2zOxqYRGAtn8mvs00ssZI4HEphJmvj2jPnvp/MLry3aSSfNu/WdLZ4a/9buA0KvKGOjwLqb9l89zcxfy3zRk9YfvPfzx/Ukmzb8BY/WXJ90QXEwAAAAASUVORK5CYII=
5 - // @version 1.0.0
5 + // @version 1.0.1
6 6 //
7 7 // @match https://sudokupad.app/*
8 8 // @grant none
@@ -11,57 +11,61 @@
11 11 // @description Add a keyboard shortcut which allows cycling through the three color palettes.
12 12 // ==/UserScript==
13 13
14 -
15 14 (function () {
16 15 "use strict";
17 16
18 - const PALETTE_BUTTON_SELECTOR = 'button.palettebutton[data-control="selectpalette"]';
17 + const PALETTE_BUTTON_SELECTOR = 'button.palettebutton[data-control="selectpalette"]';
18 +
19 + const getPaletteButton = () => {
20 + const button = document.querySelector(PALETTE_BUTTON_SELECTOR);
21 + if (!button) return null;
22 + const visible = button.offsetParent !== null || button.getClientRects().length > 0;
23 + return visible ? button : null;
24 + };
19 25
20 - // SudokuPad creates the palette button only while the color entry method is
21 - // active (ToolColor.createPaletteButton() / .removePaletteButton()), so the
22 - // presence of the button in the DOM is the mode check.
23 - const getPaletteButton = () => {
24 - const button = document.querySelector(PALETTE_BUTTON_SELECTOR);
25 - if (button === null) return null;
26 - const visible = button.offsetParent !== null || button.getClientRects().length > 0;
27 - return visible ? button : null;
28 - };
26 + const isTypingTarget = target => {
27 + if (!(target instanceof Element)) return false;
28 + if (target.isContentEditable) return true;
29 + return /^(input|textarea|select)$/i.test(target.tagName);
30 + };
29 31
30 - const isTypingTarget = target => {
31 - if (!(target instanceof Element)) return false;
32 - if (target.isContentEditable) return true;
33 - return /^(input|textarea|select)$/i.test(target.tagName);
34 - };
32 + const isDialogOpen = () => document.querySelector('.dialog-overlay') !== null;
33 + const isDotKey = event => event.key === '.' || event.code === 'NumpadDecimal';
35 34
36 - const isDialogOpen = () => document.querySelector('.dialog-overlay') !== null;
35 + const cyclePalette = () => {
36 + // Check if SudokuPad's app framework is accessible
37 + const app = window.Framework?.app;
38 + const tool = app?.tools?.color;
39 + if (tool && typeof tool.nextPalette === "function") {
40 + tool.nextPalette();
41 + return;
42 + }
37 43
38 - const isDotKey = event => event.key === '.' || event.code === 'NumpadDecimal';
44 + // Fallback: Dispatch instantaneous pointer events with release
45 + const button = getPaletteButton();
46 + if (!button) return;
39 47
40 - // The button's handler is registered via addDownEventHandler(), i.e. on
41 - // 'mousedown'/'touchstart' — element.click() would not trigger it.
42 - const pressPaletteButton = button => {
43 - button.dispatchEvent(new MouseEvent('mousedown', {
44 - bubbles: true,
45 - cancelable: true,
46 - view: window,
47 - button: 0,
48 - }));
49 - };
48 + const opts = { bubbles: true, cancelable: true, view: window, button: 0, pointerType: 'mouse' };
49 + button.dispatchEvent(new PointerEvent('pointerdown', opts));
50 + button.dispatchEvent(new MouseEvent('mousedown', opts));
51 + button.dispatchEvent(new PointerEvent('pointerup', opts));
52 + button.dispatchEvent(new MouseEvent('mouseup', opts));
53 + button.dispatchEvent(new MouseEvent('click', opts));
54 + };
50 55
51 - const handleKeyDown = event => {
52 - if (!isDotKey(event)) return;
53 - if (event.repeat) return;
54 - if (event.ctrlKey || event.altKey || event.metaKey) return;
55 - if (isTypingTarget(event.target)) return;
56 - if (isDialogOpen()) return;
56 + const handleKeyDown = event => {
57 + if (!isDotKey(event)) return;
58 + if (event.repeat) return;
59 + if (event.ctrlKey || event.altKey || event.metaKey) return;
60 + if (isTypingTarget(event.target)) return;
61 + if (isDialogOpen()) return;
57 62
58 - const button = getPaletteButton();
59 - if (button === null) return;
63 + if (getPaletteButton() === null) return;
60 64
61 - event.preventDefault();
62 - event.stopPropagation();
63 - pressPaletteButton(button);
64 - };
65 + event.preventDefault();
66 + event.stopPropagation();
67 + cyclePalette();
68 + };
65 69
66 - document.addEventListener('keydown', handleKeyDown, {capture: true});
67 - })();
70 + document.addEventListener('keydown', handleKeyDown, { capture: true });
71 + })();

mjaschen revised this gist 1 month ago · 8e7ac25

1 file changed, 67 insertions

sudokupad-color-palette-cycle-keyboard.js (file created)
@@ -0,0 +1,67 @@
1 + // ==UserScript==
2 + // @name Cycle through color palettes with dot key
3 + // @namespace Violentmonkey Scripts
4 + // @icon data:image/png;base64,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
5 + // @version 1.0.0
6 + //
7 + // @match https://sudokupad.app/*
8 + // @grant none
9 + //
10 + // @author Marcus Jaschen
11 + // @description Add a keyboard shortcut which allows cycling through the three color palettes.
12 + // ==/UserScript==
13 +
14 +
15 + (function () {
16 + "use strict";
17 +
18 + const PALETTE_BUTTON_SELECTOR = 'button.palettebutton[data-control="selectpalette"]';
19 +
20 + // SudokuPad creates the palette button only while the color entry method is
21 + // active (ToolColor.createPaletteButton() / .removePaletteButton()), so the
22 + // presence of the button in the DOM is the mode check.
23 + const getPaletteButton = () => {
24 + const button = document.querySelector(PALETTE_BUTTON_SELECTOR);
25 + if (button === null) return null;
26 + const visible = button.offsetParent !== null || button.getClientRects().length > 0;
27 + return visible ? button : null;
28 + };
29 +
30 + const isTypingTarget = target => {
31 + if (!(target instanceof Element)) return false;
32 + if (target.isContentEditable) return true;
33 + return /^(input|textarea|select)$/i.test(target.tagName);
34 + };
35 +
36 + const isDialogOpen = () => document.querySelector('.dialog-overlay') !== null;
37 +
38 + const isDotKey = event => event.key === '.' || event.code === 'NumpadDecimal';
39 +
40 + // The button's handler is registered via addDownEventHandler(), i.e. on
41 + // 'mousedown'/'touchstart' — element.click() would not trigger it.
42 + const pressPaletteButton = button => {
43 + button.dispatchEvent(new MouseEvent('mousedown', {
44 + bubbles: true,
45 + cancelable: true,
46 + view: window,
47 + button: 0,
48 + }));
49 + };
50 +
51 + const handleKeyDown = event => {
52 + if (!isDotKey(event)) return;
53 + if (event.repeat) return;
54 + if (event.ctrlKey || event.altKey || event.metaKey) return;
55 + if (isTypingTarget(event.target)) return;
56 + if (isDialogOpen()) return;
57 +
58 + const button = getPaletteButton();
59 + if (button === null) return;
60 +
61 + event.preventDefault();
62 + event.stopPropagation();
63 + pressPaletteButton(button);
64 + };
65 +
66 + document.addEventListener('keydown', handleKeyDown, {capture: true});
67 + })();