@lvl99dotcom/paintcanvas 0.6.0 → 0.7.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 (59) hide show
  1. package/README.md +2 -2
  2. package/dist/lib/classes/PaintCanvas/index.d.ts +13 -5
  3. package/dist/lib/classes/PaintCanvas/index.js +10 -6
  4. package/dist/lib/classes/UiSettings/index.d.ts +24 -2
  5. package/dist/lib/classes/UiSettings/index.js +32 -7
  6. package/dist/lib/settings/schema.d.ts +4 -1
  7. package/dist/lib/settings/schema.js +3 -2
  8. package/dist/lib/storage/index.d.ts +26 -0
  9. package/dist/lib/storage/index.js +38 -1
  10. package/dist/lib/validation/validators.generated.js +20 -2
  11. package/dist/lib/version/index.d.ts +25 -0
  12. package/dist/lib/version/index.js +35 -0
  13. package/dist/locales/en-GB.js +5 -0
  14. package/dist/locales/en-US.js +5 -0
  15. package/dist/locales/fr.js +5 -0
  16. package/dist/locales/ja.js +5 -0
  17. package/dist/locales/ko.js +5 -0
  18. package/dist/locales/zh-Hans.js +5 -0
  19. package/dist/locales/zh-Hant.js +5 -0
  20. package/dist/package.js +1 -1
  21. package/dist/ui/elements/PaintCanvas/index.d.ts +13 -3
  22. package/dist/ui/elements/PaintCanvas/index.js +22 -5
  23. package/dist/ui/elements/PcAppSettings/index.d.ts +10 -2
  24. package/dist/ui/elements/PcAppSettings/index.js +42 -7
  25. package/dist/ui/elements/PcBrushConfig/index.d.ts +1 -0
  26. package/dist/ui/elements/PcBrushConfig/index.js +8 -9
  27. package/dist/ui/elements/PcBrushTest/index.d.ts +1 -0
  28. package/dist/ui/elements/PcBrushTest/index.js +8 -7
  29. package/dist/ui/elements/PcBrushTest/style.js +1 -1
  30. package/dist/ui/elements/PcButtonMenu/index.d.ts +88 -0
  31. package/dist/ui/elements/PcButtonMenu/index.js +125 -0
  32. package/dist/ui/elements/PcCanvas/index.d.ts +9 -0
  33. package/dist/ui/elements/PcCanvas/index.js +33 -17
  34. package/dist/ui/elements/PcClipboardTools/index.d.ts +7 -4
  35. package/dist/ui/elements/PcClipboardTools/index.js +20 -26
  36. package/dist/ui/elements/PcInputSettings/index.d.ts +2 -1
  37. package/dist/ui/elements/PcInputSettings/index.js +9 -15
  38. package/dist/ui/elements/PcLayerItem/index.d.ts +21 -6
  39. package/dist/ui/elements/PcLayerItem/index.js +53 -26
  40. package/dist/ui/elements/PcLayerItem/style.js +1 -1
  41. package/dist/ui/elements/PcSliderRange/index.js +3 -2
  42. package/dist/ui/elements/PcToggleCheckbox/index.d.ts +13 -2
  43. package/dist/ui/elements/PcToggleCheckbox/index.js +27 -8
  44. package/dist/ui/elements/PcToolSettings/index.d.ts +1 -0
  45. package/dist/ui/elements/PcToolSettings/index.js +8 -8
  46. package/dist/ui/elements/PcVersion/index.d.ts +12 -0
  47. package/dist/ui/elements/PcVersion/index.js +43 -0
  48. package/dist/ui/index.d.ts +3 -1
  49. package/dist/ui/index.js +4 -2
  50. package/dist/ui/layout/index.d.ts +2 -2
  51. package/dist/ui/layout/index.js +2 -2
  52. package/locales/en-GB.json +5 -0
  53. package/locales/en-US.json +5 -0
  54. package/locales/fr.json +5 -0
  55. package/locales/ja.json +5 -0
  56. package/locales/ko.json +5 -0
  57. package/locales/zh-Hans.json +5 -0
  58. package/locales/zh-Hant.json +5 -0
  59. package/package.json +2 -2
package/README.md CHANGED
@@ -89,9 +89,9 @@ The demo is published at https://lvl99.github.io/paintcanvas/app/. To run it loc
89
89
  pnpm dev
90
90
  ```
91
91
 
92
- Starts a development server and opens the demo in your browser. It uses the source directly, so saving a change to `src/`, `demo/` or `locales/` reloads the page with the change straight away. (The whole page reloads because custom elements can't be redefined in a running page, so the drawing starts again.)
92
+ Starts a development server and opens the demo in your browser. It uses the source directly, so saving a change to `src/`, `demo/` or `locales/` reloads the page with the change straight away. (The whole page reloads because custom elements can't be redefined in a running page, so the drawing starts again.) It uses vite.demo.config.ts, which also gives the demo the commit it was built from (see scripts/build-info.ts).
93
93
 
94
- - http://localhost:5190/: a full-page PaintCanvas with a layers panel, the colour palette (from the colours at the top of the toolbar), a menu with input settings, keyboard shortcuts, settings import and export, a theme picker (light, dark or system) and a "New drawing" button. The drawing and the settings, including the theme and language, are kept in the browser, so they survive reloads. Below 600px wide, the toolbar and the layers rail sit over the canvas.
94
+ - http://localhost:5190/: a full-page PaintCanvas with a layers panel, the colour palette (from the colours at the top of the toolbar), a menu with PaintCanvas's version, input settings, a "Draw with the GPU" switch, keyboard shortcuts, settings import and export, a theme picker (light, dark or system) and a "New drawing" button. The drawing and the settings, including the theme, language and rendering (CPU or GPU), are kept in the browser, so they survive reloads. Below 600px wide, the toolbar and the layers rail sit over the canvas.
95
95
  - http://localhost:5190/elements.html: the base elements.
96
96
 
97
97
  To try it with a pen or touch on another device (e.g. a tablet) on the same network, run `pnpm dev --host` and open the network address it prints. This makes the server reachable by anyone on that network.
@@ -18,7 +18,7 @@ import type { LayerStoreLike } from "../../storage";
18
18
  import type { AssetAuthor, BrushConfig } from "../../brushes/types";
19
19
  import CustomPresets, { type PresetStoreLike } from "../CustomPresets";
20
20
  import InputSettings from "../InputSettings";
21
- import UiSettings from "../UiSettings";
21
+ import UiSettings, { type Rendering } from "../UiSettings";
22
22
  import Palette from "../Palette";
23
23
  import ProfileGraphs from "../ProfileGraphs";
24
24
  import ShortcutSettings from "../ShortcutSettings";
@@ -116,9 +116,15 @@ export interface PaintCanvasOptions {
116
116
  /** Most layers the user can have (default `DEFAULT_MAX_LAYERS`) */
117
117
  maxLayers?: number;
118
118
  /**
119
- * Creates the canvases the engine draws in: layers, stroke buffers, masks and copies. `createMemoryCanvas` by default,
120
- * keeping pixels in regular memory so saving and copying don't stall phones; pass `createOffscreenCanvas` to keep them
121
- * on the GPU instead (replaceable for tests).
119
+ * Where the canvases draw (`cpu` by default): `cpu` keeps their pixels in regular memory (`createMemoryCanvas`) so
120
+ * saving and copying don't stall phones; `gpu` asks the browser to keep them on the GPU (`createOffscreenCanvas`).
121
+ * Browsers fix this when a canvas is made, so it can't change while the canvas runs: to follow the user's choice
122
+ * (`ui.data.rendering`), pass the one they made last time (`<paint-canvas>` does this with its `settings-store`).
123
+ */
124
+ rendering?: Rendering;
125
+ /**
126
+ * Creates the canvases the engine draws in: layers, stroke buffers, masks and copies. Chosen by `rendering` unless
127
+ * given (replaceable for tests).
122
128
  */
123
129
  createCanvas?: CanvasFactory;
124
130
  /** Where custom brush presets are kept (defaults to keeping them in memory only) */
@@ -172,7 +178,7 @@ export default class PaintCanvas extends EventTarget {
172
178
  readonly background: BackgroundWork;
173
179
  /** How the user's input devices control the canvas (scrolling, pinching, touch gestures, focus pen mode) */
174
180
  readonly input: InputSettings;
175
- /** The user's interface choices (theme and language), kept with the other app settings */
181
+ /** The user's interface choices (theme, language and where canvases draw), kept with the other app settings */
176
182
  readonly ui: UiSettings;
177
183
  /** The user's saved colours, shown as swatches */
178
184
  readonly palette: Palette;
@@ -233,6 +239,8 @@ export default class PaintCanvas extends EventTarget {
233
239
  /** The layer stack whose changes are being sent */
234
240
  private sendingLayers?;
235
241
  private readonly onLayersChangeToSend;
242
+ /** Where the canvases draw, chosen when the canvas started (the user's choice for next time is `ui.data.rendering`) */
243
+ readonly rendering: Rendering;
236
244
  /** Creates the canvases the app draws on (fake ones in tests) */
237
245
  readonly createCanvas: CanvasFactory;
238
246
  /** Who the custom presets belong to (passed to the preset store) */
@@ -11,15 +11,15 @@ import Selection from "../../selection/Selection/index.js";
11
11
  import Lasso from "../../selection/Lasso/index.js";
12
12
  import Transformer, { applyMask, contentBounds, copyArea, drawTransformed } from "../../transform/Transformer/index.js";
13
13
  import { boxBounds } from "../../transform/geometry/index.js";
14
- import Layer, { createMemoryCanvas } from "../Layer/index.js";
14
+ import Layer, { createMemoryCanvas, createOffscreenCanvas } from "../Layer/index.js";
15
15
  import LayerAutosave from "../LayerAutosave/index.js";
16
16
  import { brushPresets, eraserPresets } from "../../brushes/index.js";
17
17
  import { BUILT_IN_AUTHOR, UNKNOWN_AUTHOR, authorHandle } from "../../brushes/authors/index.js";
18
18
  import "../../presets/index.js";
19
- import { BRUSH_PRESET_FILE_FORMAT } from "../../presets/schema.js";
20
19
  import CustomPresets from "../CustomPresets/index.js";
20
+ import { BRUSH_PRESET_FILE_FORMAT } from "../../presets/schema.js";
21
21
  import InputSettings from "../InputSettings/index.js";
22
- import UiSettings from "../UiSettings/index.js";
22
+ import UiSettings, { DEFAULT_UI_SETTINGS } from "../UiSettings/index.js";
23
23
  import Palette from "../Palette/index.js";
24
24
  import { PALETTE_FILE_FORMAT } from "../../palette/schema.js";
25
25
  import ProfileGraphs from "../ProfileGraphs/index.js";
@@ -127,8 +127,8 @@ var PaintCanvas = class extends EventTarget {
127
127
  background = new BackgroundWork(this.strokeLog);
128
128
  /** How the user's input devices control the canvas (scrolling, pinching, touch gestures, focus pen mode) */
129
129
  input = new InputSettings();
130
- /** The user's interface choices (theme and language), kept with the other app settings */
131
- ui = new UiSettings();
130
+ /** The user's interface choices (theme, language and where canvases draw), kept with the other app settings */
131
+ ui;
132
132
  /** The user's saved colours, shown as swatches */
133
133
  palette = new Palette();
134
134
  /** The user's own profile graphs (and the presets), for shaping how brush settings vary */
@@ -212,6 +212,8 @@ var PaintCanvas = class extends EventTarget {
212
212
  /** The layer stack whose changes are being sent */
213
213
  sendingLayers;
214
214
  onLayersChangeToSend = () => this.sender?.schedule();
215
+ /** Where the canvases draw, chosen when the canvas started (the user's choice for next time is `ui.data.rendering`) */
216
+ rendering;
215
217
  /** Creates the canvases the app draws on (fake ones in tests) */
216
218
  createCanvas;
217
219
  /** Who the custom presets belong to (passed to the preset store) */
@@ -242,7 +244,9 @@ var PaintCanvas = class extends EventTarget {
242
244
  this.customPresets = new CustomPresets(this.ownerId, options.presetStore);
243
245
  this.customPresets.addEventListener("change", () => this.listCustomPresets());
244
246
  this.customPresets.load().catch(() => void 0);
245
- this.createCanvas = options.createCanvas ?? createMemoryCanvas;
247
+ this.rendering = options.rendering ?? DEFAULT_UI_SETTINGS.rendering;
248
+ this.ui = new UiSettings({ rendering: this.rendering });
249
+ this.createCanvas = options.createCanvas ?? (this.rendering === "gpu" ? createOffscreenCanvas : createMemoryCanvas);
246
250
  this.systemClipboard = options.systemClipboard === null ? void 0 : options.systemClipboard ?? globalThis.navigator?.clipboard;
247
251
  this.canvasWidth = DEFAULT_CANVAS_SIZE;
248
252
  this.canvasHeight = DEFAULT_CANVAS_SIZE;
@@ -1,6 +1,12 @@
1
1
  /** The themes: light, dark, or following the device's (system) */
2
2
  export declare const THEMES: readonly ["light", "dark", "system"];
3
3
  export type Theme = (typeof THEMES)[number];
4
+ /**
5
+ * Where canvases draw: `cpu` keeps their pixels in regular memory (reading and saving them never waits for the GPU),
6
+ * `gpu` asks the browser to keep them on the GPU (drawing may be quicker; the browser can still use the CPU)
7
+ */
8
+ export declare const RENDERINGS: readonly ["cpu", "gpu"];
9
+ export type Rendering = (typeof RENDERINGS)[number];
4
10
  /** Longest locale tag kept in UI settings (characters) */
5
11
  export declare const MAX_LOCALE_LENGTH = 35;
6
12
  /** A locale tag's characters: letters, digits and hyphens (e.g. `zh-Hant`) */
@@ -13,6 +19,14 @@ export declare const LOCALE_PATTERN = "^[A-Za-z0-9-]+$";
13
19
  * isTheme(undefined); // false
14
20
  */
15
21
  export declare const isTheme: (value: unknown) => value is Theme;
22
+ /**
23
+ * Whether a value is one of the renderings.
24
+ *
25
+ * @example
26
+ * isRendering("gpu"); // true
27
+ * isRendering("webgl"); // false
28
+ */
29
+ export declare const isRendering: (value: unknown) => value is Rendering;
16
30
  /**
17
31
  * Whether a value can be a locale tag (letters, digits and hyphens, at most `MAX_LOCALE_LENGTH` characters).
18
32
  *
@@ -27,7 +41,14 @@ export interface UiSettingsData {
27
41
  theme: Theme;
28
42
  /** The language's locale tag, e.g. `fr`, or undefined to follow the device's languages */
29
43
  locale?: string;
44
+ /**
45
+ * Where canvases draw (`cpu` by default). Browsers fix this when a canvas is made, so a change applies the next time
46
+ * the canvas starts (e.g. after reloading the page; see `PaintCanvasOptions.rendering`).
47
+ */
48
+ rendering: Rendering;
30
49
  }
50
+ /** UI settings used for anything not given */
51
+ export declare const DEFAULT_UI_SETTINGS: Readonly<UiSettingsData>;
31
52
  /** UI settings the UI can show and change: PaintCanvas's `UiSettings`, or anything with the same data and `update` */
32
53
  export interface UiSettingsLike extends EventTarget {
33
54
  readonly data: Readonly<UiSettingsData>;
@@ -38,11 +59,12 @@ export interface UiSettingsLike extends EventTarget {
38
59
  * or invalid. Other properties are left out.
39
60
  *
40
61
  * @example
41
- * sanitiseUiSettings({ theme: "dark", locale: 42 }, { theme: "system" }); // { theme: "dark" }
62
+ * sanitiseUiSettings({ theme: "dark", locale: 42 }, { theme: "system", rendering: "cpu" });
63
+ * // { theme: "dark", rendering: "cpu" }
42
64
  */
43
65
  export declare const sanitiseUiSettings: (input: unknown, defaults: UiSettingsData) => UiSettingsData;
44
66
  /**
45
- * The user's interface choices: the theme and the language. Kept in memory; the implementer saves and restores them
67
+ * The user's interface choices: the theme, the language and where canvases draw. Kept in memory; the implementer saves and restores them
46
68
  * with the other app settings (see `SettingsAutosave`), and the UI shows and applies them (e.g. `<pc-theme-picker>` and
47
69
  * `<pc-input-settings>`).
48
70
  *
@@ -5,6 +5,11 @@ var THEMES = [
5
5
  "dark",
6
6
  "system"
7
7
  ];
8
+ /**
9
+ * Where canvases draw: `cpu` keeps their pixels in regular memory (reading and saving them never waits for the GPU),
10
+ * `gpu` asks the browser to keep them on the GPU (drawing may be quicker; the browser can still use the CPU)
11
+ */
12
+ var RENDERINGS = ["cpu", "gpu"];
8
13
  /** Longest locale tag kept in UI settings (characters) */
9
14
  var MAX_LOCALE_LENGTH = 35;
10
15
  /** A locale tag's characters: letters, digits and hyphens (e.g. `zh-Hant`) */
@@ -18,6 +23,14 @@ var LOCALE_PATTERN = "^[A-Za-z0-9-]+$";
18
23
  */
19
24
  var isTheme = (value) => THEMES.includes(value);
20
25
  /**
26
+ * Whether a value is one of the renderings.
27
+ *
28
+ * @example
29
+ * isRendering("gpu"); // true
30
+ * isRendering("webgl"); // false
31
+ */
32
+ var isRendering = (value) => RENDERINGS.includes(value);
33
+ /**
21
34
  * Whether a value can be a locale tag (letters, digits and hyphens, at most `MAX_LOCALE_LENGTH` characters).
22
35
  *
23
36
  * @example
@@ -25,24 +38,35 @@ var isTheme = (value) => THEMES.includes(value);
25
38
  * isLocaleTag("<script>"); // false
26
39
  */
27
40
  var isLocaleTag = (value) => typeof value === "string" && value.length <= 35 && (/* @__PURE__ */ new RegExp("^[A-Za-z0-9-]+$")).test(value);
41
+ /** UI settings used for anything not given */
42
+ var DEFAULT_UI_SETTINGS = {
43
+ theme: "system",
44
+ rendering: "cpu"
45
+ };
28
46
  /**
29
47
  * Keep only valid UI settings from an untrusted value (e.g. stored or imported), using `defaults` for anything missing
30
48
  * or invalid. Other properties are left out.
31
49
  *
32
50
  * @example
33
- * sanitiseUiSettings({ theme: "dark", locale: 42 }, { theme: "system" }); // { theme: "dark" }
51
+ * sanitiseUiSettings({ theme: "dark", locale: 42 }, { theme: "system", rendering: "cpu" });
52
+ * // { theme: "dark", rendering: "cpu" }
34
53
  */
35
54
  var sanitiseUiSettings = (input, defaults) => {
36
55
  const value = typeof input === "object" && input !== null ? input : {};
37
56
  const theme = isTheme(value.theme) ? value.theme : defaults.theme;
38
57
  const locale = isLocaleTag(value.locale) ? value.locale : defaults.locale;
39
- return locale === void 0 ? { theme } : {
58
+ const rendering = isRendering(value.rendering) ? value.rendering : defaults.rendering;
59
+ return locale === void 0 ? {
60
+ theme,
61
+ rendering
62
+ } : {
40
63
  theme,
41
- locale
64
+ locale,
65
+ rendering
42
66
  };
43
67
  };
44
68
  /**
45
- * The user's interface choices: the theme and the language. Kept in memory; the implementer saves and restores them
69
+ * The user's interface choices: the theme, the language and where canvases draw. Kept in memory; the implementer saves and restores them
46
70
  * with the other app settings (see `SettingsAutosave`), and the UI shows and applies them (e.g. `<pc-theme-picker>` and
47
71
  * `<pc-input-settings>`).
48
72
  *
@@ -57,7 +81,7 @@ var UiSettings = class extends EventTarget {
57
81
  values;
58
82
  constructor(initial) {
59
83
  super();
60
- this.values = sanitiseUiSettings(initial, { theme: "system" });
84
+ this.values = sanitiseUiSettings(initial, DEFAULT_UI_SETTINGS);
61
85
  }
62
86
  /** Current settings (read-only copy) */
63
87
  get data() {
@@ -74,9 +98,10 @@ var UiSettings = class extends EventTarget {
74
98
  ...this.values,
75
99
  ...changes
76
100
  }, this.values);
77
- if (next.theme === this.values.theme && next.locale === this.values.locale) return;
101
+ const keys = Object.keys(next);
102
+ if (keys.length === Object.keys(this.values).length && keys.every((key) => next[key] === this.values[key])) return;
78
103
  this.values = next;
79
104
  this.dispatchEvent(new Event("change"));
80
105
  }
81
106
  };
82
- export { LOCALE_PATTERN, MAX_LOCALE_LENGTH, THEMES, UiSettings as default, isLocaleTag, isTheme, sanitiseUiSettings };
107
+ export { DEFAULT_UI_SETTINGS, LOCALE_PATTERN, MAX_LOCALE_LENGTH, RENDERINGS, THEMES, UiSettings as default, isLocaleTag, isRendering, isTheme, sanitiseUiSettings };
@@ -27,7 +27,7 @@ export interface AppSettings {
27
27
  };
28
28
  /** The user's own profile graphs */
29
29
  profiles?: ProfileGraph[];
30
- /** The theme and language */
30
+ /** The theme, language and where canvases draw */
31
31
  ui?: Partial<UiSettingsData>;
32
32
  }
33
33
  /** A settings file: what users export and import */
@@ -191,6 +191,9 @@ export declare const settingsSchema: {
191
191
  readonly maxLength: 35;
192
192
  readonly pattern: "^[A-Za-z0-9-]+$";
193
193
  };
194
+ readonly rendering: {
195
+ readonly enum: readonly ["cpu", "gpu"];
196
+ };
194
197
  };
195
198
  };
196
199
  };
@@ -1,4 +1,4 @@
1
- import { LOCALE_PATTERN, THEMES } from "../classes/UiSettings/index.js";
1
+ import { LOCALE_PATTERN, RENDERINGS, THEMES } from "../classes/UiSettings/index.js";
2
2
  import { HEX_COLOUR_PATTERN, MAX_PALETTE_COLOURS, paletteFileSchema } from "../palette/schema.js";
3
3
  import { profileGraphSchema } from "../profiles/schema.js";
4
4
  // src/lib/settings/schema.ts
@@ -94,7 +94,8 @@ var settingsSchema = {
94
94
  minLength: 1,
95
95
  maxLength: 35,
96
96
  pattern: LOCALE_PATTERN
97
- }
97
+ },
98
+ rendering: { enum: RENDERINGS }
98
99
  }
99
100
  }
100
101
  }
@@ -1,5 +1,6 @@
1
1
  import type { LayerConfig } from "../classes/Layer";
2
2
  import type { AppSettings } from "../settings/schema";
3
+ import { type Rendering } from "../classes/UiSettings";
3
4
  /** Version of a saved drawing's shape, so drawings saved by other versions can be recognised */
4
5
  export declare const SAVED_DRAWING_VERSION = 1;
5
6
  /** Name of the IndexedDB database `IndexedDbLayerStore` keeps drawings in, and `IndexedDbSettingsStore` settings */
@@ -92,3 +93,28 @@ export declare class IndexedDbSettingsStore implements SettingsStoreLike {
92
93
  load(): Promise<unknown>;
93
94
  save(settings: AppSettings): Promise<void>;
94
95
  }
96
+ /**
97
+ * The browser storage key that keeps the rendering chosen with the app settings of a name (see
98
+ * `loadRenderingChoice`).
99
+ *
100
+ * @example
101
+ * renderingChoiceKey("demo"); // "paintcanvas-rendering:demo"
102
+ */
103
+ export declare const renderingChoiceKey: (name: string) => string;
104
+ /**
105
+ * The rendering (`cpu` or `gpu`) last chosen with the app settings of a name, kept in `localStorage` so it can be read
106
+ * before the canvas is made (the app settings themselves load later, from IndexedDB). Undefined when none was kept, it
107
+ * isn't valid, or the storage can't be read (e.g. blocked by the browser).
108
+ *
109
+ * @example
110
+ * new PaintCanvas({ rendering: loadRenderingChoice("demo") });
111
+ */
112
+ export declare const loadRenderingChoice: (name?: string, storage?: Pick<Storage, "getItem"> | undefined) => Rendering | undefined;
113
+ /**
114
+ * Keep the rendering chosen with the app settings of a name, to be read with `loadRenderingChoice` when the canvas is
115
+ * next made. Does nothing if the storage can't be written.
116
+ *
117
+ * @example
118
+ * canvas.ui.addEventListener("change", () => saveRenderingChoice("demo", canvas.ui.data.rendering));
119
+ */
120
+ export declare const saveRenderingChoice: (name: string, rendering: Rendering, storage?: Pick<Storage, "setItem"> | undefined) => void;
@@ -1,3 +1,4 @@
1
+ import { isRendering } from "../classes/UiSettings/index.js";
1
2
  // src/lib/storage/index.ts
2
3
  /** Version of a saved drawing's shape, so drawings saved by other versions can be recognised */
3
4
  var SAVED_DRAWING_VERSION = 1;
@@ -169,4 +170,40 @@ var IndexedDbSettingsStore = class {
169
170
  await done;
170
171
  }
171
172
  };
172
- export { DATABASE_NAME, DATABASE_VERSION, DEFAULT_SETTINGS_NAME, IndexedDbSettingsStore, SAVED_DRAWING_VERSION, IndexedDbLayerStore as default };
173
+ /**
174
+ * The browser storage key that keeps the rendering chosen with the app settings of a name (see
175
+ * `loadRenderingChoice`).
176
+ *
177
+ * @example
178
+ * renderingChoiceKey("demo"); // "paintcanvas-rendering:demo"
179
+ */
180
+ var renderingChoiceKey = (name) => `${DATABASE_NAME}-rendering:${name}`;
181
+ /**
182
+ * The rendering (`cpu` or `gpu`) last chosen with the app settings of a name, kept in `localStorage` so it can be read
183
+ * before the canvas is made (the app settings themselves load later, from IndexedDB). Undefined when none was kept, it
184
+ * isn't valid, or the storage can't be read (e.g. blocked by the browser).
185
+ *
186
+ * @example
187
+ * new PaintCanvas({ rendering: loadRenderingChoice("demo") });
188
+ */
189
+ var loadRenderingChoice = (name = DEFAULT_SETTINGS_NAME, storage = globalThis.localStorage) => {
190
+ try {
191
+ const value = storage?.getItem(renderingChoiceKey(name));
192
+ return isRendering(value) ? value : void 0;
193
+ } catch {
194
+ return;
195
+ }
196
+ };
197
+ /**
198
+ * Keep the rendering chosen with the app settings of a name, to be read with `loadRenderingChoice` when the canvas is
199
+ * next made. Does nothing if the storage can't be written.
200
+ *
201
+ * @example
202
+ * canvas.ui.addEventListener("change", () => saveRenderingChoice("demo", canvas.ui.data.rendering));
203
+ */
204
+ var saveRenderingChoice = (name, rendering, storage = globalThis.localStorage) => {
205
+ try {
206
+ storage?.setItem(renderingChoiceKey(name), rendering);
207
+ } catch {}
208
+ };
209
+ export { DATABASE_NAME, DATABASE_VERSION, DEFAULT_SETTINGS_NAME, IndexedDbSettingsStore, SAVED_DRAWING_VERSION, IndexedDbLayerStore as default, loadRenderingChoice, renderingChoiceKey, saveRenderingChoice };
@@ -29059,7 +29059,8 @@ var schema20 = {
29059
29059
  "minLength": 1,
29060
29060
  "maxLength": 35,
29061
29061
  "pattern": "^[A-Za-z0-9-]+$"
29062
- }
29062
+ },
29063
+ "rendering": { "enum": ["cpu", "gpu"] }
29063
29064
  }
29064
29065
  }
29065
29066
  }
@@ -30009,7 +30010,7 @@ function validate17(data, { instancePath = "", parentData, parentDataProperty, r
30009
30010
  if (errors === _errs70) {
30010
30011
  if (data28 && typeof data28 == "object" && !Array.isArray(data28)) {
30011
30012
  const _errs72 = errors;
30012
- for (const key9 in data28) if (!(key9 === "theme" || key9 === "locale")) {
30013
+ for (const key9 in data28) if (!(key9 === "theme" || key9 === "locale" || key9 === "rendering")) {
30013
30014
  validate17.errors = [{
30014
30015
  instancePath: instancePath + "/ui",
30015
30016
  schemaPath: "#/properties/ui/additionalProperties",
@@ -30082,6 +30083,23 @@ function validate17(data, { instancePath = "", parentData, parentDataProperty, r
30082
30083
  }
30083
30084
  var valid14 = _errs74 === errors;
30084
30085
  } else var valid14 = true;
30086
+ if (valid14) {
30087
+ if (data28.rendering !== void 0) {
30088
+ let data31 = data28.rendering;
30089
+ const _errs76 = errors;
30090
+ if (!(data31 === "cpu" || data31 === "gpu")) {
30091
+ validate17.errors = [{
30092
+ instancePath: instancePath + "/ui/rendering",
30093
+ schemaPath: "#/properties/ui/properties/rendering/enum",
30094
+ keyword: "enum",
30095
+ params: { allowedValues: schema20.properties.ui.properties.rendering.enum },
30096
+ message: "must be equal to one of the allowed values"
30097
+ }];
30098
+ return false;
30099
+ }
30100
+ var valid14 = _errs76 === errors;
30101
+ } else var valid14 = true;
30102
+ }
30085
30103
  }
30086
30104
  }
30087
30105
  } else {
@@ -0,0 +1,25 @@
1
+ /** PaintCanvas's version, from its package.json, e.g. `0.6.0` */
2
+ export declare const PAINTCANVAS_VERSION: string;
3
+ /** What the commit is called when it's unknown */
4
+ export declare const UNKNOWN_COMMIT = "unknown";
5
+ /**
6
+ * Read the commit's hash, or `UNKNOWN_COMMIT` when it wasn't put in place (e.g. the source served or bundled without
7
+ * PaintCanvas's Vite config, where `__PAINTCANVAS_COMMIT__` is undefined).
8
+ *
9
+ * @example
10
+ * readCommit(() => "ac12a0d"); // "ac12a0d"
11
+ * readCommit(() => { throw new ReferenceError(); }); // "unknown"
12
+ */
13
+ export declare const readCommit: (read: () => unknown) => string;
14
+ /** The short hash of the commit PaintCanvas was built from, e.g. `ac12a0d` (`unknown` when it can't be told) */
15
+ export declare const PAINTCANVAS_COMMIT: string;
16
+ /** Where PaintCanvas's source code is */
17
+ export declare const PAINTCANVAS_REPOSITORY_URL = "https://github.com/lvl99/paintcanvas";
18
+ /**
19
+ * PaintCanvas's name, version and commit as one line.
20
+ *
21
+ * @example
22
+ * versionText(); // "PaintCanvas v0.6.0 (ac12a0d)"
23
+ * versionText("1.0.0", "abc1234"); // "PaintCanvas v1.0.0 (abc1234)"
24
+ */
25
+ export declare const versionText: (version?: string, commit?: string) => string;
@@ -0,0 +1,35 @@
1
+ import { version } from "../../package.js";
2
+ // src/lib/version/index.ts
3
+ /** PaintCanvas's version, from its package.json, e.g. `0.6.0` */
4
+ var PAINTCANVAS_VERSION = version;
5
+ /** What the commit is called when it's unknown */
6
+ var UNKNOWN_COMMIT = "unknown";
7
+ /**
8
+ * Read the commit's hash, or `UNKNOWN_COMMIT` when it wasn't put in place (e.g. the source served or bundled without
9
+ * PaintCanvas's Vite config, where `__PAINTCANVAS_COMMIT__` is undefined).
10
+ *
11
+ * @example
12
+ * readCommit(() => "ac12a0d"); // "ac12a0d"
13
+ * readCommit(() => { throw new ReferenceError(); }); // "unknown"
14
+ */
15
+ var readCommit = (read) => {
16
+ try {
17
+ const commit = read();
18
+ return typeof commit === "string" && commit ? commit : UNKNOWN_COMMIT;
19
+ } catch {
20
+ return UNKNOWN_COMMIT;
21
+ }
22
+ };
23
+ /** The short hash of the commit PaintCanvas was built from, e.g. `ac12a0d` (`unknown` when it can't be told) */
24
+ var PAINTCANVAS_COMMIT = readCommit(() => "9f00978");
25
+ /** Where PaintCanvas's source code is */
26
+ var PAINTCANVAS_REPOSITORY_URL = "https://github.com/lvl99/paintcanvas";
27
+ /**
28
+ * PaintCanvas's name, version and commit as one line.
29
+ *
30
+ * @example
31
+ * versionText(); // "PaintCanvas v0.6.0 (ac12a0d)"
32
+ * versionText("1.0.0", "abc1234"); // "PaintCanvas v1.0.0 (abc1234)"
33
+ */
34
+ var versionText = (version = PAINTCANVAS_VERSION, commit = PAINTCANVAS_COMMIT) => `PaintCanvas v${version} (${commit})`;
35
+ export { PAINTCANVAS_COMMIT, PAINTCANVAS_REPOSITORY_URL, PAINTCANVAS_VERSION, UNKNOWN_COMMIT, readCommit, versionText };
@@ -164,6 +164,8 @@ var en_GB_default = {
164
164
  "paintCanvas.layers.alphaLock": "Alpha lock",
165
165
  "paintCanvas.layers.blendMode": "Blending mode",
166
166
  "paintCanvas.layers.actions": "Layer actions",
167
+ "paintCanvas.layers.rename": "Rename…",
168
+ "paintCanvas.layers.renameTitle": "Rename layer",
167
169
  "paintCanvas.layers.reorder": "Reorder {name}: drag, or press for actions",
168
170
  "paintCanvas.layers.duplicate": "Duplicate",
169
171
  "paintCanvas.layers.createMask": "Create mask",
@@ -291,6 +293,9 @@ var en_GB_default = {
291
293
  "paintCanvas.settings.fileIncompatible": "Settings JSON file not compatible",
292
294
  "paintCanvas.settings.fileReadFailed": "Failed to read settings JSON file",
293
295
  "paintCanvas.settings.exportFailed": "Error exporting settings JSON file",
296
+ "paintCanvas.settings.gpu": "Draw with the GPU",
297
+ "paintCanvas.settings.gpuHint": "Try this if drawing is slow. It applies after reloading the page.",
298
+ "paintCanvas.settings.reloadToApply": "Reload the page to apply this change.",
294
299
  "paintCanvas.stats.label": "Canvas details",
295
300
  "paintCanvas.stats.size": "Canvas: {width} × {height} px",
296
301
  "paintCanvas.stats.zoom": "Zoom: {zoom}",
@@ -164,6 +164,8 @@ var en_US_default = {
164
164
  "paintCanvas.layers.alphaLock": "Alpha lock",
165
165
  "paintCanvas.layers.blendMode": "Blending mode",
166
166
  "paintCanvas.layers.actions": "Layer actions",
167
+ "paintCanvas.layers.rename": "Rename…",
168
+ "paintCanvas.layers.renameTitle": "Rename layer",
167
169
  "paintCanvas.layers.reorder": "Reorder {name}: drag, or press for actions",
168
170
  "paintCanvas.layers.duplicate": "Duplicate",
169
171
  "paintCanvas.layers.createMask": "Create mask",
@@ -291,6 +293,9 @@ var en_US_default = {
291
293
  "paintCanvas.settings.fileIncompatible": "Settings JSON file not compatible",
292
294
  "paintCanvas.settings.fileReadFailed": "Failed to read settings JSON file",
293
295
  "paintCanvas.settings.exportFailed": "Error exporting settings JSON file",
296
+ "paintCanvas.settings.gpu": "Draw with the GPU",
297
+ "paintCanvas.settings.gpuHint": "Try this if drawing is slow. It applies after reloading the page.",
298
+ "paintCanvas.settings.reloadToApply": "Reload the page to apply this change.",
294
299
  "paintCanvas.stats.label": "Canvas details",
295
300
  "paintCanvas.stats.size": "Canvas: {width} × {height} px",
296
301
  "paintCanvas.stats.zoom": "Zoom: {zoom}",
@@ -164,6 +164,8 @@ var fr_default = {
164
164
  "paintCanvas.layers.alphaLock": "Verrouillage alpha",
165
165
  "paintCanvas.layers.blendMode": "Mode de fusion",
166
166
  "paintCanvas.layers.actions": "Actions du calque",
167
+ "paintCanvas.layers.rename": "Renommer…",
168
+ "paintCanvas.layers.renameTitle": "Renommer le calque",
167
169
  "paintCanvas.layers.reorder": "Réorganiser {name}\xA0: faire glisser, ou appuyer pour les actions",
168
170
  "paintCanvas.layers.duplicate": "Dupliquer",
169
171
  "paintCanvas.layers.createMask": "Créer un masque",
@@ -291,6 +293,9 @@ var fr_default = {
291
293
  "paintCanvas.settings.fileIncompatible": "Fichier JSON de réglages incompatible",
292
294
  "paintCanvas.settings.fileReadFailed": "Échec de la lecture du fichier JSON de réglages",
293
295
  "paintCanvas.settings.exportFailed": "Erreur lors de l'exportation du fichier JSON de réglages",
296
+ "paintCanvas.settings.gpu": "Dessiner avec le GPU",
297
+ "paintCanvas.settings.gpuHint": "Essayez ceci si le dessin est lent. Le changement s’applique après le rechargement de la page.",
298
+ "paintCanvas.settings.reloadToApply": "Rechargez la page pour appliquer ce changement.",
294
299
  "paintCanvas.stats.label": "Détails de la toile",
295
300
  "paintCanvas.stats.size": "Toile\xA0: {width} × {height}\xA0px",
296
301
  "paintCanvas.stats.zoom": "Zoom\xA0: {zoom}",
@@ -164,6 +164,8 @@ var ja_default = {
164
164
  "paintCanvas.layers.alphaLock": "透明部分をロック",
165
165
  "paintCanvas.layers.blendMode": "描画モード",
166
166
  "paintCanvas.layers.actions": "レイヤーの操作",
167
+ "paintCanvas.layers.rename": "名前を変更…",
168
+ "paintCanvas.layers.renameTitle": "レイヤー名を変更",
167
169
  "paintCanvas.layers.reorder": "{name}の並べ替え:ドラッグ、または押して操作を表示",
168
170
  "paintCanvas.layers.duplicate": "複製",
169
171
  "paintCanvas.layers.createMask": "マスクを作成",
@@ -291,6 +293,9 @@ var ja_default = {
291
293
  "paintCanvas.settings.fileIncompatible": "この設定のJSONファイルには対応していません",
292
294
  "paintCanvas.settings.fileReadFailed": "設定のJSONファイルを読み込めませんでした",
293
295
  "paintCanvas.settings.exportFailed": "設定のJSONファイルを書き出せませんでした",
296
+ "paintCanvas.settings.gpu": "GPU で描画する",
297
+ "paintCanvas.settings.gpuHint": "描画が遅い場合にお試しください。ページを再読み込みすると適用されます。",
298
+ "paintCanvas.settings.reloadToApply": "この変更を適用するにはページを再読み込みしてください。",
294
299
  "paintCanvas.stats.label": "キャンバスの詳細",
295
300
  "paintCanvas.stats.size": "キャンバス: {width} × {height} px",
296
301
  "paintCanvas.stats.zoom": "ズーム: {zoom}",
@@ -164,6 +164,8 @@ var ko_default = {
164
164
  "paintCanvas.layers.alphaLock": "투명 픽셀 잠금",
165
165
  "paintCanvas.layers.blendMode": "혼합 모드",
166
166
  "paintCanvas.layers.actions": "레이어 작업",
167
+ "paintCanvas.layers.rename": "이름 바꾸기…",
168
+ "paintCanvas.layers.renameTitle": "레이어 이름 바꾸기",
167
169
  "paintCanvas.layers.reorder": "{name} 순서 변경: 드래그하거나 눌러서 작업 보기",
168
170
  "paintCanvas.layers.duplicate": "복제",
169
171
  "paintCanvas.layers.createMask": "마스크 만들기",
@@ -291,6 +293,9 @@ var ko_default = {
291
293
  "paintCanvas.settings.fileIncompatible": "호환되지 않는 설정 JSON 파일입니다",
292
294
  "paintCanvas.settings.fileReadFailed": "설정 JSON 파일을 읽지 못했습니다",
293
295
  "paintCanvas.settings.exportFailed": "설정 JSON 파일을 내보내지 못했습니다",
296
+ "paintCanvas.settings.gpu": "GPU로 그리기",
297
+ "paintCanvas.settings.gpuHint": "그리기가 느리면 사용해 보세요. 페이지를 새로 고친 후 적용됩니다.",
298
+ "paintCanvas.settings.reloadToApply": "이 변경 사항을 적용하려면 페이지를 새로 고치세요.",
294
299
  "paintCanvas.stats.label": "캔버스 정보",
295
300
  "paintCanvas.stats.size": "캔버스: {width} × {height} px",
296
301
  "paintCanvas.stats.zoom": "확대/축소: {zoom}",
@@ -164,6 +164,8 @@ var zh_Hans_default = {
164
164
  "paintCanvas.layers.alphaLock": "锁定透明像素",
165
165
  "paintCanvas.layers.blendMode": "混合模式",
166
166
  "paintCanvas.layers.actions": "图层操作",
167
+ "paintCanvas.layers.rename": "重命名…",
168
+ "paintCanvas.layers.renameTitle": "重命名图层",
167
169
  "paintCanvas.layers.reorder": "调整{name}的顺序:拖动,或按下以显示操作",
168
170
  "paintCanvas.layers.duplicate": "复制",
169
171
  "paintCanvas.layers.createMask": "创建蒙版",
@@ -291,6 +293,9 @@ var zh_Hans_default = {
291
293
  "paintCanvas.settings.fileIncompatible": "不兼容的设置 JSON 文件",
292
294
  "paintCanvas.settings.fileReadFailed": "无法读取设置 JSON 文件",
293
295
  "paintCanvas.settings.exportFailed": "导出设置 JSON 文件时出错",
296
+ "paintCanvas.settings.gpu": "使用 GPU 绘制",
297
+ "paintCanvas.settings.gpuHint": "如果绘制缓慢,请尝试此选项。重新加载页面后生效。",
298
+ "paintCanvas.settings.reloadToApply": "重新加载页面以应用此更改。",
294
299
  "paintCanvas.stats.label": "画布详情",
295
300
  "paintCanvas.stats.size": "画布:{width} × {height} 像素",
296
301
  "paintCanvas.stats.zoom": "缩放:{zoom}",
@@ -164,6 +164,8 @@ var zh_Hant_default = {
164
164
  "paintCanvas.layers.alphaLock": "鎖定透明像素",
165
165
  "paintCanvas.layers.blendMode": "混合模式",
166
166
  "paintCanvas.layers.actions": "圖層動作",
167
+ "paintCanvas.layers.rename": "重新命名…",
168
+ "paintCanvas.layers.renameTitle": "重新命名圖層",
167
169
  "paintCanvas.layers.reorder": "調整{name}的順序:拖曳,或按下以顯示動作",
168
170
  "paintCanvas.layers.duplicate": "複製",
169
171
  "paintCanvas.layers.createMask": "建立遮罩",
@@ -291,6 +293,9 @@ var zh_Hant_default = {
291
293
  "paintCanvas.settings.fileIncompatible": "不相容的設定 JSON 檔案",
292
294
  "paintCanvas.settings.fileReadFailed": "無法讀取設定 JSON 檔案",
293
295
  "paintCanvas.settings.exportFailed": "匯出設定 JSON 檔案時發生錯誤",
296
+ "paintCanvas.settings.gpu": "使用 GPU 繪製",
297
+ "paintCanvas.settings.gpuHint": "如果繪製緩慢,請嘗試此選項。重新載入頁面後生效。",
298
+ "paintCanvas.settings.reloadToApply": "重新載入頁面以套用此變更。",
294
299
  "paintCanvas.stats.label": "畫布詳細資料",
295
300
  "paintCanvas.stats.size": "畫布:{width} × {height} 像素",
296
301
  "paintCanvas.stats.zoom": "縮放:{zoom}",
package/dist/package.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // package.json
2
- var version = "0.6.0";
2
+ var version = "0.7.0";
3
3
  export { version };