| 1 | // ==UserScript== |
| 2 | // @name Cycle through color palettes with dot key |
| 3 | // @namespace Violentmonkey Scripts |
| 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.1 |
| 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 | (function () { |
| 15 | "use strict"; |
| 16 | |
| 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 | }; |
| 25 | |
| 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 | }; |
| 31 | |
| 32 | const isDialogOpen = () => document.querySelector('.dialog-overlay') !== null; |
| 33 | const isDotKey = event => event.key === '.' || event.code === 'NumpadDecimal'; |
| 34 | |
| 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 | } |
| 43 | |
| 44 | // Fallback: Dispatch instantaneous pointer events with release |
| 45 | const button = getPaletteButton(); |
| 46 | if (!button) return; |
| 47 | |
| 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 | }; |
| 55 | |
| 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; |
| 62 | |
| 63 | if (getPaletteButton() === null) return; |
| 64 | |
| 65 | event.preventDefault(); |
| 66 | event.stopPropagation(); |
| 67 | cyclePalette(); |
| 68 | }; |
| 69 | |
| 70 | document.addEventListener('keydown', handleKeyDown, { capture: true }); |
| 71 | })(); |
| 72 |
mjaschen / Sudokupad: Cycle Color Palette with Dot Key
Last active 1 month ago
Revision da29044bd3d0e21febb93254e446ce58cdbb7196