@lvl99dotcom/paintcanvas 0.4.0 → 0.6.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 +30 -15
- package/dist/lib/brushes/configRanges/index.d.ts +17 -0
- package/dist/lib/brushes/configRanges/index.js +31 -1
- package/dist/lib/brushes/params/index.d.ts +2 -1
- package/dist/lib/brushes/params/index.js +7 -4
- package/dist/lib/brushes/presets/round/index.d.ts +1 -1
- package/dist/lib/brushes/presets/round/index.js +12 -7
- package/dist/lib/brushes/presets/square/index.d.ts +1 -1
- package/dist/lib/brushes/presets/square/index.js +7 -3
- package/dist/lib/brushes/shapeImage/index.d.ts +7 -3
- package/dist/lib/brushes/shapeImage/index.js +15 -9
- package/dist/lib/brushes/shapes/index.d.ts +18 -0
- package/dist/lib/brushes/shapes/index.js +86 -0
- package/dist/lib/brushes/types/index.d.ts +8 -0
- package/dist/lib/classes/BrushSettings/index.d.ts +29 -2
- package/dist/lib/classes/BrushSettings/index.js +51 -14
- package/dist/lib/classes/KeyboardShortcuts/index.d.ts +31 -2
- package/dist/lib/classes/KeyboardShortcuts/index.js +66 -4
- package/dist/lib/classes/Layer/index.d.ts +10 -0
- package/dist/lib/classes/Layer/index.js +15 -1
- package/dist/lib/classes/LayerAutosave/index.d.ts +9 -15
- package/dist/lib/classes/LayerAutosave/index.js +30 -43
- package/dist/lib/classes/LayerStack/index.d.ts +15 -0
- package/dist/lib/classes/LayerStack/index.js +26 -2
- package/dist/lib/classes/PaintCanvas/index.d.ts +72 -3
- package/dist/lib/classes/PaintCanvas/index.js +106 -12
- package/dist/lib/classes/PngEncoder/index.d.ts +41 -0
- package/dist/lib/classes/PngEncoder/index.js +104 -0
- package/dist/lib/classes/ProfileGraphs/index.d.ts +87 -0
- package/dist/lib/classes/ProfileGraphs/index.js +148 -0
- package/dist/lib/classes/SettingsAutosave/index.d.ts +3 -2
- package/dist/lib/classes/SettingsAutosave/index.js +6 -3
- package/dist/lib/classes/UiSettings/index.d.ts +68 -0
- package/dist/lib/classes/UiSettings/index.js +82 -0
- package/dist/lib/messages/schema.d.ts +720 -0
- package/dist/lib/presets/schema.d.ts +360 -0
- package/dist/lib/presets/schema.js +8 -1
- package/dist/lib/profiles/index.d.ts +61 -0
- package/dist/lib/profiles/index.js +131 -0
- package/dist/lib/profiles/schema.d.ts +73 -0
- package/dist/lib/profiles/schema.js +53 -0
- package/dist/lib/rendering/sample/index.d.ts +11 -0
- package/dist/lib/rendering/sample/index.js +36 -5
- package/dist/lib/settings/index.d.ts +2 -1
- package/dist/lib/settings/index.js +7 -2
- package/dist/lib/settings/schema.d.ts +67 -0
- package/dist/lib/settings/schema.js +20 -0
- package/dist/lib/validation/validators.generated.js +13191 -3655
- package/dist/locales/en-GB.js +39 -2
- package/dist/locales/en-US.js +39 -2
- package/dist/locales/fr.js +39 -2
- package/dist/locales/ja.js +39 -2
- package/dist/locales/ko.js +39 -2
- package/dist/locales/zh-Hans.js +39 -2
- package/dist/locales/zh-Hant.js +39 -2
- package/dist/package.js +1 -1
- package/dist/testing/recordingCanvas.js +2 -1
- package/dist/ui/ask/index.d.ts +48 -0
- package/dist/ui/ask/index.js +67 -0
- package/dist/ui/base/index.d.ts +10 -3
- package/dist/ui/base/index.js +19 -4
- package/dist/ui/dismiss/index.d.ts +21 -0
- package/dist/ui/dismiss/index.js +27 -0
- package/dist/ui/drag/index.d.ts +51 -0
- package/dist/ui/drag/index.js +81 -0
- package/dist/ui/elements/PaintCanvas/index.d.ts +17 -12
- package/dist/ui/elements/PaintCanvas/index.js +36 -17
- package/dist/ui/elements/PaintCanvas/style.js +1 -1
- package/dist/ui/elements/PcAppSettings/index.d.ts +6 -6
- package/dist/ui/elements/PcAppSettings/index.js +21 -65
- package/dist/ui/elements/PcBrushConfig/index.d.ts +34 -16
- package/dist/ui/elements/PcBrushConfig/index.js +263 -153
- package/dist/ui/elements/PcBrushConfig/style.js +1 -1
- package/dist/ui/elements/PcBrushSize/index.d.ts +53 -0
- package/dist/ui/elements/PcBrushSize/index.js +66 -0
- package/dist/ui/elements/PcBrushTest/index.d.ts +26 -0
- package/dist/ui/elements/PcBrushTest/index.js +79 -6
- package/dist/ui/elements/PcBrushTest/style.js +1 -1
- package/dist/ui/elements/PcButton/index.d.ts +12 -3
- package/dist/ui/elements/PcButton/index.js +18 -10
- package/dist/ui/elements/PcCanvas/index.d.ts +27 -2
- package/dist/ui/elements/PcCanvas/index.js +63 -13
- package/dist/ui/elements/PcCanvas/style.js +1 -1
- package/dist/ui/elements/PcCanvasMap/style.js +1 -1
- package/dist/ui/elements/PcClipboardTools/index.d.ts +49 -0
- package/dist/ui/elements/PcClipboardTools/index.js +125 -0
- package/dist/ui/elements/PcContextMenu/index.d.ts +2 -2
- package/dist/ui/elements/PcDialog/index.d.ts +46 -10
- package/dist/ui/elements/PcDialog/index.js +102 -51
- package/dist/ui/elements/PcDialogWindow/index.d.ts +141 -0
- package/dist/ui/elements/PcDialogWindow/index.js +385 -0
- package/dist/ui/elements/PcInputSettings/index.d.ts +13 -0
- package/dist/ui/elements/PcInputSettings/index.js +16 -1
- package/dist/ui/elements/PcMenu/index.d.ts +48 -3
- package/dist/ui/elements/PcMenu/index.js +111 -19
- package/dist/ui/elements/PcNumberSlider/index.d.ts +137 -0
- package/dist/ui/elements/PcNumberSlider/index.js +247 -0
- package/dist/ui/elements/PcPane/index.d.ts +3 -1
- package/dist/ui/elements/PcPane/index.js +44 -7
- package/dist/ui/elements/PcPanel/index.d.ts +2 -2
- package/dist/ui/elements/PcPanel/index.js +8 -8
- package/dist/ui/elements/PcPanelBrushes/index.d.ts +5 -17
- package/dist/ui/elements/PcPanelBrushes/index.js +13 -48
- package/dist/ui/elements/PcPanelBrushes/style.js +1 -1
- package/dist/ui/elements/PcPanelColours/index.js +2 -2
- package/dist/ui/elements/PcPanelColours/style.js +1 -1
- package/dist/ui/elements/PcPanelLayers/index.js +1 -1
- package/dist/ui/elements/PcPanelLayers/style.js +1 -1
- package/dist/ui/elements/PcPopover/index.d.ts +39 -1
- package/dist/ui/elements/PcPopover/index.js +72 -4
- package/dist/ui/elements/PcProfileEditor/index.d.ts +103 -0
- package/dist/ui/elements/PcProfileEditor/index.js +259 -0
- package/dist/ui/elements/PcProfileGraph/index.d.ts +84 -0
- package/dist/ui/elements/PcProfileGraph/index.js +317 -0
- package/dist/ui/elements/PcShortcutSettings/index.js +9 -13
- package/dist/ui/elements/PcSliderRange/index.d.ts +147 -0
- package/dist/ui/elements/PcSliderRange/index.js +307 -0
- package/dist/ui/elements/PcStats/index.d.ts +19 -4
- package/dist/ui/elements/PcStats/index.js +29 -13
- package/dist/ui/elements/PcThemePicker/index.d.ts +17 -15
- package/dist/ui/elements/PcThemePicker/index.js +22 -21
- package/dist/ui/elements/PcToolSettings/index.d.ts +48 -9
- package/dist/ui/elements/PcToolSettings/index.js +382 -318
- package/dist/ui/elements/PcToolSettings/style.js +1 -1
- package/dist/ui/elements/PcToolbar/index.d.ts +15 -0
- package/dist/ui/elements/PcToolbar/index.js +55 -14
- package/dist/ui/elements/PcToolbar/style.js +1 -1
- package/dist/ui/icons/images/icon-brush@2x.js +3 -0
- package/dist/ui/icons/images/icon-circle-mask@2x.js +3 -0
- package/dist/ui/icons/images/icon-clipboard@2x.js +3 -0
- package/dist/ui/icons/images/icon-cog@2x.js +3 -0
- package/dist/ui/icons/images/icon-cursor@2x.js +3 -0
- package/dist/ui/icons/images/icon-diagonal-tlbr-size@2x.js +3 -0
- package/dist/ui/icons/images/icon-diagonal-trbl-size@2x.js +3 -0
- package/dist/ui/icons/images/icon-eraser@2x.js +3 -0
- package/dist/ui/icons/images/icon-eyedropper@2x.js +3 -0
- package/dist/ui/icons/images/icon-flip-horizontal@2x.js +3 -0
- package/dist/ui/icons/images/icon-flip-vertical@2x.js +3 -0
- package/dist/ui/icons/images/icon-focus-pen@2x.js +3 -0
- package/dist/ui/icons/images/icon-freehand-mask@2x.js +3 -0
- package/dist/ui/icons/images/icon-hidden@2x.js +3 -0
- package/dist/ui/icons/images/icon-horizontal-size@2x.js +3 -0
- package/dist/ui/icons/images/icon-layers@2x.js +3 -0
- package/dist/ui/icons/images/icon-linked@2x.js +3 -0
- package/dist/ui/icons/images/icon-locked@2x.js +3 -0
- package/dist/ui/icons/images/icon-moon@2x.js +3 -0
- package/dist/ui/icons/images/icon-pan@2x.js +3 -0
- package/dist/ui/icons/images/icon-polygon-mask@2x.js +3 -0
- package/dist/ui/icons/images/icon-pressure@2x.js +3 -0
- package/dist/ui/icons/images/icon-profile-graph@2x.js +3 -0
- package/dist/ui/icons/images/icon-randomise@2x.js +3 -0
- package/dist/ui/icons/images/icon-rectangle-mask@2x.js +3 -0
- package/dist/ui/icons/images/icon-redo@2x.js +3 -0
- package/dist/ui/icons/images/icon-rotate-90-left@2x.js +3 -0
- package/dist/ui/icons/images/icon-rotate-90-right@2x.js +3 -0
- package/dist/ui/icons/images/icon-rotate@2x.js +3 -0
- package/dist/ui/icons/images/icon-smoothing-bezier-cubic@2x.js +3 -0
- package/dist/ui/icons/images/icon-smoothing-bezier-quad@2x.js +3 -0
- package/dist/ui/icons/images/icon-smoothing-linear@2x.js +3 -0
- package/dist/ui/icons/images/icon-star@2x.js +3 -0
- package/dist/ui/icons/images/icon-sun@2x.js +3 -0
- package/dist/ui/icons/images/icon-tilt@2x.js +3 -0
- package/dist/ui/icons/images/icon-undo@2x.js +3 -0
- package/dist/ui/icons/images/icon-unlinked@2x.js +3 -0
- package/dist/ui/icons/images/icon-unlocked@2x.js +3 -0
- package/dist/ui/icons/images/icon-vertical-size@2x.js +3 -0
- package/dist/ui/icons/images/icon-visible@2x.js +3 -0
- package/dist/ui/icons/images/icon-zoom-in@2x.js +3 -0
- package/dist/ui/icons/images/icon-zoom-out@2x.js +3 -0
- package/dist/ui/icons/images/icon-zoom@2x.js +3 -0
- package/dist/ui/icons/index.d.ts +1 -1
- package/dist/ui/icons/index.js +86 -72
- package/dist/ui/index.d.ts +13 -3
- package/dist/ui/index.js +15 -4
- package/dist/ui/layout/index.d.ts +9 -1
- package/dist/ui/layout/index.js +11 -2
- package/dist/ui/styles/index.d.ts +21 -1
- package/dist/ui/styles/index.js +73 -14
- package/dist/ui/tokens/index.d.ts +5 -4
- package/dist/ui/tokens/index.js +12 -7
- package/locales/en-GB.json +39 -2
- package/locales/en-US.json +39 -2
- package/locales/fr.json +39 -2
- package/locales/ja.json +39 -2
- package/locales/ko.json +39 -2
- package/locales/zh-Hans.json +39 -2
- package/locales/zh-Hant.json +39 -2
- package/package.json +27 -18
- package/dist/ui/icons/images/brush.js +0 -3
- package/dist/ui/icons/images/circle-mask.js +0 -3
- package/dist/ui/icons/images/clipboard.js +0 -3
- package/dist/ui/icons/images/cursor.js +0 -3
- package/dist/ui/icons/images/eraser.js +0 -3
- package/dist/ui/icons/images/eyedropper.js +0 -3
- package/dist/ui/icons/images/flip-horizontal.js +0 -3
- package/dist/ui/icons/images/flip-vertical.js +0 -3
- package/dist/ui/icons/images/focus-pen.js +0 -3
- package/dist/ui/icons/images/freehand-mask.js +0 -3
- package/dist/ui/icons/images/hidden.js +0 -3
- package/dist/ui/icons/images/layers.js +0 -3
- package/dist/ui/icons/images/linked.js +0 -3
- package/dist/ui/icons/images/locked.js +0 -3
- package/dist/ui/icons/images/moon.js +0 -3
- package/dist/ui/icons/images/pan.js +0 -3
- package/dist/ui/icons/images/polygon-mask.js +0 -3
- package/dist/ui/icons/images/pressure.js +0 -3
- package/dist/ui/icons/images/randomise.js +0 -3
- package/dist/ui/icons/images/rectangle-mask.js +0 -3
- package/dist/ui/icons/images/redo.js +0 -3
- package/dist/ui/icons/images/rotate-90-left.js +0 -3
- package/dist/ui/icons/images/rotate-90-right.js +0 -3
- package/dist/ui/icons/images/smoothing-bezier-cubic.js +0 -3
- package/dist/ui/icons/images/smoothing-bezier-quad.js +0 -3
- package/dist/ui/icons/images/smoothing-linear.js +0 -3
- package/dist/ui/icons/images/star.js +0 -3
- package/dist/ui/icons/images/sun.js +0 -3
- package/dist/ui/icons/images/tilt.js +0 -3
- package/dist/ui/icons/images/undo.js +0 -3
- package/dist/ui/icons/images/unlinked.js +0 -3
- package/dist/ui/icons/images/unlocked.js +0 -3
- package/dist/ui/icons/images/visible.js +0 -3
- package/dist/ui/icons/images/zoom-in.js +0 -3
- package/dist/ui/icons/images/zoom-out.js +0 -3
- package/dist/ui/icons/images/zoom.js +0 -3
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Interpolation, PROFILE_PRESETS } from "../../profiles/index.js";
|
|
2
|
+
import "../../profiles/schema.js";
|
|
3
|
+
// src/lib/classes/ProfileGraphs/index.ts
|
|
4
|
+
/** The interpolations a point can have */
|
|
5
|
+
var INTERPOLATIONS = Object.values(Interpolation);
|
|
6
|
+
/** Whether a value is text of a usable length for an ID or name */
|
|
7
|
+
var isText = (value) => typeof value === "string" && value.trim().length > 0 && value.length <= 100;
|
|
8
|
+
/**
|
|
9
|
+
* Keep only valid points from an untrusted value, in order from left to right: each on the grid, with a known
|
|
10
|
+
* interpolation. Undefined if there aren't 2 to `MAX_PROFILE_POINTS` of them.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* sanitisePoints([{ x: 10, y: 0, i: "linear" }, { x: 0, y: -10, i: "linear" }]); // sorted by x
|
|
14
|
+
*/
|
|
15
|
+
var sanitisePoints = (input) => {
|
|
16
|
+
if (!Array.isArray(input)) return void 0;
|
|
17
|
+
const points = input.filter((point) => typeof point?.x === "number" && typeof point.y === "number" && point.x >= 0 && point.x <= 10 && point.y >= -10 && point.y <= 0 && INTERPOLATIONS.includes(point.i)).map(({ x, y, i }) => ({
|
|
18
|
+
x,
|
|
19
|
+
y,
|
|
20
|
+
i
|
|
21
|
+
})).sort((a, b) => a.x - b.x);
|
|
22
|
+
return points.length >= 2 && points.length <= 32 ? points : void 0;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Keep only valid profile graphs from an untrusted value (e.g. stored or imported): each with an ID and name and valid
|
|
26
|
+
* points (see `sanitisePoints`), each ID once and none of the presets', up to `MAX_SAVED_PROFILES`. Undefined if the
|
|
27
|
+
* value isn't an array.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* sanitiseProfiles([{ id: "a", name: "Soft", points: [...] }, { id: "a", name: "Again", points: [...] }]); // the first
|
|
31
|
+
*/
|
|
32
|
+
var sanitiseProfiles = (input) => {
|
|
33
|
+
if (!Array.isArray(input)) return void 0;
|
|
34
|
+
const graphs = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const graph of input) {
|
|
36
|
+
if (graphs.size >= 100) break;
|
|
37
|
+
const points = sanitisePoints(graph?.points);
|
|
38
|
+
if (!points || !isText(graph.id) || !isText(graph.name) || graphs.has(graph.id) || PROFILE_PRESETS.some((preset) => preset.id === graph.id)) continue;
|
|
39
|
+
graphs.set(graph.id, {
|
|
40
|
+
id: graph.id,
|
|
41
|
+
name: graph.name.trim(),
|
|
42
|
+
points
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return [...graphs.values()];
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The user's own profile graphs, alongside the ones PaintCanvas comes with (`PROFILE_PRESETS`). Kept in memory; the app
|
|
49
|
+
* settings save and restore them (see `set` and the `change` event). Each gets a UUID when it's first saved.
|
|
50
|
+
*
|
|
51
|
+
* Dispatches a `change` event when the user's graphs change.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* const profiles = new ProfileGraphs();
|
|
55
|
+
* const soft = profiles.save({ name: "Soft", points: PROFILE_PRESETS[1].points });
|
|
56
|
+
* profiles.get(soft.id)?.name; // "Soft"
|
|
57
|
+
*/
|
|
58
|
+
var ProfileGraphs = class extends EventTarget {
|
|
59
|
+
values = [];
|
|
60
|
+
/** The user's own graphs, in the order they were first saved */
|
|
61
|
+
get graphs() {
|
|
62
|
+
return this.values;
|
|
63
|
+
}
|
|
64
|
+
/** Every graph: the presets, then the user's own */
|
|
65
|
+
get all() {
|
|
66
|
+
return [...PROFILE_PRESETS, ...this.values];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A graph by its ID: a preset or one of the user's own.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* profiles.get("overweight")?.name; // "Overweight"
|
|
73
|
+
*/
|
|
74
|
+
get(id) {
|
|
75
|
+
return this.all.find((graph) => graph.id === id);
|
|
76
|
+
}
|
|
77
|
+
/** Whether a graph is one PaintCanvas comes with (so it can't be renamed, replaced or deleted) */
|
|
78
|
+
isPreset(id) {
|
|
79
|
+
return PROFILE_PRESETS.some((graph) => graph.id === id);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* One of the user's own graphs with a name, ignoring case and spaces around it.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* profiles.findByName(" soft "); // the graph called "Soft"
|
|
86
|
+
*/
|
|
87
|
+
findByName(name) {
|
|
88
|
+
const wanted = name.trim().toLowerCase();
|
|
89
|
+
return this.values.find((graph) => graph.name.toLowerCase() === wanted);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Save a graph as one of the user's own: as a new one (with a new UUID), or in place of the one with its ID. Returns
|
|
93
|
+
* the saved graph, or undefined if it can't be saved (no name, invalid points, a preset's ID, or no room).
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* profiles.save({ name: "Soft", points }); // { id: "1b9d…", name: "Soft", points }
|
|
97
|
+
*/
|
|
98
|
+
save(draft) {
|
|
99
|
+
const index = this.values.findIndex((graph) => graph.id === draft.id);
|
|
100
|
+
const [graph] = sanitiseProfiles([{
|
|
101
|
+
...draft,
|
|
102
|
+
id: index >= 0 ? draft.id : crypto.randomUUID()
|
|
103
|
+
}]);
|
|
104
|
+
if (!graph || index < 0 && this.values.length >= 100) return void 0;
|
|
105
|
+
this.values = index >= 0 ? this.values.map((saved, i) => i === index ? graph : saved) : [...this.values, graph];
|
|
106
|
+
this.dispatchEvent(new Event("change"));
|
|
107
|
+
return graph;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Rename one of the user's own graphs. Returns whether it was renamed.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* profiles.rename(id, "Softer");
|
|
114
|
+
*/
|
|
115
|
+
rename(id, name) {
|
|
116
|
+
const graph = this.values.find((saved) => saved.id === id);
|
|
117
|
+
return graph !== void 0 && this.save({
|
|
118
|
+
...graph,
|
|
119
|
+
name
|
|
120
|
+
}) !== void 0;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Delete one of the user's own graphs. Returns whether it was deleted.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* profiles.remove(id);
|
|
127
|
+
*/
|
|
128
|
+
remove(id) {
|
|
129
|
+
const next = this.values.filter((graph) => graph.id !== id);
|
|
130
|
+
if (next.length === this.values.length) return false;
|
|
131
|
+
this.values = next;
|
|
132
|
+
this.dispatchEvent(new Event("change"));
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Replace all the user's graphs (e.g. when restoring saved ones), keeping only valid ones (see `sanitiseProfiles`).
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* profiles.set(settings.profiles);
|
|
140
|
+
*/
|
|
141
|
+
set(graphs) {
|
|
142
|
+
const next = sanitiseProfiles(graphs);
|
|
143
|
+
if (!next) return;
|
|
144
|
+
this.values = next;
|
|
145
|
+
this.dispatchEvent(new Event("change"));
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
export { ProfileGraphs as default, sanitisePoints, sanitiseProfiles };
|
|
@@ -10,8 +10,9 @@ export interface SettingsAutosaveOptions {
|
|
|
10
10
|
delayMs?: number;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Keeps a canvas's app settings (input settings, keyboard shortcuts, colour palette and
|
|
14
|
-
* settings are restored when it starts (invalid ones are ignored), then changes are saved soon after
|
|
13
|
+
* Keeps a canvas's app settings (input settings, keyboard shortcuts, colour palette, colours, profile graphs, theme and language) in a
|
|
14
|
+
* store: the saved settings are restored when it starts (invalid ones are ignored), then changes are saved soon after
|
|
15
|
+
* they happen.
|
|
15
16
|
*
|
|
16
17
|
* Dispatches an `error` event (with the error as `detail`) when loading or saving fails.
|
|
17
18
|
*
|
|
@@ -5,8 +5,9 @@ var SETTINGS_SAVE_DELAY_MS = 500;
|
|
|
5
5
|
/** Event dispatched when loading or saving settings fails (detail: the error) */
|
|
6
6
|
var SETTINGS_AUTOSAVE_ERROR_EVENT = "error";
|
|
7
7
|
/**
|
|
8
|
-
* Keeps a canvas's app settings (input settings, keyboard shortcuts, colour palette and
|
|
9
|
-
* settings are restored when it starts (invalid ones are ignored), then changes are saved soon after
|
|
8
|
+
* Keeps a canvas's app settings (input settings, keyboard shortcuts, colour palette, colours, profile graphs, theme and language) in a
|
|
9
|
+
* store: the saved settings are restored when it starts (invalid ones are ignored), then changes are saved soon after
|
|
10
|
+
* they happen.
|
|
10
11
|
*
|
|
11
12
|
* Dispatches an `error` event (with the error as `detail`) when loading or saving fails.
|
|
12
13
|
*
|
|
@@ -41,7 +42,9 @@ var SettingsAutosave = class extends EventTarget {
|
|
|
41
42
|
app,
|
|
42
43
|
app.input,
|
|
43
44
|
app.shortcuts,
|
|
44
|
-
app.palette
|
|
45
|
+
app.palette,
|
|
46
|
+
app.profiles,
|
|
47
|
+
app.ui
|
|
45
48
|
];
|
|
46
49
|
}
|
|
47
50
|
/**
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** The themes: light, dark, or following the device's (system) */
|
|
2
|
+
export declare const THEMES: readonly ["light", "dark", "system"];
|
|
3
|
+
export type Theme = (typeof THEMES)[number];
|
|
4
|
+
/** Longest locale tag kept in UI settings (characters) */
|
|
5
|
+
export declare const MAX_LOCALE_LENGTH = 35;
|
|
6
|
+
/** A locale tag's characters: letters, digits and hyphens (e.g. `zh-Hant`) */
|
|
7
|
+
export declare const LOCALE_PATTERN = "^[A-Za-z0-9-]+$";
|
|
8
|
+
/**
|
|
9
|
+
* Whether a value is one of the themes.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* isTheme("dark"); // true
|
|
13
|
+
* isTheme(undefined); // false
|
|
14
|
+
*/
|
|
15
|
+
export declare const isTheme: (value: unknown) => value is Theme;
|
|
16
|
+
/**
|
|
17
|
+
* Whether a value can be a locale tag (letters, digits and hyphens, at most `MAX_LOCALE_LENGTH` characters).
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* isLocaleTag("zh-Hant"); // true
|
|
21
|
+
* isLocaleTag("<script>"); // false
|
|
22
|
+
*/
|
|
23
|
+
export declare const isLocaleTag: (value: unknown) => value is string;
|
|
24
|
+
/** The user's interface choices */
|
|
25
|
+
export interface UiSettingsData {
|
|
26
|
+
/** The theme (`system` by default) */
|
|
27
|
+
theme: Theme;
|
|
28
|
+
/** The language's locale tag, e.g. `fr`, or undefined to follow the device's languages */
|
|
29
|
+
locale?: string;
|
|
30
|
+
}
|
|
31
|
+
/** UI settings the UI can show and change: PaintCanvas's `UiSettings`, or anything with the same data and `update` */
|
|
32
|
+
export interface UiSettingsLike extends EventTarget {
|
|
33
|
+
readonly data: Readonly<UiSettingsData>;
|
|
34
|
+
update(changes: Partial<UiSettingsData>): void;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Keep only valid UI settings from an untrusted value (e.g. stored or imported), using `defaults` for anything missing
|
|
38
|
+
* or invalid. Other properties are left out.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* sanitiseUiSettings({ theme: "dark", locale: 42 }, { theme: "system" }); // { theme: "dark" }
|
|
42
|
+
*/
|
|
43
|
+
export declare const sanitiseUiSettings: (input: unknown, defaults: UiSettingsData) => UiSettingsData;
|
|
44
|
+
/**
|
|
45
|
+
* The user's interface choices: the theme and the language. Kept in memory; the implementer saves and restores them
|
|
46
|
+
* with the other app settings (see `SettingsAutosave`), and the UI shows and applies them (e.g. `<pc-theme-picker>` and
|
|
47
|
+
* `<pc-input-settings>`).
|
|
48
|
+
*
|
|
49
|
+
* Dispatches a `change` event when a setting changes.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const ui = new UiSettings();
|
|
53
|
+
* ui.update({ theme: "dark", locale: "fr" });
|
|
54
|
+
* ui.data.theme; // "dark"
|
|
55
|
+
*/
|
|
56
|
+
export default class UiSettings extends EventTarget {
|
|
57
|
+
private values;
|
|
58
|
+
constructor(initial?: unknown);
|
|
59
|
+
/** Current settings (read-only copy) */
|
|
60
|
+
get data(): Readonly<UiSettingsData>;
|
|
61
|
+
/**
|
|
62
|
+
* Change some settings and dispatch `change` if anything changed. Invalid values and other properties are ignored.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ui.update({ locale: "ja" });
|
|
66
|
+
*/
|
|
67
|
+
update(changes: Partial<UiSettingsData>): void;
|
|
68
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// src/lib/classes/UiSettings/index.ts
|
|
2
|
+
/** The themes: light, dark, or following the device's (system) */
|
|
3
|
+
var THEMES = [
|
|
4
|
+
"light",
|
|
5
|
+
"dark",
|
|
6
|
+
"system"
|
|
7
|
+
];
|
|
8
|
+
/** Longest locale tag kept in UI settings (characters) */
|
|
9
|
+
var MAX_LOCALE_LENGTH = 35;
|
|
10
|
+
/** A locale tag's characters: letters, digits and hyphens (e.g. `zh-Hant`) */
|
|
11
|
+
var LOCALE_PATTERN = "^[A-Za-z0-9-]+$";
|
|
12
|
+
/**
|
|
13
|
+
* Whether a value is one of the themes.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* isTheme("dark"); // true
|
|
17
|
+
* isTheme(undefined); // false
|
|
18
|
+
*/
|
|
19
|
+
var isTheme = (value) => THEMES.includes(value);
|
|
20
|
+
/**
|
|
21
|
+
* Whether a value can be a locale tag (letters, digits and hyphens, at most `MAX_LOCALE_LENGTH` characters).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* isLocaleTag("zh-Hant"); // true
|
|
25
|
+
* isLocaleTag("<script>"); // false
|
|
26
|
+
*/
|
|
27
|
+
var isLocaleTag = (value) => typeof value === "string" && value.length <= 35 && (/* @__PURE__ */ new RegExp("^[A-Za-z0-9-]+$")).test(value);
|
|
28
|
+
/**
|
|
29
|
+
* Keep only valid UI settings from an untrusted value (e.g. stored or imported), using `defaults` for anything missing
|
|
30
|
+
* or invalid. Other properties are left out.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* sanitiseUiSettings({ theme: "dark", locale: 42 }, { theme: "system" }); // { theme: "dark" }
|
|
34
|
+
*/
|
|
35
|
+
var sanitiseUiSettings = (input, defaults) => {
|
|
36
|
+
const value = typeof input === "object" && input !== null ? input : {};
|
|
37
|
+
const theme = isTheme(value.theme) ? value.theme : defaults.theme;
|
|
38
|
+
const locale = isLocaleTag(value.locale) ? value.locale : defaults.locale;
|
|
39
|
+
return locale === void 0 ? { theme } : {
|
|
40
|
+
theme,
|
|
41
|
+
locale
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The user's interface choices: the theme and the language. Kept in memory; the implementer saves and restores them
|
|
46
|
+
* with the other app settings (see `SettingsAutosave`), and the UI shows and applies them (e.g. `<pc-theme-picker>` and
|
|
47
|
+
* `<pc-input-settings>`).
|
|
48
|
+
*
|
|
49
|
+
* Dispatches a `change` event when a setting changes.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* const ui = new UiSettings();
|
|
53
|
+
* ui.update({ theme: "dark", locale: "fr" });
|
|
54
|
+
* ui.data.theme; // "dark"
|
|
55
|
+
*/
|
|
56
|
+
var UiSettings = class extends EventTarget {
|
|
57
|
+
values;
|
|
58
|
+
constructor(initial) {
|
|
59
|
+
super();
|
|
60
|
+
this.values = sanitiseUiSettings(initial, { theme: "system" });
|
|
61
|
+
}
|
|
62
|
+
/** Current settings (read-only copy) */
|
|
63
|
+
get data() {
|
|
64
|
+
return this.values;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Change some settings and dispatch `change` if anything changed. Invalid values and other properties are ignored.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ui.update({ locale: "ja" });
|
|
71
|
+
*/
|
|
72
|
+
update(changes) {
|
|
73
|
+
const next = sanitiseUiSettings({
|
|
74
|
+
...this.values,
|
|
75
|
+
...changes
|
|
76
|
+
}, this.values);
|
|
77
|
+
if (next.theme === this.values.theme && next.locale === this.values.locale) return;
|
|
78
|
+
this.values = next;
|
|
79
|
+
this.dispatchEvent(new Event("change"));
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
export { LOCALE_PATTERN, MAX_LOCALE_LENGTH, THEMES, UiSettings as default, isLocaleTag, isTheme, sanitiseUiSettings };
|