|
|
@@ -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,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 |
| 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 |
+ |
})(); |