@lvl99dotcom/paintcanvas 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/lib/brushes/StrokeEngine/index.d.ts +2 -2
- package/dist/lib/brushes/StrokeEngine/index.js +26 -28
- package/dist/lib/classes/BrushSettings/index.d.ts +24 -0
- package/dist/lib/classes/BrushSettings/index.js +35 -1
- package/dist/lib/classes/KeyboardShortcuts/index.d.ts +17 -6
- package/dist/lib/classes/KeyboardShortcuts/index.js +81 -18
- package/dist/lib/classes/PaintCanvas/index.d.ts +77 -27
- package/dist/lib/classes/PaintCanvas/index.js +141 -41
- package/dist/lib/classes/PaintCanvas/tool.d.ts +15 -0
- package/dist/lib/classes/PaintCanvas/tool.js +18 -0
- package/dist/lib/classes/Palette/index.d.ts +58 -0
- package/dist/lib/classes/Palette/index.js +92 -0
- package/dist/lib/classes/SettingsAutosave/index.d.ts +52 -0
- package/dist/lib/classes/SettingsAutosave/index.js +96 -0
- package/dist/lib/classes/ShortcutSettings/index.d.ts +63 -0
- package/dist/lib/classes/ShortcutSettings/index.js +110 -0
- package/dist/lib/palette/index.d.ts +49 -0
- package/dist/lib/palette/index.js +82 -0
- package/dist/lib/palette/schema.d.ts +45 -0
- package/dist/lib/palette/schema.js +41 -0
- package/dist/lib/rendering/cursors/index.d.ts +2 -0
- package/dist/lib/rendering/cursors/index.js +3 -1
- package/dist/lib/rendering/sample/index.js +3 -2
- package/dist/lib/settings/index.d.ts +63 -0
- package/dist/lib/settings/index.js +91 -0
- package/dist/lib/settings/schema.d.ts +150 -0
- package/dist/lib/settings/schema.js +102 -0
- package/dist/lib/storage/index.d.ts +36 -5
- package/dist/lib/storage/index.js +87 -29
- package/dist/lib/utils/colour/index.d.ts +72 -0
- package/dist/lib/utils/colour/index.js +154 -0
- package/dist/lib/utils/shortcuts/index.d.ts +174 -11
- package/dist/lib/utils/shortcuts/index.js +391 -145
- package/dist/lib/validation/validators.generated.d.ts +5 -0
- package/dist/lib/validation/validators.generated.js +872 -1
- package/dist/locales/en-GB.js +87 -1
- package/dist/locales/en-US.js +87 -1
- package/dist/locales/fr.js +87 -1
- package/dist/locales/ja.js +87 -1
- package/dist/locales/ko.js +87 -1
- package/dist/locales/zh-Hans.js +87 -1
- package/dist/locales/zh-Hant.js +87 -1
- package/dist/package.js +1 -1
- package/dist/ui/elements/PaintCanvas/index.d.ts +49 -47
- package/dist/ui/elements/PaintCanvas/index.js +121 -171
- package/dist/ui/elements/PaintCanvas/style.js +1 -1
- package/dist/ui/elements/PcAppSettings/index.d.ts +59 -0
- package/dist/ui/elements/PcAppSettings/index.js +222 -0
- package/dist/ui/elements/PcCanvas/index.d.ts +1 -1
- package/dist/ui/elements/PcCanvas/index.js +7 -5
- package/dist/ui/elements/PcColourPair/index.d.ts +43 -0
- package/dist/ui/elements/PcColourPair/index.js +125 -0
- package/dist/ui/elements/PcContextMenu/index.d.ts +70 -33
- package/dist/ui/elements/PcContextMenu/index.js +128 -97
- package/dist/ui/elements/PcLayerItem/index.d.ts +13 -34
- package/dist/ui/elements/PcLayerItem/index.js +39 -91
- package/dist/ui/elements/PcLayerItem/style.js +1 -1
- package/dist/ui/elements/PcMenu/index.d.ts +93 -0
- package/dist/ui/elements/PcMenu/index.js +204 -0
- package/dist/ui/elements/PcPane/index.d.ts +118 -0
- package/dist/ui/elements/PcPane/index.js +357 -0
- package/dist/ui/elements/PcPanel/index.d.ts +26 -8
- package/dist/ui/elements/PcPanel/index.js +76 -8
- package/dist/ui/elements/{PcBrushMenu → PcPanelBrushes}/index.d.ts +20 -6
- package/dist/ui/elements/{PcBrushMenu → PcPanelBrushes}/index.js +18 -14
- package/dist/ui/elements/PcPanelBrushes/style.js +3 -0
- package/dist/ui/elements/PcPanelColours/index.d.ts +171 -0
- package/dist/ui/elements/PcPanelColours/index.js +650 -0
- package/dist/ui/elements/PcPanelColours/style.js +3 -0
- package/dist/ui/elements/{PcLayersMenu → PcPanelLayers}/index.d.ts +17 -4
- package/dist/ui/elements/{PcLayersMenu → PcPanelLayers}/index.js +17 -10
- package/dist/ui/elements/PcPanelLayers/style.js +3 -0
- package/dist/ui/elements/PcShortcutSettings/index.d.ts +69 -0
- package/dist/ui/elements/PcShortcutSettings/index.js +273 -0
- package/dist/ui/elements/PcToolSettings/index.js +2 -1
- package/dist/ui/elements/PcToolbar/index.d.ts +47 -12
- package/dist/ui/elements/PcToolbar/index.js +77 -56
- package/dist/ui/elements/PcToolbar/style.js +1 -1
- package/dist/ui/index.d.ts +7 -1
- package/dist/ui/index.js +8 -2
- package/dist/ui/tokens/index.js +3 -1
- package/locales/en-GB.json +87 -1
- package/locales/en-US.json +87 -1
- package/locales/fr.json +87 -1
- package/locales/ja.json +87 -1
- package/locales/ko.json +87 -1
- package/locales/zh-Hans.json +87 -1
- package/locales/zh-Hant.json +87 -1
- package/package.json +1 -1
- package/dist/ui/elements/PcBrushMenu/style.js +0 -3
- package/dist/ui/elements/PcLayersMenu/style.js +0 -3
|
@@ -1,150 +1,224 @@
|
|
|
1
|
-
import { Tool } from "../../classes/PaintCanvas/
|
|
1
|
+
import { Tool } from "../../classes/PaintCanvas/tool.js";
|
|
2
2
|
// src/lib/utils/shortcuts/index.ts
|
|
3
|
-
/**
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{
|
|
3
|
+
/** ID of the action run while its key is held: pan, returning to the last tool when released */
|
|
4
|
+
var HOLD_TO_PAN = "pan";
|
|
5
|
+
/**
|
|
6
|
+
* Define a shortcut action.
|
|
7
|
+
*/
|
|
8
|
+
var define = (id, group, action, combos, configurable = true) => ({
|
|
9
|
+
id,
|
|
10
|
+
group,
|
|
11
|
+
action,
|
|
12
|
+
combos,
|
|
13
|
+
configurable
|
|
14
|
+
});
|
|
15
|
+
/** Define a configurable tool shortcut */
|
|
16
|
+
var tool = (id, toolName, combo) => define(id, "tools", {
|
|
17
|
+
type: "tool",
|
|
18
|
+
tool: toolName
|
|
19
|
+
}, [combo]);
|
|
20
|
+
/** Layer opacity shortcuts: 1 for 10% to 9 for 90%, and 0 for 100% */
|
|
21
|
+
var LAYER_OPACITY_SHORTCUTS = Array.from({ length: 10 }, (_, i) => define(`layer-opacity-${(i + 1) * 10}`, "layers", {
|
|
22
|
+
type: "layer-opacity",
|
|
23
|
+
opacity: (i + 1) / 10
|
|
24
|
+
}, [{ key: String((i + 1) % 10) }], false));
|
|
25
|
+
/** Every keyboard shortcut action, in the order they are listed */
|
|
26
|
+
var SHORTCUT_DEFINITIONS = [
|
|
27
|
+
define("pan", "tools", { type: "pan" }, [{ key: " " }]),
|
|
28
|
+
tool("tool-move", Tool.Move, { key: "v" }),
|
|
29
|
+
tool("tool-brush", Tool.Brush, { key: "b" }),
|
|
30
|
+
tool("tool-eraser", Tool.Eraser, { key: "e" }),
|
|
31
|
+
tool("tool-rectangle-mask", Tool.RectangleLasso, { key: "m" }),
|
|
32
|
+
tool("tool-circle-mask", Tool.CircleLasso, {
|
|
34
33
|
key: "m",
|
|
35
|
-
shift: true
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
key: "l",
|
|
43
|
-
action: {
|
|
44
|
-
type: "tool",
|
|
45
|
-
tool: Tool.PolygonLasso
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
{
|
|
34
|
+
shift: true
|
|
35
|
+
}),
|
|
36
|
+
tool("tool-polygon-mask", Tool.PolygonLasso, { key: "l" }),
|
|
37
|
+
tool("tool-freehand-mask", Tool.FreeformLasso, {
|
|
49
38
|
key: "l",
|
|
50
|
-
shift: true
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
type: "tool",
|
|
60
|
-
tool: Tool.Pan
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
key: "z",
|
|
65
|
-
action: {
|
|
66
|
-
type: "tool",
|
|
67
|
-
tool: Tool.Zoom
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
key: "i",
|
|
72
|
-
action: {
|
|
73
|
-
type: "tool",
|
|
74
|
-
tool: Tool.ColourSample
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
key: "[",
|
|
79
|
-
action: { type: "brush-smaller" }
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
key: "]",
|
|
83
|
-
action: { type: "brush-bigger" }
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
key: "tab",
|
|
87
|
-
action: { type: "toggle-interface" }
|
|
88
|
-
},
|
|
89
|
-
{
|
|
39
|
+
shift: true
|
|
40
|
+
}),
|
|
41
|
+
tool("tool-colour-sample", Tool.ColourSample, { key: "i" }),
|
|
42
|
+
tool("tool-zoom", Tool.Zoom, { key: "z" }),
|
|
43
|
+
define("brush-bigger", "brushes", { type: "brush-bigger" }, [{ key: "]" }]),
|
|
44
|
+
define("brush-smaller", "brushes", { type: "brush-smaller" }, [{ key: "[" }]),
|
|
45
|
+
define("brush-more-opaque", "brushes", { type: "brush-more-opaque" }, [{ key: ">" }]),
|
|
46
|
+
define("brush-less-opaque", "brushes", { type: "brush-less-opaque" }, [{ key: "<" }]),
|
|
47
|
+
define("select-all", "masking", { type: "select-all" }, [{
|
|
90
48
|
key: "a",
|
|
49
|
+
cmd: true
|
|
50
|
+
}]),
|
|
51
|
+
define("invert-selection", "masking", { type: "invert-selection" }, [{
|
|
52
|
+
key: "i",
|
|
91
53
|
cmd: true,
|
|
92
|
-
|
|
93
|
-
},
|
|
94
|
-
{
|
|
54
|
+
shift: true
|
|
55
|
+
}]),
|
|
56
|
+
define("deselect", "masking", { type: "deselect" }, [{
|
|
95
57
|
key: "d",
|
|
96
|
-
cmd: true
|
|
97
|
-
|
|
98
|
-
},
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
key: "z",
|
|
106
|
-
cmd: true,
|
|
107
|
-
action: { type: "undo" }
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
key: "z",
|
|
111
|
-
cmd: true,
|
|
112
|
-
shift: true,
|
|
113
|
-
action: { type: "redo" }
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
key: "x",
|
|
117
|
-
cmd: true,
|
|
118
|
-
action: { type: "cut" }
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
key: "delete",
|
|
122
|
-
action: { type: "delete" }
|
|
123
|
-
},
|
|
124
|
-
{
|
|
58
|
+
cmd: true
|
|
59
|
+
}]),
|
|
60
|
+
define("fill-foreground", "fills", { type: "fill-foreground" }, [{
|
|
61
|
+
key: "backspace",
|
|
62
|
+
cmd: true
|
|
63
|
+
}]),
|
|
64
|
+
define("fill-background", "fills", { type: "fill-background" }, [{
|
|
125
65
|
key: "backspace",
|
|
126
|
-
|
|
127
|
-
},
|
|
128
|
-
{
|
|
66
|
+
alt: true
|
|
67
|
+
}]),
|
|
68
|
+
define("swap-colours", "fills", { type: "swap-colours" }, [{ key: "x" }]),
|
|
69
|
+
define("zoom-in", "viewport", { type: "zoom-in" }, [{
|
|
70
|
+
key: "+",
|
|
71
|
+
cmd: true
|
|
72
|
+
}]),
|
|
73
|
+
define("zoom-out", "viewport", { type: "zoom-out" }, [{
|
|
74
|
+
key: "-",
|
|
75
|
+
cmd: true
|
|
76
|
+
}]),
|
|
77
|
+
define("reset-zoom", "viewport", { type: "reset-zoom" }, [{
|
|
78
|
+
key: "0",
|
|
79
|
+
cmd: true
|
|
80
|
+
}]),
|
|
81
|
+
...LAYER_OPACITY_SHORTCUTS,
|
|
82
|
+
define("cut", "clipboard", { type: "cut" }, [{
|
|
83
|
+
key: "x",
|
|
84
|
+
cmd: true
|
|
85
|
+
}], false),
|
|
86
|
+
define("copy", "clipboard", { type: "copy" }, [{
|
|
129
87
|
key: "c",
|
|
130
|
-
cmd: true
|
|
131
|
-
|
|
132
|
-
},
|
|
133
|
-
{
|
|
88
|
+
cmd: true
|
|
89
|
+
}], false),
|
|
90
|
+
define("paste", "clipboard", { type: "paste" }, [{
|
|
134
91
|
key: "v",
|
|
92
|
+
cmd: true
|
|
93
|
+
}], false),
|
|
94
|
+
define("undo", "history", { type: "undo" }, [{
|
|
95
|
+
key: "z",
|
|
96
|
+
cmd: true
|
|
97
|
+
}], false),
|
|
98
|
+
define("redo", "history", { type: "redo" }, [{
|
|
99
|
+
key: "z",
|
|
135
100
|
cmd: true,
|
|
136
|
-
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
101
|
+
shift: true
|
|
102
|
+
}], false),
|
|
103
|
+
define("toggle-interface", "general", { type: "toggle-interface" }, [{ key: "tab" }], false),
|
|
104
|
+
define("save", "general", { type: "save" }, [{
|
|
105
|
+
key: "s",
|
|
106
|
+
cmd: true
|
|
107
|
+
}], false),
|
|
108
|
+
define("delete", "general", { type: "delete" }, [{ key: "delete" }, { key: "backspace" }], false),
|
|
109
|
+
define("cancel", "general", { type: "cancel" }, [{ key: "escape" }], false),
|
|
110
|
+
define("commit", "general", { type: "commit" }, [{ key: "enter" }], false)
|
|
146
111
|
];
|
|
147
|
-
/**
|
|
112
|
+
/** The actions users can change the key combinations of */
|
|
113
|
+
var CONFIGURABLE_SHORTCUTS = SHORTCUT_DEFINITIONS.filter((definition) => definition.configurable);
|
|
114
|
+
/** The actions whose key combinations can't be changed */
|
|
115
|
+
var FIXED_SHORTCUTS = SHORTCUT_DEFINITIONS.filter((definition) => !definition.configurable);
|
|
116
|
+
/** Longest key name in a saved key combination (characters) */
|
|
117
|
+
var MAX_KEY_LENGTH = 32;
|
|
118
|
+
/**
|
|
119
|
+
* A key combination with only the modifiers that are on, e.g. to compare or save it.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* normaliseCombo({ key: "s", cmd: true, shift: false }); // { key: "s", cmd: true }
|
|
123
|
+
*/
|
|
124
|
+
var normaliseCombo = ({ key, cmd, ctrl, shift, alt }) => ({
|
|
125
|
+
key,
|
|
126
|
+
...cmd && { cmd: true },
|
|
127
|
+
...ctrl && { ctrl: true },
|
|
128
|
+
...shift && { shift: true },
|
|
129
|
+
...alt && { alt: true }
|
|
130
|
+
});
|
|
131
|
+
/**
|
|
132
|
+
* Whether two key combinations are the same.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* sameCombo({ key: "s", cmd: true }, { key: "s", cmd: true, shift: false }); // true
|
|
136
|
+
*/
|
|
137
|
+
var sameCombo = (a, b) => a.key === b.key && Boolean(a.cmd) === Boolean(b.cmd) && Boolean(a.ctrl) === Boolean(b.ctrl) && Boolean(a.shift) === Boolean(b.shift) && Boolean(a.alt) === Boolean(b.alt);
|
|
138
|
+
/**
|
|
139
|
+
* Keep a valid key combination from an untrusted value (e.g. stored or imported). Undefined if it isn't one.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* sanitiseCombo({ key: "s", cmd: true, extra: 1 }); // { key: "s", cmd: true }
|
|
143
|
+
* sanitiseCombo({ key: "" }); // undefined
|
|
144
|
+
*/
|
|
145
|
+
var sanitiseCombo = (input) => {
|
|
146
|
+
if (typeof input !== "object" || input === null) return void 0;
|
|
147
|
+
const { key, cmd, ctrl, shift, alt } = input;
|
|
148
|
+
if (typeof key !== "string" || key.length === 0 || key.length > MAX_KEY_LENGTH || key !== key.toLowerCase() || ![
|
|
149
|
+
cmd,
|
|
150
|
+
ctrl,
|
|
151
|
+
shift,
|
|
152
|
+
alt
|
|
153
|
+
].every((flag) => flag === void 0 || typeof flag === "boolean")) return void 0;
|
|
154
|
+
return normaliseCombo({
|
|
155
|
+
key,
|
|
156
|
+
cmd,
|
|
157
|
+
ctrl,
|
|
158
|
+
shift,
|
|
159
|
+
alt
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* The default key combination of each configurable action.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* defaultShortcutBindings()["tool-brush"]; // { key: "b" }
|
|
167
|
+
*/
|
|
168
|
+
var defaultShortcutBindings = () => Object.fromEntries(CONFIGURABLE_SHORTCUTS.map(({ id, combos }) => [id, normaliseCombo(combos[0])]));
|
|
169
|
+
/**
|
|
170
|
+
* The fixed action that uses a key combination, if any.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* fixedShortcutFor({ key: "z", cmd: true })?.id; // "undo"
|
|
174
|
+
*/
|
|
175
|
+
var fixedShortcutFor = (combo) => FIXED_SHORTCUTS.find(({ combos }) => combos.some((fixed) => sameCombo(fixed, combo)));
|
|
176
|
+
/**
|
|
177
|
+
* Keep valid key combinations from an untrusted value (e.g. stored or imported), using `defaults` for actions that are
|
|
178
|
+
* missing or invalid. `null` removes an action's key combination. A key combination a fixed action uses, or that an
|
|
179
|
+
* earlier action (in `SHORTCUT_DEFINITIONS` order) already has, is removed. Unknown actions are left out.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* sanitiseShortcutBindings({ "tool-brush": { key: "p" }, cut: null }, defaultShortcutBindings());
|
|
183
|
+
*/
|
|
184
|
+
var sanitiseShortcutBindings = (input, defaults) => {
|
|
185
|
+
const given = typeof input === "object" && input !== null ? input : {};
|
|
186
|
+
const used = [];
|
|
187
|
+
return Object.fromEntries(CONFIGURABLE_SHORTCUTS.map(({ id }) => {
|
|
188
|
+
const value = given[id];
|
|
189
|
+
const combo = value === null ? null : sanitiseCombo(value) ?? defaults[id] ?? null;
|
|
190
|
+
if (!combo || fixedShortcutFor(combo) || used.some((other) => sameCombo(other, combo))) return [id, null];
|
|
191
|
+
used.push(combo);
|
|
192
|
+
return [id, combo];
|
|
193
|
+
}));
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* The shortcuts to match key presses against: the configurable actions' key combinations (except the hold-to-pan key,
|
|
197
|
+
* which acts while held) and the fixed ones.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* shortcutsFor(defaultShortcutBindings()).find((s) => s.key === "b")?.action; // { type: "tool", tool: Tool.Brush }
|
|
201
|
+
*/
|
|
202
|
+
var shortcutsFor = (bindings) => [...CONFIGURABLE_SHORTCUTS.flatMap(({ id, action }) => {
|
|
203
|
+
const combo = bindings[id];
|
|
204
|
+
return combo && id !== "pan" ? [{
|
|
205
|
+
...combo,
|
|
206
|
+
action
|
|
207
|
+
}] : [];
|
|
208
|
+
}), ...FIXED_SHORTCUTS.flatMap(({ action, combos }) => combos.map((combo) => ({
|
|
209
|
+
...combo,
|
|
210
|
+
action
|
|
211
|
+
})))];
|
|
212
|
+
/** All keyboard shortcuts with their default key combinations */
|
|
213
|
+
var SHORTCUTS = shortcutsFor(defaultShortcutBindings());
|
|
214
|
+
/**
|
|
215
|
+
* Whether a platform (`navigator.platform` by default) is an Apple device, where "cmd" is the Command key.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* isApplePlatform("MacIntel"); // true
|
|
219
|
+
* isApplePlatform("Win32"); // false
|
|
220
|
+
*/
|
|
221
|
+
var isApplePlatform = (platform = navigator.platform) => /Mac|iPhone|iPad|iPod/.test(platform);
|
|
148
222
|
/**
|
|
149
223
|
* The symbol for the "cmd" key in shortcut labels: ⌘ on Apple devices, ^ (Control) elsewhere.
|
|
150
224
|
*
|
|
@@ -152,21 +226,193 @@ var SHORTCUTS = [
|
|
|
152
226
|
* cmdSymbol("MacIntel"); // "⌘"
|
|
153
227
|
* cmdSymbol("Win32"); // "^"
|
|
154
228
|
*/
|
|
155
|
-
var cmdSymbol = (platform
|
|
156
|
-
|
|
229
|
+
var cmdSymbol = (platform) => isApplePlatform(platform) ? "⌘" : "^";
|
|
230
|
+
/** Symbols for keys in shortcut labels; other keys are shown in uppercase */
|
|
231
|
+
var KEY_SYMBOLS = {
|
|
232
|
+
backspace: "⌫",
|
|
233
|
+
delete: "⌦",
|
|
234
|
+
tab: "⇥",
|
|
235
|
+
escape: "⎋",
|
|
236
|
+
enter: "↵",
|
|
237
|
+
arrowleft: "←",
|
|
238
|
+
arrowright: "→",
|
|
239
|
+
arrowup: "↑",
|
|
240
|
+
arrowdown: "↓",
|
|
241
|
+
" ": "Space"
|
|
242
|
+
};
|
|
243
|
+
/** Names of keys for `aria-keyshortcuts`; other keys are named as they are, in uppercase */
|
|
244
|
+
var ARIA_KEY_NAMES = {
|
|
245
|
+
backspace: "Backspace",
|
|
246
|
+
delete: "Delete",
|
|
247
|
+
tab: "Tab",
|
|
248
|
+
escape: "Escape",
|
|
249
|
+
enter: "Enter",
|
|
250
|
+
arrowleft: "ArrowLeft",
|
|
251
|
+
arrowright: "ArrowRight",
|
|
252
|
+
arrowup: "ArrowUp",
|
|
253
|
+
arrowdown: "ArrowDown",
|
|
254
|
+
" ": "Space"
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* A short label for a key combination, e.g. for menus: modifier symbols (^ Control, ⌥ Alt, ⇧ Shift, then ⌘ for "cmd" on
|
|
258
|
+
* Apple devices or ^ elsewhere), then the key as a symbol or in uppercase.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* shortcutLabel({ key: "z", cmd: true, shift: true }, "MacIntel"); // "⇧⌘Z"
|
|
262
|
+
* shortcutLabel({ key: "backspace", alt: true }, "Win32"); // "⌥⌫"
|
|
263
|
+
*/
|
|
264
|
+
var shortcutLabel = (combo, platform) => [
|
|
265
|
+
combo.ctrl && "^",
|
|
266
|
+
combo.alt && "⌥",
|
|
267
|
+
combo.shift && "⇧",
|
|
268
|
+
combo.cmd && cmdSymbol(platform),
|
|
269
|
+
KEY_SYMBOLS[combo.key] ?? combo.key.toUpperCase()
|
|
270
|
+
].filter(Boolean).join("");
|
|
271
|
+
/**
|
|
272
|
+
* A key combination as an `aria-keyshortcuts` value, so screen readers can announce it.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ariaKeyShortcut({ key: "s", cmd: true }, "MacIntel"); // "Meta+S"
|
|
276
|
+
* ariaKeyShortcut({ key: "backspace", alt: true }, "Win32"); // "Alt+Backspace"
|
|
277
|
+
*/
|
|
278
|
+
var ariaKeyShortcut = (combo, platform) => [
|
|
279
|
+
(combo.ctrl || combo.cmd && !isApplePlatform(platform)) && "Control",
|
|
280
|
+
combo.cmd && isApplePlatform(platform) && "Meta",
|
|
281
|
+
combo.alt && "Alt",
|
|
282
|
+
combo.shift && "Shift",
|
|
283
|
+
ARIA_KEY_NAMES[combo.key] ?? combo.key.toUpperCase()
|
|
284
|
+
].filter(Boolean).join("+");
|
|
285
|
+
/** Keys that are only modifiers, which don't make a key combination on their own (lowercase) */
|
|
286
|
+
var MODIFIER_KEYS = /* @__PURE__ */ new Set([
|
|
287
|
+
"shift",
|
|
288
|
+
"control",
|
|
289
|
+
"alt",
|
|
290
|
+
"altgraph",
|
|
291
|
+
"meta",
|
|
292
|
+
"os",
|
|
293
|
+
"super",
|
|
294
|
+
"hyper",
|
|
295
|
+
"fn",
|
|
296
|
+
"fnlock",
|
|
297
|
+
"capslock",
|
|
298
|
+
"numlock",
|
|
299
|
+
"scrolllock",
|
|
300
|
+
"symbol",
|
|
301
|
+
"symbollock"
|
|
302
|
+
]);
|
|
303
|
+
/** Characters of physical keys (by `KeyboardEvent.code`) other than letters and digits, as on a US keyboard */
|
|
304
|
+
var CODE_CHARACTERS = {
|
|
305
|
+
Minus: "-",
|
|
306
|
+
Equal: "=",
|
|
307
|
+
BracketLeft: "[",
|
|
308
|
+
BracketRight: "]",
|
|
309
|
+
Backslash: "\\",
|
|
310
|
+
Semicolon: ";",
|
|
311
|
+
Quote: "'",
|
|
312
|
+
Backquote: "`",
|
|
313
|
+
Comma: ",",
|
|
314
|
+
Period: ".",
|
|
315
|
+
Slash: "/",
|
|
316
|
+
IntlBackslash: "<"
|
|
317
|
+
};
|
|
318
|
+
/** Keys treated as another key: `=` (typed without Shift on many keyboards) as `+` */
|
|
319
|
+
var KEY_ALIASES = { "=": "+" };
|
|
157
320
|
/**
|
|
158
|
-
*
|
|
321
|
+
* The character of a physical key (by `KeyboardEvent.code`) as on a US keyboard, e.g. `KeyA` → `a`, `Digit5` → `5`.
|
|
159
322
|
*
|
|
160
323
|
* @example
|
|
161
|
-
*
|
|
324
|
+
* codeCharacter("KeyG"); // "g"
|
|
325
|
+
* codeCharacter("BracketLeft"); // "["
|
|
326
|
+
*/
|
|
327
|
+
var codeCharacter = (code) => {
|
|
328
|
+
const character = /^(?:Key([A-Z])|Digit(\d)|Numpad(\d))$/.exec(code ?? "")?.slice(1).find(Boolean);
|
|
329
|
+
if (character) return character.toLowerCase();
|
|
330
|
+
return code ? CODE_CHARACTERS[code] : void 0;
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* Whether a key is one printable basic Latin (ASCII) character.
|
|
334
|
+
*/
|
|
335
|
+
var isBasicCharacter = (key) => key.length === 1 && key >= " " && key <= "~";
|
|
336
|
+
/**
|
|
337
|
+
* The key combination of a key press, matching keyboards with different layouts:
|
|
338
|
+
* - Keys are matched by the character they type, so letters follow the key's label (e.g. `a` on a French keyboard).
|
|
339
|
+
* - Digits and symbols ignore Shift and AltGr/Option, which are often needed to type them (e.g. `<` and `[` on a
|
|
340
|
+
* German keyboard).
|
|
341
|
+
* - When a key types a character that isn't basic Latin (another alphabet, or Option on macOS), or is a dead key, the
|
|
342
|
+
* physical key is used instead (e.g. `a` for `ф`, and `alt g` for Option+G typing `©`).
|
|
343
|
+
* - `=` counts as `+`, so `cmd +` works with or without Shift.
|
|
344
|
+
*
|
|
345
|
+
* `cmd` is Command on Apple devices and Ctrl elsewhere; `ctrl` is Control on Apple devices. Undefined while composing
|
|
346
|
+
* text (e.g. with an input method), for modifier keys alone, and for the Windows key elsewhere.
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* comboFromEvent({ key: "Z", metaKey: true, ctrlKey: false, shiftKey: true, altKey: false }, "MacIntel");
|
|
350
|
+
* // { key: "z", cmd: true, shift: true }
|
|
351
|
+
*/
|
|
352
|
+
var comboFromEvent = (event, platform) => {
|
|
353
|
+
const apple = isApplePlatform(platform);
|
|
354
|
+
const typed = event.key.toLowerCase();
|
|
355
|
+
if (event.isComposing || typed === "process" || typed === "unidentified" || MODIFIER_KEYS.has(typed) || !apple && event.metaKey) return void 0;
|
|
356
|
+
const character = [...event.key].length === 1;
|
|
357
|
+
const physical = character && !isBasicCharacter(typed) || typed === "dead" ? codeCharacter(event.code) : void 0;
|
|
358
|
+
if (typed === "dead" && !physical) return void 0;
|
|
359
|
+
const key = KEY_ALIASES[physical ?? typed] ?? physical ?? typed;
|
|
360
|
+
const altGraph = event.getModifierState?.("AltGraph") ?? false;
|
|
361
|
+
const typedSymbol = character && !physical && !/^[a-z ]$/.test(key);
|
|
362
|
+
return normaliseCombo({
|
|
363
|
+
key,
|
|
364
|
+
cmd: apple ? event.metaKey : event.ctrlKey && !altGraph,
|
|
365
|
+
ctrl: apple && event.ctrlKey,
|
|
366
|
+
shift: event.shiftKey && !typedSymbol,
|
|
367
|
+
alt: event.altKey && !altGraph && !typedSymbol
|
|
368
|
+
});
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Find the action for a key press (see `comboFromEvent`). Modifier keys must match exactly.
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* matchShortcut({ key: "Z", metaKey: true, ctrlKey: false, shiftKey: true, altKey: false }, SHORTCUTS, "MacIntel");
|
|
162
375
|
* // { type: "redo" }
|
|
163
376
|
*/
|
|
164
|
-
var matchShortcut = (event, shortcuts = SHORTCUTS) => {
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
return shortcuts.find((s) => s.key === key && Boolean(s.cmd) === cmd && Boolean(s.shift) === event.shiftKey && Boolean(s.alt) === event.altKey)?.action;
|
|
377
|
+
var matchShortcut = (event, shortcuts = SHORTCUTS, platform) => {
|
|
378
|
+
const combo = comboFromEvent(event, platform);
|
|
379
|
+
return combo && shortcuts.find((shortcut) => sameCombo(shortcut, combo))?.action;
|
|
168
380
|
};
|
|
169
381
|
/**
|
|
382
|
+
* The modifier keys held during a key press, as a key combination without a key (e.g. to show them while recording a
|
|
383
|
+
* key combination). Read like `comboFromEvent`: `cmd` is Command on Apple devices and Ctrl elsewhere.
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* heldModifiers({ key: "Shift", metaKey: true, ctrlKey: false, shiftKey: true, altKey: false }, "MacIntel");
|
|
387
|
+
* // { key: "", cmd: true, shift: true }
|
|
388
|
+
*/
|
|
389
|
+
var heldModifiers = (event, platform) => {
|
|
390
|
+
const apple = isApplePlatform(platform);
|
|
391
|
+
return normaliseCombo({
|
|
392
|
+
key: "",
|
|
393
|
+
cmd: apple ? event.metaKey : event.ctrlKey,
|
|
394
|
+
ctrl: apple && event.ctrlKey,
|
|
395
|
+
shift: event.shiftKey,
|
|
396
|
+
alt: event.altKey
|
|
397
|
+
});
|
|
398
|
+
};
|
|
399
|
+
/**
|
|
400
|
+
* A key combination as words, e.g. for a text field: modifiers in the order shift, alt, cmd, ctrl, then the key
|
|
401
|
+
* (Space as `space`). "cmd" is written `ctrl` except on Apple devices.
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* comboText({ key: "g", shift: true, alt: true, cmd: true, ctrl: true }, "MacIntel"); // "shift alt cmd ctrl g"
|
|
405
|
+
* comboText({ key: "s", cmd: true }, "Win32"); // "ctrl s"
|
|
406
|
+
* comboText({ key: "", shift: true }, "MacIntel"); // "shift" (modifiers only)
|
|
407
|
+
*/
|
|
408
|
+
var comboText = (combo, platform) => [
|
|
409
|
+
combo.shift && "shift",
|
|
410
|
+
combo.alt && "alt",
|
|
411
|
+
combo.cmd && (isApplePlatform(platform) ? "cmd" : "ctrl"),
|
|
412
|
+
combo.ctrl && "ctrl",
|
|
413
|
+
combo.key === " " ? "space" : combo.key
|
|
414
|
+
].filter(Boolean).join(" ");
|
|
415
|
+
/**
|
|
170
416
|
* Check if a key press happened while typing in a text field, where shortcuts must not apply.
|
|
171
417
|
*
|
|
172
418
|
* @example
|
|
@@ -186,4 +432,4 @@ var isTypingTarget = (target) => {
|
|
|
186
432
|
].includes(target.type);
|
|
187
433
|
return false;
|
|
188
434
|
};
|
|
189
|
-
export {
|
|
435
|
+
export { CONFIGURABLE_SHORTCUTS, FIXED_SHORTCUTS, HOLD_TO_PAN, SHORTCUTS, SHORTCUT_DEFINITIONS, ariaKeyShortcut, cmdSymbol, comboFromEvent, comboText, defaultShortcutBindings, fixedShortcutFor, heldModifiers, isApplePlatform, isTypingTarget, matchShortcut, normaliseCombo, sameCombo, sanitiseCombo, sanitiseShortcutBindings, shortcutLabel, shortcutsFor };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Types for the generated validators (see validators.generated.js).
|
|
2
2
|
import type { BrushPresetFile } from "../presets/schema";
|
|
3
3
|
import type { PaintCanvasDocument } from "../document/schema";
|
|
4
|
+
import type { PaletteFile } from "../palette/schema";
|
|
5
|
+
import type { AppSettings, SettingsFile } from "../settings/schema";
|
|
4
6
|
import type {
|
|
5
7
|
HistoryMessage,
|
|
6
8
|
PatchMessage,
|
|
@@ -25,3 +27,6 @@ export declare const validatePatchMessage: Validator<PatchMessage>;
|
|
|
25
27
|
export declare const validateStrokeMessage: Validator<StrokeMessage>;
|
|
26
28
|
export declare const validateHistoryMessage: Validator<HistoryMessage>;
|
|
27
29
|
export declare const validateDocument: Validator<PaintCanvasDocument>;
|
|
30
|
+
export declare const validatePaletteFile: Validator<PaletteFile>;
|
|
31
|
+
export declare const validateSettings: Validator<AppSettings>;
|
|
32
|
+
export declare const validateSettingsFile: Validator<SettingsFile>;
|