@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.
Files changed (224) hide show
  1. package/README.md +30 -15
  2. package/dist/lib/brushes/configRanges/index.d.ts +17 -0
  3. package/dist/lib/brushes/configRanges/index.js +31 -1
  4. package/dist/lib/brushes/params/index.d.ts +2 -1
  5. package/dist/lib/brushes/params/index.js +7 -4
  6. package/dist/lib/brushes/presets/round/index.d.ts +1 -1
  7. package/dist/lib/brushes/presets/round/index.js +12 -7
  8. package/dist/lib/brushes/presets/square/index.d.ts +1 -1
  9. package/dist/lib/brushes/presets/square/index.js +7 -3
  10. package/dist/lib/brushes/shapeImage/index.d.ts +7 -3
  11. package/dist/lib/brushes/shapeImage/index.js +15 -9
  12. package/dist/lib/brushes/shapes/index.d.ts +18 -0
  13. package/dist/lib/brushes/shapes/index.js +86 -0
  14. package/dist/lib/brushes/types/index.d.ts +8 -0
  15. package/dist/lib/classes/BrushSettings/index.d.ts +29 -2
  16. package/dist/lib/classes/BrushSettings/index.js +51 -14
  17. package/dist/lib/classes/KeyboardShortcuts/index.d.ts +31 -2
  18. package/dist/lib/classes/KeyboardShortcuts/index.js +66 -4
  19. package/dist/lib/classes/Layer/index.d.ts +10 -0
  20. package/dist/lib/classes/Layer/index.js +15 -1
  21. package/dist/lib/classes/LayerAutosave/index.d.ts +9 -15
  22. package/dist/lib/classes/LayerAutosave/index.js +30 -43
  23. package/dist/lib/classes/LayerStack/index.d.ts +15 -0
  24. package/dist/lib/classes/LayerStack/index.js +26 -2
  25. package/dist/lib/classes/PaintCanvas/index.d.ts +72 -3
  26. package/dist/lib/classes/PaintCanvas/index.js +106 -12
  27. package/dist/lib/classes/PngEncoder/index.d.ts +41 -0
  28. package/dist/lib/classes/PngEncoder/index.js +104 -0
  29. package/dist/lib/classes/ProfileGraphs/index.d.ts +87 -0
  30. package/dist/lib/classes/ProfileGraphs/index.js +148 -0
  31. package/dist/lib/classes/SettingsAutosave/index.d.ts +3 -2
  32. package/dist/lib/classes/SettingsAutosave/index.js +6 -3
  33. package/dist/lib/classes/UiSettings/index.d.ts +68 -0
  34. package/dist/lib/classes/UiSettings/index.js +82 -0
  35. package/dist/lib/messages/schema.d.ts +720 -0
  36. package/dist/lib/presets/schema.d.ts +360 -0
  37. package/dist/lib/presets/schema.js +8 -1
  38. package/dist/lib/profiles/index.d.ts +61 -0
  39. package/dist/lib/profiles/index.js +131 -0
  40. package/dist/lib/profiles/schema.d.ts +73 -0
  41. package/dist/lib/profiles/schema.js +53 -0
  42. package/dist/lib/rendering/sample/index.d.ts +11 -0
  43. package/dist/lib/rendering/sample/index.js +36 -5
  44. package/dist/lib/settings/index.d.ts +2 -1
  45. package/dist/lib/settings/index.js +7 -2
  46. package/dist/lib/settings/schema.d.ts +67 -0
  47. package/dist/lib/settings/schema.js +20 -0
  48. package/dist/lib/validation/validators.generated.js +13191 -3655
  49. package/dist/locales/en-GB.js +39 -2
  50. package/dist/locales/en-US.js +39 -2
  51. package/dist/locales/fr.js +39 -2
  52. package/dist/locales/ja.js +39 -2
  53. package/dist/locales/ko.js +39 -2
  54. package/dist/locales/zh-Hans.js +39 -2
  55. package/dist/locales/zh-Hant.js +39 -2
  56. package/dist/package.js +1 -1
  57. package/dist/testing/recordingCanvas.js +2 -1
  58. package/dist/ui/ask/index.d.ts +48 -0
  59. package/dist/ui/ask/index.js +67 -0
  60. package/dist/ui/base/index.d.ts +10 -3
  61. package/dist/ui/base/index.js +19 -4
  62. package/dist/ui/dismiss/index.d.ts +21 -0
  63. package/dist/ui/dismiss/index.js +27 -0
  64. package/dist/ui/drag/index.d.ts +51 -0
  65. package/dist/ui/drag/index.js +81 -0
  66. package/dist/ui/elements/PaintCanvas/index.d.ts +17 -12
  67. package/dist/ui/elements/PaintCanvas/index.js +36 -17
  68. package/dist/ui/elements/PaintCanvas/style.js +1 -1
  69. package/dist/ui/elements/PcAppSettings/index.d.ts +6 -6
  70. package/dist/ui/elements/PcAppSettings/index.js +21 -65
  71. package/dist/ui/elements/PcBrushConfig/index.d.ts +34 -16
  72. package/dist/ui/elements/PcBrushConfig/index.js +263 -153
  73. package/dist/ui/elements/PcBrushConfig/style.js +1 -1
  74. package/dist/ui/elements/PcBrushSize/index.d.ts +53 -0
  75. package/dist/ui/elements/PcBrushSize/index.js +66 -0
  76. package/dist/ui/elements/PcBrushTest/index.d.ts +26 -0
  77. package/dist/ui/elements/PcBrushTest/index.js +79 -6
  78. package/dist/ui/elements/PcBrushTest/style.js +1 -1
  79. package/dist/ui/elements/PcButton/index.d.ts +12 -3
  80. package/dist/ui/elements/PcButton/index.js +18 -10
  81. package/dist/ui/elements/PcCanvas/index.d.ts +27 -2
  82. package/dist/ui/elements/PcCanvas/index.js +63 -13
  83. package/dist/ui/elements/PcCanvas/style.js +1 -1
  84. package/dist/ui/elements/PcCanvasMap/style.js +1 -1
  85. package/dist/ui/elements/PcClipboardTools/index.d.ts +49 -0
  86. package/dist/ui/elements/PcClipboardTools/index.js +125 -0
  87. package/dist/ui/elements/PcContextMenu/index.d.ts +2 -2
  88. package/dist/ui/elements/PcDialog/index.d.ts +46 -10
  89. package/dist/ui/elements/PcDialog/index.js +102 -51
  90. package/dist/ui/elements/PcDialogWindow/index.d.ts +141 -0
  91. package/dist/ui/elements/PcDialogWindow/index.js +385 -0
  92. package/dist/ui/elements/PcInputSettings/index.d.ts +13 -0
  93. package/dist/ui/elements/PcInputSettings/index.js +16 -1
  94. package/dist/ui/elements/PcMenu/index.d.ts +48 -3
  95. package/dist/ui/elements/PcMenu/index.js +111 -19
  96. package/dist/ui/elements/PcNumberSlider/index.d.ts +137 -0
  97. package/dist/ui/elements/PcNumberSlider/index.js +247 -0
  98. package/dist/ui/elements/PcPane/index.d.ts +3 -1
  99. package/dist/ui/elements/PcPane/index.js +44 -7
  100. package/dist/ui/elements/PcPanel/index.d.ts +2 -2
  101. package/dist/ui/elements/PcPanel/index.js +8 -8
  102. package/dist/ui/elements/PcPanelBrushes/index.d.ts +5 -17
  103. package/dist/ui/elements/PcPanelBrushes/index.js +13 -48
  104. package/dist/ui/elements/PcPanelBrushes/style.js +1 -1
  105. package/dist/ui/elements/PcPanelColours/index.js +2 -2
  106. package/dist/ui/elements/PcPanelColours/style.js +1 -1
  107. package/dist/ui/elements/PcPanelLayers/index.js +1 -1
  108. package/dist/ui/elements/PcPanelLayers/style.js +1 -1
  109. package/dist/ui/elements/PcPopover/index.d.ts +39 -1
  110. package/dist/ui/elements/PcPopover/index.js +72 -4
  111. package/dist/ui/elements/PcProfileEditor/index.d.ts +103 -0
  112. package/dist/ui/elements/PcProfileEditor/index.js +259 -0
  113. package/dist/ui/elements/PcProfileGraph/index.d.ts +84 -0
  114. package/dist/ui/elements/PcProfileGraph/index.js +317 -0
  115. package/dist/ui/elements/PcShortcutSettings/index.js +9 -13
  116. package/dist/ui/elements/PcSliderRange/index.d.ts +147 -0
  117. package/dist/ui/elements/PcSliderRange/index.js +307 -0
  118. package/dist/ui/elements/PcStats/index.d.ts +19 -4
  119. package/dist/ui/elements/PcStats/index.js +29 -13
  120. package/dist/ui/elements/PcThemePicker/index.d.ts +17 -15
  121. package/dist/ui/elements/PcThemePicker/index.js +22 -21
  122. package/dist/ui/elements/PcToolSettings/index.d.ts +48 -9
  123. package/dist/ui/elements/PcToolSettings/index.js +382 -318
  124. package/dist/ui/elements/PcToolSettings/style.js +1 -1
  125. package/dist/ui/elements/PcToolbar/index.d.ts +15 -0
  126. package/dist/ui/elements/PcToolbar/index.js +55 -14
  127. package/dist/ui/elements/PcToolbar/style.js +1 -1
  128. package/dist/ui/icons/images/icon-brush@2x.js +3 -0
  129. package/dist/ui/icons/images/icon-circle-mask@2x.js +3 -0
  130. package/dist/ui/icons/images/icon-clipboard@2x.js +3 -0
  131. package/dist/ui/icons/images/icon-cog@2x.js +3 -0
  132. package/dist/ui/icons/images/icon-cursor@2x.js +3 -0
  133. package/dist/ui/icons/images/icon-diagonal-tlbr-size@2x.js +3 -0
  134. package/dist/ui/icons/images/icon-diagonal-trbl-size@2x.js +3 -0
  135. package/dist/ui/icons/images/icon-eraser@2x.js +3 -0
  136. package/dist/ui/icons/images/icon-eyedropper@2x.js +3 -0
  137. package/dist/ui/icons/images/icon-flip-horizontal@2x.js +3 -0
  138. package/dist/ui/icons/images/icon-flip-vertical@2x.js +3 -0
  139. package/dist/ui/icons/images/icon-focus-pen@2x.js +3 -0
  140. package/dist/ui/icons/images/icon-freehand-mask@2x.js +3 -0
  141. package/dist/ui/icons/images/icon-hidden@2x.js +3 -0
  142. package/dist/ui/icons/images/icon-horizontal-size@2x.js +3 -0
  143. package/dist/ui/icons/images/icon-layers@2x.js +3 -0
  144. package/dist/ui/icons/images/icon-linked@2x.js +3 -0
  145. package/dist/ui/icons/images/icon-locked@2x.js +3 -0
  146. package/dist/ui/icons/images/icon-moon@2x.js +3 -0
  147. package/dist/ui/icons/images/icon-pan@2x.js +3 -0
  148. package/dist/ui/icons/images/icon-polygon-mask@2x.js +3 -0
  149. package/dist/ui/icons/images/icon-pressure@2x.js +3 -0
  150. package/dist/ui/icons/images/icon-profile-graph@2x.js +3 -0
  151. package/dist/ui/icons/images/icon-randomise@2x.js +3 -0
  152. package/dist/ui/icons/images/icon-rectangle-mask@2x.js +3 -0
  153. package/dist/ui/icons/images/icon-redo@2x.js +3 -0
  154. package/dist/ui/icons/images/icon-rotate-90-left@2x.js +3 -0
  155. package/dist/ui/icons/images/icon-rotate-90-right@2x.js +3 -0
  156. package/dist/ui/icons/images/icon-rotate@2x.js +3 -0
  157. package/dist/ui/icons/images/icon-smoothing-bezier-cubic@2x.js +3 -0
  158. package/dist/ui/icons/images/icon-smoothing-bezier-quad@2x.js +3 -0
  159. package/dist/ui/icons/images/icon-smoothing-linear@2x.js +3 -0
  160. package/dist/ui/icons/images/icon-star@2x.js +3 -0
  161. package/dist/ui/icons/images/icon-sun@2x.js +3 -0
  162. package/dist/ui/icons/images/icon-tilt@2x.js +3 -0
  163. package/dist/ui/icons/images/icon-undo@2x.js +3 -0
  164. package/dist/ui/icons/images/icon-unlinked@2x.js +3 -0
  165. package/dist/ui/icons/images/icon-unlocked@2x.js +3 -0
  166. package/dist/ui/icons/images/icon-vertical-size@2x.js +3 -0
  167. package/dist/ui/icons/images/icon-visible@2x.js +3 -0
  168. package/dist/ui/icons/images/icon-zoom-in@2x.js +3 -0
  169. package/dist/ui/icons/images/icon-zoom-out@2x.js +3 -0
  170. package/dist/ui/icons/images/icon-zoom@2x.js +3 -0
  171. package/dist/ui/icons/index.d.ts +1 -1
  172. package/dist/ui/icons/index.js +86 -72
  173. package/dist/ui/index.d.ts +13 -3
  174. package/dist/ui/index.js +15 -4
  175. package/dist/ui/layout/index.d.ts +9 -1
  176. package/dist/ui/layout/index.js +11 -2
  177. package/dist/ui/styles/index.d.ts +21 -1
  178. package/dist/ui/styles/index.js +73 -14
  179. package/dist/ui/tokens/index.d.ts +5 -4
  180. package/dist/ui/tokens/index.js +12 -7
  181. package/locales/en-GB.json +39 -2
  182. package/locales/en-US.json +39 -2
  183. package/locales/fr.json +39 -2
  184. package/locales/ja.json +39 -2
  185. package/locales/ko.json +39 -2
  186. package/locales/zh-Hans.json +39 -2
  187. package/locales/zh-Hant.json +39 -2
  188. package/package.json +27 -18
  189. package/dist/ui/icons/images/brush.js +0 -3
  190. package/dist/ui/icons/images/circle-mask.js +0 -3
  191. package/dist/ui/icons/images/clipboard.js +0 -3
  192. package/dist/ui/icons/images/cursor.js +0 -3
  193. package/dist/ui/icons/images/eraser.js +0 -3
  194. package/dist/ui/icons/images/eyedropper.js +0 -3
  195. package/dist/ui/icons/images/flip-horizontal.js +0 -3
  196. package/dist/ui/icons/images/flip-vertical.js +0 -3
  197. package/dist/ui/icons/images/focus-pen.js +0 -3
  198. package/dist/ui/icons/images/freehand-mask.js +0 -3
  199. package/dist/ui/icons/images/hidden.js +0 -3
  200. package/dist/ui/icons/images/layers.js +0 -3
  201. package/dist/ui/icons/images/linked.js +0 -3
  202. package/dist/ui/icons/images/locked.js +0 -3
  203. package/dist/ui/icons/images/moon.js +0 -3
  204. package/dist/ui/icons/images/pan.js +0 -3
  205. package/dist/ui/icons/images/polygon-mask.js +0 -3
  206. package/dist/ui/icons/images/pressure.js +0 -3
  207. package/dist/ui/icons/images/randomise.js +0 -3
  208. package/dist/ui/icons/images/rectangle-mask.js +0 -3
  209. package/dist/ui/icons/images/redo.js +0 -3
  210. package/dist/ui/icons/images/rotate-90-left.js +0 -3
  211. package/dist/ui/icons/images/rotate-90-right.js +0 -3
  212. package/dist/ui/icons/images/smoothing-bezier-cubic.js +0 -3
  213. package/dist/ui/icons/images/smoothing-bezier-quad.js +0 -3
  214. package/dist/ui/icons/images/smoothing-linear.js +0 -3
  215. package/dist/ui/icons/images/star.js +0 -3
  216. package/dist/ui/icons/images/sun.js +0 -3
  217. package/dist/ui/icons/images/tilt.js +0 -3
  218. package/dist/ui/icons/images/undo.js +0 -3
  219. package/dist/ui/icons/images/unlinked.js +0 -3
  220. package/dist/ui/icons/images/unlocked.js +0 -3
  221. package/dist/ui/icons/images/visible.js +0 -3
  222. package/dist/ui/icons/images/zoom-in.js +0 -3
  223. package/dist/ui/icons/images/zoom-out.js +0 -3
  224. 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 colours) in a store: the saved
14
- * settings are restored when it starts (invalid ones are ignored), then changes are saved soon after they happen.
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 colours) in a store: the saved
9
- * settings are restored when it starts (invalid ones are ignored), then changes are saved soon after they happen.
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 };