@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
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { type default as PaintCanvas } from "../PaintCanvas";
|
|
2
2
|
import { type ShortcutAction } from "../../utils/shortcuts";
|
|
3
|
+
/**
|
|
4
|
+
* How long (milliseconds) the paste shortcut waits for the browser's `paste` event (which carries other apps' images)
|
|
5
|
+
* before pasting the image data copied here
|
|
6
|
+
*/
|
|
7
|
+
export declare const PASTE_EVENT_WAIT_MS = 50;
|
|
8
|
+
/**
|
|
9
|
+
* The first image in a paste's clipboard data (e.g. copied in another app), if any.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* pastedImage(event.clipboardData); // File of type "image/png", or undefined
|
|
13
|
+
*/
|
|
14
|
+
export declare const pastedImage: (data: DataTransfer | null) => File | undefined;
|
|
3
15
|
/** Event dispatched on the window for shortcut actions handled elsewhere (e.g. saving) */
|
|
4
16
|
export declare const SHORTCUT_EVENT = "pc-shortcut";
|
|
5
17
|
/**
|
|
@@ -9,7 +21,10 @@ export declare const SHORTCUT_EVENT = "pc-shortcut";
|
|
|
9
21
|
*
|
|
10
22
|
* Tool selection, brush/eraser size and opacity, interface visibility, select all/invert/deselect, undo/redo,
|
|
11
23
|
* cut/copy/paste, deleting the masked image data (Delete or Backspace), fills, swapping colours, zooming, layer opacity
|
|
12
|
-
* and finishing or cancelling a lasso shape are handled here
|
|
24
|
+
* and finishing or cancelling a lasso shape are handled here. Pasting (the shortcut, or the browser's own paste, e.g. from
|
|
25
|
+
* its Edit menu) pastes the image data copied here, unless the window has lost focus since it was copied and the
|
|
26
|
+
* clipboard has an image (e.g. copied in another app): that image is pasted instead (see `PaintCanvas.pasteImage`).
|
|
27
|
+
* Other actions other actions (and Escape/Enter for later features)
|
|
13
28
|
* dispatch an `pc-shortcut` event with the action as `detail`. Holding the hold-to-pan key (Space by default) pans
|
|
14
29
|
* temporarily; releasing it returns to the selected tool.
|
|
15
30
|
*
|
|
@@ -24,8 +39,15 @@ export default class KeyboardShortcuts {
|
|
|
24
39
|
private heldKey?;
|
|
25
40
|
private readonly onKeyDown;
|
|
26
41
|
private readonly onKeyUp;
|
|
42
|
+
/** Turns pasted image files into images (replaceable for tests) */
|
|
43
|
+
private readonly decodeImage;
|
|
44
|
+
/** `app.copyCount` when the window last lost focus: copies since then were made here */
|
|
45
|
+
private copiesBeforeBlur;
|
|
46
|
+
/** Pastes the image data copied here if the browser doesn't fire `paste` soon after the shortcut */
|
|
47
|
+
private pasteTimer?;
|
|
27
48
|
private readonly onBlur;
|
|
28
|
-
|
|
49
|
+
private readonly onPaste;
|
|
50
|
+
constructor(app: PaintCanvas, decodeImage?: (blob: Blob) => Promise<ImageBitmap>);
|
|
29
51
|
/**
|
|
30
52
|
* Start listening for key presses on a target (usually `window`).
|
|
31
53
|
*/
|
|
@@ -38,6 +60,13 @@ export default class KeyboardShortcuts {
|
|
|
38
60
|
* Run the shortcut for a key press, if any. Returns the action run.
|
|
39
61
|
*/
|
|
40
62
|
handle(event: KeyboardEvent): ShortcutAction | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Paste from the browser's `paste` event: the image data copied here if it was copied since the window last lost
|
|
65
|
+
* focus (or the clipboard has no image), otherwise the clipboard's image. Ignored in text fields and open dialogs.
|
|
66
|
+
*/
|
|
67
|
+
handlePaste(event: ClipboardEvent): Promise<void>;
|
|
68
|
+
/** Stop waiting for the browser's `paste` event */
|
|
69
|
+
private cancelPasteTimer;
|
|
41
70
|
/**
|
|
42
71
|
* Return to the selected tool when the hold-to-pan key is released (whatever modifiers are held by then).
|
|
43
72
|
*/
|
|
@@ -7,6 +7,18 @@ import { comboFromEvent, isTypingTarget, sameCombo } from "../../utils/shortcuts
|
|
|
7
7
|
* Escape is left to close it.
|
|
8
8
|
*/
|
|
9
9
|
var inOpenDialog = (path) => path.some((target) => target instanceof HTMLDialogElement && target.open);
|
|
10
|
+
/**
|
|
11
|
+
* How long (milliseconds) the paste shortcut waits for the browser's `paste` event (which carries other apps' images)
|
|
12
|
+
* before pasting the image data copied here
|
|
13
|
+
*/
|
|
14
|
+
var PASTE_EVENT_WAIT_MS = 50;
|
|
15
|
+
/**
|
|
16
|
+
* The first image in a paste's clipboard data (e.g. copied in another app), if any.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* pastedImage(event.clipboardData); // File of type "image/png", or undefined
|
|
20
|
+
*/
|
|
21
|
+
var pastedImage = (data) => [...data?.files ?? []].find((file) => file.type.startsWith("image/"));
|
|
10
22
|
/** Event dispatched on the window for shortcut actions handled elsewhere (e.g. saving) */
|
|
11
23
|
var SHORTCUT_EVENT = "pc-shortcut";
|
|
12
24
|
/**
|
|
@@ -16,7 +28,10 @@ var SHORTCUT_EVENT = "pc-shortcut";
|
|
|
16
28
|
*
|
|
17
29
|
* Tool selection, brush/eraser size and opacity, interface visibility, select all/invert/deselect, undo/redo,
|
|
18
30
|
* cut/copy/paste, deleting the masked image data (Delete or Backspace), fills, swapping colours, zooming, layer opacity
|
|
19
|
-
* and finishing or cancelling a lasso shape are handled here
|
|
31
|
+
* and finishing or cancelling a lasso shape are handled here. Pasting (the shortcut, or the browser's own paste, e.g. from
|
|
32
|
+
* its Edit menu) pastes the image data copied here, unless the window has lost focus since it was copied and the
|
|
33
|
+
* clipboard has an image (e.g. copied in another app): that image is pasted instead (see `PaintCanvas.pasteImage`).
|
|
34
|
+
* Other actions other actions (and Escape/Enter for later features)
|
|
20
35
|
* dispatch an `pc-shortcut` event with the action as `detail`. Holding the hold-to-pan key (Space by default) pans
|
|
21
36
|
* temporarily; releasing it returns to the selected tool.
|
|
22
37
|
*
|
|
@@ -31,9 +46,20 @@ var KeyboardShortcuts = class {
|
|
|
31
46
|
heldKey;
|
|
32
47
|
onKeyDown = (event) => this.handle(event);
|
|
33
48
|
onKeyUp = (event) => this.handleKeyUp(event);
|
|
34
|
-
|
|
35
|
-
|
|
49
|
+
/** Turns pasted image files into images (replaceable for tests) */
|
|
50
|
+
decodeImage;
|
|
51
|
+
/** `app.copyCount` when the window last lost focus: copies since then were made here */
|
|
52
|
+
copiesBeforeBlur = 0;
|
|
53
|
+
/** Pastes the image data copied here if the browser doesn't fire `paste` soon after the shortcut */
|
|
54
|
+
pasteTimer;
|
|
55
|
+
onBlur = () => {
|
|
56
|
+
this.release();
|
|
57
|
+
this.copiesBeforeBlur = this.app.copyCount;
|
|
58
|
+
};
|
|
59
|
+
onPaste = (event) => void this.handlePaste(event);
|
|
60
|
+
constructor(app, decodeImage = (blob) => createImageBitmap(blob)) {
|
|
36
61
|
this.app = app;
|
|
62
|
+
this.decodeImage = decodeImage;
|
|
37
63
|
}
|
|
38
64
|
/**
|
|
39
65
|
* Start listening for key presses on a target (usually `window`).
|
|
@@ -44,6 +70,7 @@ var KeyboardShortcuts = class {
|
|
|
44
70
|
target.addEventListener("keydown", this.onKeyDown);
|
|
45
71
|
target.addEventListener("keyup", this.onKeyUp);
|
|
46
72
|
target.addEventListener("blur", this.onBlur);
|
|
73
|
+
target.addEventListener("paste", this.onPaste);
|
|
47
74
|
}
|
|
48
75
|
/**
|
|
49
76
|
* Stop listening for key presses.
|
|
@@ -52,7 +79,9 @@ var KeyboardShortcuts = class {
|
|
|
52
79
|
this.target?.removeEventListener("keydown", this.onKeyDown);
|
|
53
80
|
this.target?.removeEventListener("keyup", this.onKeyUp);
|
|
54
81
|
this.target?.removeEventListener("blur", this.onBlur);
|
|
82
|
+
this.target?.removeEventListener("paste", this.onPaste);
|
|
55
83
|
this.target = null;
|
|
84
|
+
this.cancelPasteTimer();
|
|
56
85
|
}
|
|
57
86
|
/**
|
|
58
87
|
* Run the shortcut for a key press, if any. Returns the action run.
|
|
@@ -71,11 +100,44 @@ var KeyboardShortcuts = class {
|
|
|
71
100
|
}
|
|
72
101
|
const action = shortcuts.find((shortcut) => sameCombo(shortcut, combo))?.action;
|
|
73
102
|
if (!action) return void 0;
|
|
103
|
+
if (action.type === "paste") {
|
|
104
|
+
this.cancelPasteTimer();
|
|
105
|
+
this.pasteTimer = setTimeout(() => {
|
|
106
|
+
this.pasteTimer = void 0;
|
|
107
|
+
this.app.paste();
|
|
108
|
+
}, 50);
|
|
109
|
+
return action;
|
|
110
|
+
}
|
|
74
111
|
event.preventDefault();
|
|
75
112
|
this.run(action);
|
|
76
113
|
return action;
|
|
77
114
|
}
|
|
78
115
|
/**
|
|
116
|
+
* Paste from the browser's `paste` event: the image data copied here if it was copied since the window last lost
|
|
117
|
+
* focus (or the clipboard has no image), otherwise the clipboard's image. Ignored in text fields and open dialogs.
|
|
118
|
+
*/
|
|
119
|
+
async handlePaste(event) {
|
|
120
|
+
const path = event.composedPath();
|
|
121
|
+
if (isTypingTarget(path[0] ?? event.target) || inOpenDialog(path)) return;
|
|
122
|
+
this.cancelPasteTimer();
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
const app = this.app;
|
|
125
|
+
const image = pastedImage(event.clipboardData);
|
|
126
|
+
const copiedHere = app.copyCount > this.copiesBeforeBlur;
|
|
127
|
+
if (!image || copiedHere && app.hasClipboard) {
|
|
128
|
+
app.paste();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
app.pasteImage(await this.decodeImage(image));
|
|
133
|
+
} catch {}
|
|
134
|
+
}
|
|
135
|
+
/** Stop waiting for the browser's `paste` event */
|
|
136
|
+
cancelPasteTimer() {
|
|
137
|
+
clearTimeout(this.pasteTimer);
|
|
138
|
+
this.pasteTimer = void 0;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
79
141
|
* Return to the selected tool when the hold-to-pan key is released (whatever modifiers are held by then).
|
|
80
142
|
*/
|
|
81
143
|
handleKeyUp(event) {
|
|
@@ -197,4 +259,4 @@ var KeyboardShortcuts = class {
|
|
|
197
259
|
(this.target ?? this.app).dispatchEvent(new CustomEvent(SHORTCUT_EVENT, { detail: action }));
|
|
198
260
|
}
|
|
199
261
|
};
|
|
200
|
-
export { SHORTCUT_EVENT, KeyboardShortcuts as default };
|
|
262
|
+
export { PASTE_EVENT_WAIT_MS, SHORTCUT_EVENT, KeyboardShortcuts as default, pastedImage };
|
|
@@ -29,6 +29,16 @@ export type CanvasFactory = (width: number, height: number) => OffscreenCanvas;
|
|
|
29
29
|
* Create an `OffscreenCanvas` of the given size.
|
|
30
30
|
*/
|
|
31
31
|
export declare const createOffscreenCanvas: CanvasFactory;
|
|
32
|
+
/**
|
|
33
|
+
* Create an `OffscreenCanvas` whose pixels are kept in regular memory rather than on the GPU (its 2D context is made
|
|
34
|
+
* with `willReadFrequently`). Reading its pixels, encoding it and copying it are then plain memory copies, which on
|
|
35
|
+
* phones avoids long stalls waiting for the GPU (up to 15 seconds were seen on an iPhone), at the cost of drawing on
|
|
36
|
+
* the CPU. PaintCanvas makes its canvases this way by default.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const canvas = createMemoryCanvas(2000, 2000);
|
|
40
|
+
*/
|
|
41
|
+
export declare const createMemoryCanvas: CanvasFactory;
|
|
32
42
|
/**
|
|
33
43
|
* Clamp an opacity value to 0–1.
|
|
34
44
|
*
|
|
@@ -16,6 +16,20 @@ var MAX_LAYER_NAME_LENGTH = 64;
|
|
|
16
16
|
*/
|
|
17
17
|
var createOffscreenCanvas = (width, height) => new OffscreenCanvas(width, height);
|
|
18
18
|
/**
|
|
19
|
+
* Create an `OffscreenCanvas` whose pixels are kept in regular memory rather than on the GPU (its 2D context is made
|
|
20
|
+
* with `willReadFrequently`). Reading its pixels, encoding it and copying it are then plain memory copies, which on
|
|
21
|
+
* phones avoids long stalls waiting for the GPU (up to 15 seconds were seen on an iPhone), at the cost of drawing on
|
|
22
|
+
* the CPU. PaintCanvas makes its canvases this way by default.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const canvas = createMemoryCanvas(2000, 2000);
|
|
26
|
+
*/
|
|
27
|
+
var createMemoryCanvas = (width, height) => {
|
|
28
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
29
|
+
canvas.getContext("2d", { willReadFrequently: true });
|
|
30
|
+
return canvas;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
19
33
|
* Clamp an opacity value to 0–1.
|
|
20
34
|
*
|
|
21
35
|
* @example
|
|
@@ -108,4 +122,4 @@ var Layer = class {
|
|
|
108
122
|
};
|
|
109
123
|
}
|
|
110
124
|
};
|
|
111
|
-
export { BlendMode, MAX_LAYER_NAME_LENGTH, clampOpacity, createOffscreenCanvas, Layer as default };
|
|
125
|
+
export { BlendMode, MAX_LAYER_NAME_LENGTH, clampOpacity, createMemoryCanvas, createOffscreenCanvas, Layer as default };
|
|
@@ -34,7 +34,7 @@ export interface AutosaveOptions {
|
|
|
34
34
|
locks?: LockManager | null;
|
|
35
35
|
/** Tells when the page is hidden or shown again (the page's document by default) */
|
|
36
36
|
document?: Pick<Document, "visibilityState" | "addEventListener" | "removeEventListener"> | null;
|
|
37
|
-
/** Turns a layer's canvas into a PNG (replaceable for tests) */
|
|
37
|
+
/** Turns a layer's canvas into a PNG (in a background worker by default; replaceable for tests) */
|
|
38
38
|
encode?: (canvas: OffscreenCanvas) => Promise<Blob>;
|
|
39
39
|
/** Turns a saved PNG into an image to draw (replaceable for tests) */
|
|
40
40
|
decode?: (image: Blob) => Promise<CanvasImageSource>;
|
|
@@ -44,8 +44,8 @@ export interface AutosaveOptions {
|
|
|
44
44
|
/**
|
|
45
45
|
* Keeps a canvas's layers in a store, so the drawing survives reloading the page: restores the saved drawing when it
|
|
46
46
|
* starts, then saves soon after the layers change (once drawing pauses), and straight away when the page is hidden.
|
|
47
|
-
* Only layers whose pixels changed are encoded and saved again
|
|
48
|
-
* which.
|
|
47
|
+
* Only layers whose pixels changed are encoded and saved again: the layers' change counts tell which (see
|
|
48
|
+
* `LayerStack.pixelVersion`), without reading their pixels, which is slow on phones.
|
|
49
49
|
*
|
|
50
50
|
* Only one window draws on a drawing at a time: the first to start has it, and others wait in the `elsewhere` state
|
|
51
51
|
* (the UI blocks drawing there). When the page is shown again it tries again, and `takeOver` takes the drawing from
|
|
@@ -74,14 +74,10 @@ export default class LayerAutosave extends EventTarget {
|
|
|
74
74
|
private readonly delayMs;
|
|
75
75
|
/** The layers being watched for changes */
|
|
76
76
|
private watched?;
|
|
77
|
-
/**
|
|
78
|
-
private
|
|
77
|
+
/** Each layer as last saved, by layer ID: the layer itself and its change count (see `LayerStack.pixelVersion`) */
|
|
78
|
+
private saved;
|
|
79
79
|
/** Size in bytes of each layer's image as last saved or restored, by layer ID */
|
|
80
80
|
private sizes;
|
|
81
|
-
/** Layers whose pixels changed since the last save */
|
|
82
|
-
private changed;
|
|
83
|
-
/** Whether any layer may have changed since the last save (not only those in `changed`) */
|
|
84
|
-
private checkAll;
|
|
85
81
|
private timer?;
|
|
86
82
|
/** The save being written, if any */
|
|
87
83
|
private saving?;
|
|
@@ -133,11 +129,13 @@ export default class LayerAutosave extends EventTarget {
|
|
|
133
129
|
*/
|
|
134
130
|
private restore;
|
|
135
131
|
/**
|
|
136
|
-
* Remember each layer
|
|
132
|
+
* Remember each layer as saved (just restored).
|
|
137
133
|
*/
|
|
138
134
|
private remember;
|
|
135
|
+
/** A layer and its change count, to tell later whether it changed */
|
|
136
|
+
private record;
|
|
139
137
|
/**
|
|
140
|
-
* Save the drawing, encoding only the layers whose pixels changed since they were last saved.
|
|
138
|
+
* Save the drawing, encoding only the layers whose pixels changed since they were last saved (or that are new).
|
|
141
139
|
*/
|
|
142
140
|
private write;
|
|
143
141
|
/**
|
|
@@ -145,10 +143,6 @@ export default class LayerAutosave extends EventTarget {
|
|
|
145
143
|
* changed.
|
|
146
144
|
*/
|
|
147
145
|
private rememberSizes;
|
|
148
|
-
/**
|
|
149
|
-
* A fingerprint of a layer's pixels.
|
|
150
|
-
*/
|
|
151
|
-
private fingerprint;
|
|
152
146
|
/**
|
|
153
147
|
* Save once the layers have stopped changing for a moment and drawing pauses.
|
|
154
148
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import PngEncoder from "../PngEncoder/index.js";
|
|
2
2
|
import "../../storage/index.js";
|
|
3
3
|
// src/lib/classes/LayerAutosave/index.ts
|
|
4
4
|
/** How long to wait after the layers change before saving, so quick changes are saved together (milliseconds) */
|
|
@@ -18,12 +18,14 @@ var AutosaveState = {
|
|
|
18
18
|
/** Not saving any more (see `stop`) */
|
|
19
19
|
Stopped: "stopped"
|
|
20
20
|
};
|
|
21
|
+
/** Encodes layers as PNGs in a background worker, shared by every autosave on the page (made on first use) */
|
|
22
|
+
var sharedEncoder;
|
|
21
23
|
/**
|
|
22
|
-
* Encode a canvas as a PNG.
|
|
24
|
+
* Encode a canvas as a PNG, in a background worker where one can be used, so saving doesn't freeze the page.
|
|
23
25
|
*/
|
|
24
26
|
var encodePng = (canvas) => {
|
|
25
|
-
|
|
26
|
-
return
|
|
27
|
+
sharedEncoder ??= new PngEncoder();
|
|
28
|
+
return sharedEncoder.encode(canvas);
|
|
27
29
|
};
|
|
28
30
|
/**
|
|
29
31
|
* Whether a saved drawing has the expected shape and an image for each layer.
|
|
@@ -32,8 +34,8 @@ var isComplete = (drawing, images) => drawing.version === 1 && drawing.layers.le
|
|
|
32
34
|
/**
|
|
33
35
|
* Keeps a canvas's layers in a store, so the drawing survives reloading the page: restores the saved drawing when it
|
|
34
36
|
* starts, then saves soon after the layers change (once drawing pauses), and straight away when the page is hidden.
|
|
35
|
-
* Only layers whose pixels changed are encoded and saved again
|
|
36
|
-
* which.
|
|
37
|
+
* Only layers whose pixels changed are encoded and saved again: the layers' change counts tell which (see
|
|
38
|
+
* `LayerStack.pixelVersion`), without reading their pixels, which is slow on phones.
|
|
37
39
|
*
|
|
38
40
|
* Only one window draws on a drawing at a time: the first to start has it, and others wait in the `elsewhere` state
|
|
39
41
|
* (the UI blocks drawing there). When the page is shown again it tries again, and `takeOver` takes the drawing from
|
|
@@ -62,14 +64,10 @@ var LayerAutosave = class extends EventTarget {
|
|
|
62
64
|
delayMs;
|
|
63
65
|
/** The layers being watched for changes */
|
|
64
66
|
watched;
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
+
/** Each layer as last saved, by layer ID: the layer itself and its change count (see `LayerStack.pixelVersion`) */
|
|
68
|
+
saved = /* @__PURE__ */ new Map();
|
|
67
69
|
/** Size in bytes of each layer's image as last saved or restored, by layer ID */
|
|
68
70
|
sizes = /* @__PURE__ */ new Map();
|
|
69
|
-
/** Layers whose pixels changed since the last save */
|
|
70
|
-
changed = /* @__PURE__ */ new Set();
|
|
71
|
-
/** Whether any layer may have changed since the last save (not only those in `changed`) */
|
|
72
|
-
checkAll = false;
|
|
73
71
|
timer;
|
|
74
72
|
/** The save being written, if any */
|
|
75
73
|
saving;
|
|
@@ -77,12 +75,7 @@ var LayerAutosave = class extends EventTarget {
|
|
|
77
75
|
again = false;
|
|
78
76
|
/** Lets go of the drawing's lock, while held */
|
|
79
77
|
release;
|
|
80
|
-
onLayersChange = () =>
|
|
81
|
-
const id = this.watched?.changedLayerId;
|
|
82
|
-
if (id) this.changed.add(id);
|
|
83
|
-
else this.checkAll = true;
|
|
84
|
-
this.schedule();
|
|
85
|
-
};
|
|
78
|
+
onLayersChange = () => this.schedule();
|
|
86
79
|
onCanvasChange = () => {
|
|
87
80
|
if (this.canvas.layers === this.watched) return;
|
|
88
81
|
this.watch();
|
|
@@ -202,7 +195,7 @@ var LayerAutosave = class extends EventTarget {
|
|
|
202
195
|
return;
|
|
203
196
|
}
|
|
204
197
|
if (this.currentState !== AutosaveState.Starting) return;
|
|
205
|
-
this.
|
|
198
|
+
this.saved = /* @__PURE__ */ new Map();
|
|
206
199
|
this.watch();
|
|
207
200
|
if (restored) this.remember();
|
|
208
201
|
this.setState(AutosaveState.Ready);
|
|
@@ -222,29 +215,32 @@ var LayerAutosave = class extends EventTarget {
|
|
|
222
215
|
return true;
|
|
223
216
|
}
|
|
224
217
|
/**
|
|
225
|
-
* Remember each layer
|
|
218
|
+
* Remember each layer as saved (just restored).
|
|
226
219
|
*/
|
|
227
220
|
remember() {
|
|
228
|
-
|
|
229
|
-
this.
|
|
230
|
-
|
|
221
|
+
const stack = this.canvas.layers;
|
|
222
|
+
for (const layer of stack.layers) this.saved.set(layer.id, this.record(layer));
|
|
223
|
+
}
|
|
224
|
+
/** A layer and its change count, to tell later whether it changed */
|
|
225
|
+
record(layer) {
|
|
226
|
+
return {
|
|
227
|
+
layer,
|
|
228
|
+
version: this.canvas.layers.pixelVersion(layer.id)
|
|
229
|
+
};
|
|
231
230
|
}
|
|
232
231
|
/**
|
|
233
|
-
* Save the drawing, encoding only the layers whose pixels changed since they were last saved.
|
|
232
|
+
* Save the drawing, encoding only the layers whose pixels changed since they were last saved (or that are new).
|
|
234
233
|
*/
|
|
235
234
|
async write() {
|
|
236
235
|
const stack = this.canvas.layers;
|
|
237
|
-
const
|
|
238
|
-
this.checkAll = false;
|
|
239
|
-
this.changed = /* @__PURE__ */ new Set();
|
|
240
|
-
const prints = /* @__PURE__ */ new Map();
|
|
236
|
+
const encoded = /* @__PURE__ */ new Map();
|
|
241
237
|
try {
|
|
242
238
|
const encoding = [];
|
|
243
239
|
for (const layer of stack.layers) {
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
247
|
-
|
|
240
|
+
const record = this.record(layer);
|
|
241
|
+
const saved = this.saved.get(layer.id);
|
|
242
|
+
if (saved?.layer === layer && saved.version === record.version) continue;
|
|
243
|
+
encoded.set(layer.id, record);
|
|
248
244
|
encoding.push(this.encode(layer.canvas).then((image) => [layer.id, image]));
|
|
249
245
|
}
|
|
250
246
|
const drawing = {
|
|
@@ -258,13 +254,11 @@ var LayerAutosave = class extends EventTarget {
|
|
|
258
254
|
if (this.currentState !== AutosaveState.Ready) return;
|
|
259
255
|
await this.store.save(drawing, images);
|
|
260
256
|
const kept = new Set(drawing.layers.map((layer) => layer.id));
|
|
261
|
-
for (const id of this.
|
|
262
|
-
for (const [id,
|
|
257
|
+
for (const id of this.saved.keys()) if (!kept.has(id)) this.saved.delete(id);
|
|
258
|
+
for (const [id, record] of encoded) this.saved.set(id, record);
|
|
263
259
|
this.error = void 0;
|
|
264
260
|
this.rememberSizes(kept, images);
|
|
265
261
|
} catch (error) {
|
|
266
|
-
for (const id of [...changed, ...prints.keys()]) this.changed.add(id);
|
|
267
|
-
this.checkAll ||= checkAll;
|
|
268
262
|
this.fail(error);
|
|
269
263
|
}
|
|
270
264
|
}
|
|
@@ -279,13 +273,6 @@ var LayerAutosave = class extends EventTarget {
|
|
|
279
273
|
if (this.storedBytes !== before) this.dispatchEvent(new Event("change"));
|
|
280
274
|
}
|
|
281
275
|
/**
|
|
282
|
-
* A fingerprint of a layer's pixels.
|
|
283
|
-
*/
|
|
284
|
-
fingerprint(id) {
|
|
285
|
-
const layer = this.canvas.layers.get(id);
|
|
286
|
-
return pixelFingerprint(layer.context.getImageData(0, 0, layer.width, layer.height).data);
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
276
|
* Save once the layers have stopped changing for a moment and drawing pauses.
|
|
290
277
|
*/
|
|
291
278
|
schedule() {
|
|
@@ -29,6 +29,8 @@ export default class LayerStack extends EventTarget {
|
|
|
29
29
|
/** The layer the change being announced is about, when only its pixels changed (see `changedLayerId`) */
|
|
30
30
|
private onlyChanged?;
|
|
31
31
|
private readonly createCanvas;
|
|
32
|
+
/** How many times each layer's image data has been changed through the stack (see `pixelVersion`) */
|
|
33
|
+
private readonly versions;
|
|
32
34
|
/** Most layers the stack can have */
|
|
33
35
|
readonly maxLayers: number;
|
|
34
36
|
constructor(width: number, height: number, createCanvas?: CanvasFactory, maxLayers?: number);
|
|
@@ -71,6 +73,17 @@ export default class LayerStack extends EventTarget {
|
|
|
71
73
|
* `markChanged`, e.g. a brush stroke); undefined when anything may have changed.
|
|
72
74
|
*/
|
|
73
75
|
get changedLayerId(): string | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* How many times a layer's image data has been changed through the stack (`markChanged`, `changeImage`, `clear`,
|
|
78
|
+
* merging into it, and undoing or redoing those), to tell whether it changed since some point without reading its
|
|
79
|
+
* pixels (which is slow on phones). A new layer starts at 0.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* const before = stack.pixelVersion(id);
|
|
83
|
+
* stack.markChanged(id);
|
|
84
|
+
* stack.pixelVersion(id) > before; // true
|
|
85
|
+
*/
|
|
86
|
+
pixelVersion(id: string): number;
|
|
74
87
|
/**
|
|
75
88
|
* Tell listeners a layer's image data changed (e.g. after a brush stroke), so thumbnails and the map redraw.
|
|
76
89
|
*/
|
|
@@ -150,6 +163,8 @@ export default class LayerStack extends EventTarget {
|
|
|
150
163
|
* An undoable change of image data: undo and redo both swap the stored pixels with the layer's, then select the layer.
|
|
151
164
|
*/
|
|
152
165
|
private imageEntry;
|
|
166
|
+
/** Count a change to a layer's image data */
|
|
167
|
+
private touch;
|
|
153
168
|
/**
|
|
154
169
|
* Create a layer with the stack's size and canvas factory.
|
|
155
170
|
*/
|
|
@@ -31,6 +31,8 @@ var LayerStack = class extends EventTarget {
|
|
|
31
31
|
/** The layer the change being announced is about, when only its pixels changed (see `changedLayerId`) */
|
|
32
32
|
onlyChanged;
|
|
33
33
|
createCanvas;
|
|
34
|
+
/** How many times each layer's image data has been changed through the stack (see `pixelVersion`) */
|
|
35
|
+
versions = /* @__PURE__ */ new WeakMap();
|
|
34
36
|
/** Most layers the stack can have */
|
|
35
37
|
maxLayers;
|
|
36
38
|
constructor(width, height, createCanvas = createOffscreenCanvas, maxLayers = 5) {
|
|
@@ -130,10 +132,23 @@ var LayerStack = class extends EventTarget {
|
|
|
130
132
|
return this.onlyChanged;
|
|
131
133
|
}
|
|
132
134
|
/**
|
|
135
|
+
* How many times a layer's image data has been changed through the stack (`markChanged`, `changeImage`, `clear`,
|
|
136
|
+
* merging into it, and undoing or redoing those), to tell whether it changed since some point without reading its
|
|
137
|
+
* pixels (which is slow on phones). A new layer starts at 0.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* const before = stack.pixelVersion(id);
|
|
141
|
+
* stack.markChanged(id);
|
|
142
|
+
* stack.pixelVersion(id) > before; // true
|
|
143
|
+
*/
|
|
144
|
+
pixelVersion(id) {
|
|
145
|
+
return this.versions.get(this.get(id)) ?? 0;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
133
148
|
* Tell listeners a layer's image data changed (e.g. after a brush stroke), so thumbnails and the map redraw.
|
|
134
149
|
*/
|
|
135
150
|
markChanged(id) {
|
|
136
|
-
this.get(id);
|
|
151
|
+
this.touch(this.get(id));
|
|
137
152
|
this.onlyChanged = id;
|
|
138
153
|
try {
|
|
139
154
|
this.notify();
|
|
@@ -150,6 +165,7 @@ var LayerStack = class extends EventTarget {
|
|
|
150
165
|
const area = contentBounds(data, this.width, this.height);
|
|
151
166
|
if (area) this.recordImage(id, area);
|
|
152
167
|
layer.clear();
|
|
168
|
+
this.touch(layer);
|
|
153
169
|
this.notify();
|
|
154
170
|
}
|
|
155
171
|
/**
|
|
@@ -174,7 +190,9 @@ var LayerStack = class extends EventTarget {
|
|
|
174
190
|
*/
|
|
175
191
|
changeImage(id, area, change) {
|
|
176
192
|
this.recordImage(id, area);
|
|
177
|
-
|
|
193
|
+
const layer = this.get(id);
|
|
194
|
+
change(layer.context);
|
|
195
|
+
this.touch(layer);
|
|
178
196
|
this.notify();
|
|
179
197
|
}
|
|
180
198
|
/**
|
|
@@ -209,6 +227,7 @@ var LayerStack = class extends EventTarget {
|
|
|
209
227
|
ctx.globalCompositeOperation = layer.blendMode;
|
|
210
228
|
ctx.drawImage(layer.canvas, x, y, width, height, x, y, width, height);
|
|
211
229
|
ctx.restore();
|
|
230
|
+
this.touch(below);
|
|
212
231
|
}
|
|
213
232
|
this.items = this.items.filter((l) => l !== layer);
|
|
214
233
|
this.activeId = below.id;
|
|
@@ -332,6 +351,7 @@ var LayerStack = class extends EventTarget {
|
|
|
332
351
|
ctx.clearRect(area.x, area.y, area.width, area.height);
|
|
333
352
|
ctx.drawImage(stored, area.x, area.y);
|
|
334
353
|
stored = current;
|
|
354
|
+
this.touch(layer);
|
|
335
355
|
this.select(id);
|
|
336
356
|
};
|
|
337
357
|
return {
|
|
@@ -339,6 +359,10 @@ var LayerStack = class extends EventTarget {
|
|
|
339
359
|
redo: swap
|
|
340
360
|
};
|
|
341
361
|
}
|
|
362
|
+
/** Count a change to a layer's image data */
|
|
363
|
+
touch(layer) {
|
|
364
|
+
this.versions.set(layer, this.pixelVersion(layer.id) + 1);
|
|
365
|
+
}
|
|
342
366
|
/**
|
|
343
367
|
* Create a layer with the stack's size and canvas factory.
|
|
344
368
|
*/
|