@lvl99dotcom/paintcanvas 0.0.0-stage → 0.3.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 (284) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +131 -2
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +5 -0
  5. package/dist/lib/brushes/BrushTipCache/index.d.ts +32 -0
  6. package/dist/lib/brushes/BrushTipCache/index.js +60 -0
  7. package/dist/lib/brushes/StrokeEngine/index.d.ts +171 -0
  8. package/dist/lib/brushes/StrokeEngine/index.js +324 -0
  9. package/dist/lib/brushes/StrokePath/index.d.ts +53 -0
  10. package/dist/lib/brushes/StrokePath/index.js +161 -0
  11. package/dist/lib/brushes/StrokeSampler/index.d.ts +55 -0
  12. package/dist/lib/brushes/StrokeSampler/index.js +75 -0
  13. package/dist/lib/brushes/authors/index.d.ts +55 -0
  14. package/dist/lib/brushes/authors/index.js +60 -0
  15. package/dist/lib/brushes/configRanges/index.d.ts +52 -0
  16. package/dist/lib/brushes/configRanges/index.js +206 -0
  17. package/dist/lib/brushes/index.d.ts +5 -0
  18. package/dist/lib/brushes/index.js +19 -0
  19. package/dist/lib/brushes/params/index.d.ts +108 -0
  20. package/dist/lib/brushes/params/index.js +139 -0
  21. package/dist/lib/brushes/presetFile/index.d.ts +32 -0
  22. package/dist/lib/brushes/presetFile/index.js +55 -0
  23. package/dist/lib/brushes/presets/drawing/brush-pen.js +3 -0
  24. package/dist/lib/brushes/presets/drawing/felt-pen.js +3 -0
  25. package/dist/lib/brushes/presets/drawing/index.d.ts +13 -0
  26. package/dist/lib/brushes/presets/drawing/index.js +88 -0
  27. package/dist/lib/brushes/presets/drawing/pencil-2b.js +3 -0
  28. package/dist/lib/brushes/presets/drawing/pencil-hb.js +3 -0
  29. package/dist/lib/brushes/presets/painting/grass.js +3 -0
  30. package/dist/lib/brushes/presets/painting/hair.js +3 -0
  31. package/dist/lib/brushes/presets/painting/index.d.ts +18 -0
  32. package/dist/lib/brushes/presets/painting/index.js +118 -0
  33. package/dist/lib/brushes/presets/painting/splatter.js +3 -0
  34. package/dist/lib/brushes/presets/painting/textured-paint.js +3 -0
  35. package/dist/lib/brushes/presets/painting/watery-paint.js +3 -0
  36. package/dist/lib/brushes/presets/round/index.d.ts +9 -0
  37. package/dist/lib/brushes/presets/round/index.js +44 -0
  38. package/dist/lib/brushes/presets/round/round.js +3 -0
  39. package/dist/lib/brushes/presets/round/soft-round.js +3 -0
  40. package/dist/lib/brushes/presets/sized/index.d.ts +20 -0
  41. package/dist/lib/brushes/presets/sized/index.js +30 -0
  42. package/dist/lib/brushes/presets/sketching/index.d.ts +14 -0
  43. package/dist/lib/brushes/presets/sketching/index.js +107 -0
  44. package/dist/lib/brushes/presets/square/index.d.ts +7 -0
  45. package/dist/lib/brushes/presets/square/index.js +22 -0
  46. package/dist/lib/brushes/presets/square/square.js +3 -0
  47. package/dist/lib/brushes/shapeImage/index.d.ts +40 -0
  48. package/dist/lib/brushes/shapeImage/index.js +61 -0
  49. package/dist/lib/brushes/types/index.d.ts +243 -0
  50. package/dist/lib/brushes/types/index.js +20 -0
  51. package/dist/lib/classes/BackgroundWork/index.d.ts +55 -0
  52. package/dist/lib/classes/BackgroundWork/index.js +97 -0
  53. package/dist/lib/classes/BrushSettings/index.d.ts +91 -0
  54. package/dist/lib/classes/BrushSettings/index.js +152 -0
  55. package/dist/lib/classes/CustomPresets/index.d.ts +53 -0
  56. package/dist/lib/classes/CustomPresets/index.js +71 -0
  57. package/dist/lib/classes/ExternalSheets/index.d.ts +64 -0
  58. package/dist/lib/classes/ExternalSheets/index.js +112 -0
  59. package/dist/lib/classes/GestureTracker/index.d.ts +62 -0
  60. package/dist/lib/classes/GestureTracker/index.js +122 -0
  61. package/dist/lib/classes/History/index.d.ts +83 -0
  62. package/dist/lib/classes/History/index.js +126 -0
  63. package/dist/lib/classes/HistoryGuard/index.d.ts +58 -0
  64. package/dist/lib/classes/HistoryGuard/index.js +97 -0
  65. package/dist/lib/classes/InputSettings/index.d.ts +74 -0
  66. package/dist/lib/classes/InputSettings/index.js +101 -0
  67. package/dist/lib/classes/KeyboardShortcuts/index.d.ts +48 -0
  68. package/dist/lib/classes/KeyboardShortcuts/index.js +137 -0
  69. package/dist/lib/classes/Layer/index.d.ts +86 -0
  70. package/dist/lib/classes/Layer/index.js +111 -0
  71. package/dist/lib/classes/LayerAutosave/index.d.ts +180 -0
  72. package/dist/lib/classes/LayerAutosave/index.js +346 -0
  73. package/dist/lib/classes/LayerStack/index.d.ts +177 -0
  74. package/dist/lib/classes/LayerStack/index.js +394 -0
  75. package/dist/lib/classes/PaintCanvas/index.d.ts +537 -0
  76. package/dist/lib/classes/PaintCanvas/index.js +965 -0
  77. package/dist/lib/classes/StrokeLog/index.d.ts +149 -0
  78. package/dist/lib/classes/StrokeLog/index.js +257 -0
  79. package/dist/lib/classes/Viewport/index.d.ts +108 -0
  80. package/dist/lib/classes/Viewport/index.js +194 -0
  81. package/dist/lib/document/index.d.ts +17 -0
  82. package/dist/lib/document/index.js +25 -0
  83. package/dist/lib/document/schema.d.ts +132 -0
  84. package/dist/lib/document/schema.js +110 -0
  85. package/dist/lib/i18n/index.d.ts +131 -0
  86. package/dist/lib/i18n/index.js +277 -0
  87. package/dist/lib/image/index.d.ts +90 -0
  88. package/dist/lib/image/index.js +147 -0
  89. package/dist/lib/messages/PatchSender/index.d.ts +75 -0
  90. package/dist/lib/messages/PatchSender/index.js +125 -0
  91. package/dist/lib/messages/index.d.ts +26 -0
  92. package/dist/lib/messages/index.js +59 -0
  93. package/dist/lib/messages/schema.d.ts +1775 -0
  94. package/dist/lib/messages/schema.js +192 -0
  95. package/dist/lib/presets/index.d.ts +32 -0
  96. package/dist/lib/presets/index.js +35 -0
  97. package/dist/lib/presets/schema.d.ts +753 -0
  98. package/dist/lib/presets/schema.js +181 -0
  99. package/dist/lib/rendering/BrushTip/index.d.ts +63 -0
  100. package/dist/lib/rendering/BrushTip/index.js +112 -0
  101. package/dist/lib/rendering/checkerboard/index.d.ts +22 -0
  102. package/dist/lib/rendering/checkerboard/index.js +31 -0
  103. package/dist/lib/rendering/compositing/index.d.ts +64 -0
  104. package/dist/lib/rendering/compositing/index.js +124 -0
  105. package/dist/lib/rendering/cursor/index.d.ts +14 -0
  106. package/dist/lib/rendering/cursor/index.js +39 -0
  107. package/dist/lib/rendering/cursors/index.d.ts +18 -0
  108. package/dist/lib/rendering/cursors/index.js +23 -0
  109. package/dist/lib/rendering/maskOutline/index.d.ts +22 -0
  110. package/dist/lib/rendering/maskOutline/index.js +78 -0
  111. package/dist/lib/rendering/outline/index.d.ts +36 -0
  112. package/dist/lib/rendering/outline/index.js +120 -0
  113. package/dist/lib/rendering/sample/index.d.ts +55 -0
  114. package/dist/lib/rendering/sample/index.js +83 -0
  115. package/dist/lib/rendering/screenshot/index.d.ts +67 -0
  116. package/dist/lib/rendering/screenshot/index.js +96 -0
  117. package/dist/lib/rendering/transformBox/index.d.ts +10 -0
  118. package/dist/lib/rendering/transformBox/index.js +57 -0
  119. package/dist/lib/selection/Lasso/index.d.ts +70 -0
  120. package/dist/lib/selection/Lasso/index.js +156 -0
  121. package/dist/lib/selection/Selection/index.d.ts +86 -0
  122. package/dist/lib/selection/Selection/index.js +228 -0
  123. package/dist/lib/selection/geometry/index.d.ts +37 -0
  124. package/dist/lib/selection/geometry/index.js +57 -0
  125. package/dist/lib/storage/index.d.ts +63 -0
  126. package/dist/lib/storage/index.js +114 -0
  127. package/dist/lib/transform/Transformer/index.d.ts +124 -0
  128. package/dist/lib/transform/Transformer/index.js +286 -0
  129. package/dist/lib/transform/geometry/index.d.ts +95 -0
  130. package/dist/lib/transform/geometry/index.js +258 -0
  131. package/dist/lib/utils/array/index.d.ts +10 -0
  132. package/dist/lib/utils/array/index.js +21 -0
  133. package/dist/lib/utils/gestures/index.d.ts +35 -0
  134. package/dist/lib/utils/gestures/index.js +81 -0
  135. package/dist/lib/utils/navigation/index.d.ts +51 -0
  136. package/dist/lib/utils/navigation/index.js +59 -0
  137. package/dist/lib/utils/random/index.d.ts +18 -0
  138. package/dist/lib/utils/random/index.js +27 -0
  139. package/dist/lib/utils/shortcuts/index.d.ts +74 -0
  140. package/dist/lib/utils/shortcuts/index.js +189 -0
  141. package/dist/lib/validation/validators.generated.d.ts +27 -0
  142. package/dist/lib/validation/validators.generated.js +19826 -0
  143. package/dist/locales/en-GB.js +181 -0
  144. package/dist/locales/en-US.js +181 -0
  145. package/dist/locales/fr.js +181 -0
  146. package/dist/locales/ja.js +181 -0
  147. package/dist/locales/ko.js +181 -0
  148. package/dist/locales/zh-Hans.js +181 -0
  149. package/dist/locales/zh-Hant.js +181 -0
  150. package/dist/package.js +3 -0
  151. package/dist/testing/app.d.ts +7 -0
  152. package/dist/testing/app.js +16 -0
  153. package/dist/testing/elements.d.ts +42 -0
  154. package/dist/testing/elements.js +58 -0
  155. package/dist/testing/fakeCanvas.d.ts +21 -0
  156. package/dist/testing/fakeCanvas.js +26 -0
  157. package/dist/testing/layerStore.d.ts +26 -0
  158. package/dist/testing/layerStore.js +43 -0
  159. package/dist/testing/locks.d.ts +15 -0
  160. package/dist/testing/locks.js +41 -0
  161. package/dist/testing/recordingCanvas.d.ts +14 -0
  162. package/dist/testing/recordingCanvas.js +28 -0
  163. package/dist/testing/recordingContext.d.ts +10 -0
  164. package/dist/testing/recordingContext.js +19 -0
  165. package/dist/ui/ChangeController/index.d.ts +40 -0
  166. package/dist/ui/ChangeController/index.js +62 -0
  167. package/dist/ui/base/index.d.ts +69 -0
  168. package/dist/ui/base/index.js +94 -0
  169. package/dist/ui/elements/PaintCanvas/index.d.ts +249 -0
  170. package/dist/ui/elements/PaintCanvas/index.js +584 -0
  171. package/dist/ui/elements/PaintCanvas/style.js +3 -0
  172. package/dist/ui/elements/PcBrushConfig/index.d.ts +130 -0
  173. package/dist/ui/elements/PcBrushConfig/index.js +465 -0
  174. package/dist/ui/elements/PcBrushConfig/style.js +3 -0
  175. package/dist/ui/elements/PcBrushMenu/index.d.ts +121 -0
  176. package/dist/ui/elements/PcBrushMenu/index.js +362 -0
  177. package/dist/ui/elements/PcBrushMenu/style.js +3 -0
  178. package/dist/ui/elements/PcBrushTest/index.d.ts +71 -0
  179. package/dist/ui/elements/PcBrushTest/index.js +136 -0
  180. package/dist/ui/elements/PcBrushTest/style.js +3 -0
  181. package/dist/ui/elements/PcButton/index.d.ts +57 -0
  182. package/dist/ui/elements/PcButton/index.js +124 -0
  183. package/dist/ui/elements/PcButtonGroup/index.d.ts +44 -0
  184. package/dist/ui/elements/PcButtonGroup/index.js +93 -0
  185. package/dist/ui/elements/PcCanvas/index.d.ts +356 -0
  186. package/dist/ui/elements/PcCanvas/index.js +1004 -0
  187. package/dist/ui/elements/PcCanvas/style.js +3 -0
  188. package/dist/ui/elements/PcCanvasMap/index.d.ts +96 -0
  189. package/dist/ui/elements/PcCanvasMap/index.js +187 -0
  190. package/dist/ui/elements/PcCanvasMap/style.js +3 -0
  191. package/dist/ui/elements/PcContextMenu/index.d.ts +72 -0
  192. package/dist/ui/elements/PcContextMenu/index.js +148 -0
  193. package/dist/ui/elements/PcDialog/index.d.ts +63 -0
  194. package/dist/ui/elements/PcDialog/index.js +114 -0
  195. package/dist/ui/elements/PcIcon/index.d.ts +36 -0
  196. package/dist/ui/elements/PcIcon/index.js +43 -0
  197. package/dist/ui/elements/PcIconToggle/index.d.ts +46 -0
  198. package/dist/ui/elements/PcIconToggle/index.js +87 -0
  199. package/dist/ui/elements/PcInfoTip/index.d.ts +40 -0
  200. package/dist/ui/elements/PcInfoTip/index.js +95 -0
  201. package/dist/ui/elements/PcInputSettings/index.d.ts +69 -0
  202. package/dist/ui/elements/PcInputSettings/index.js +154 -0
  203. package/dist/ui/elements/PcLayerItem/index.d.ts +221 -0
  204. package/dist/ui/elements/PcLayerItem/index.js +561 -0
  205. package/dist/ui/elements/PcLayerItem/style.js +3 -0
  206. package/dist/ui/elements/PcLayersMenu/index.d.ts +108 -0
  207. package/dist/ui/elements/PcLayersMenu/index.js +208 -0
  208. package/dist/ui/elements/PcLayersMenu/style.js +3 -0
  209. package/dist/ui/elements/PcMatch/index.d.ts +31 -0
  210. package/dist/ui/elements/PcMatch/index.js +58 -0
  211. package/dist/ui/elements/PcPanel/index.d.ts +43 -0
  212. package/dist/ui/elements/PcPanel/index.js +51 -0
  213. package/dist/ui/elements/PcPopover/index.d.ts +48 -0
  214. package/dist/ui/elements/PcPopover/index.js +86 -0
  215. package/dist/ui/elements/PcSlider/index.d.ts +57 -0
  216. package/dist/ui/elements/PcSlider/index.js +88 -0
  217. package/dist/ui/elements/PcStats/index.d.ts +54 -0
  218. package/dist/ui/elements/PcStats/index.js +144 -0
  219. package/dist/ui/elements/PcThemePicker/index.d.ts +56 -0
  220. package/dist/ui/elements/PcThemePicker/index.js +134 -0
  221. package/dist/ui/elements/PcToast/index.d.ts +58 -0
  222. package/dist/ui/elements/PcToast/index.js +110 -0
  223. package/dist/ui/elements/PcToggleCheckbox/index.d.ts +37 -0
  224. package/dist/ui/elements/PcToggleCheckbox/index.js +105 -0
  225. package/dist/ui/elements/PcToolSettings/index.d.ts +128 -0
  226. package/dist/ui/elements/PcToolSettings/index.js +549 -0
  227. package/dist/ui/elements/PcToolSettings/style.js +3 -0
  228. package/dist/ui/elements/PcToolbar/index.d.ts +71 -0
  229. package/dist/ui/elements/PcToolbar/index.js +190 -0
  230. package/dist/ui/elements/PcToolbar/style.js +3 -0
  231. package/dist/ui/icons/images/brush.js +3 -0
  232. package/dist/ui/icons/images/circle-mask.js +3 -0
  233. package/dist/ui/icons/images/clipboard.js +3 -0
  234. package/dist/ui/icons/images/cursor.js +3 -0
  235. package/dist/ui/icons/images/eraser.js +3 -0
  236. package/dist/ui/icons/images/eyedropper.js +3 -0
  237. package/dist/ui/icons/images/flip-horizontal.js +3 -0
  238. package/dist/ui/icons/images/flip-vertical.js +3 -0
  239. package/dist/ui/icons/images/focus-pen.js +3 -0
  240. package/dist/ui/icons/images/freehand-mask.js +3 -0
  241. package/dist/ui/icons/images/hidden.js +3 -0
  242. package/dist/ui/icons/images/layers.js +3 -0
  243. package/dist/ui/icons/images/linked.js +3 -0
  244. package/dist/ui/icons/images/locked.js +3 -0
  245. package/dist/ui/icons/images/moon.js +3 -0
  246. package/dist/ui/icons/images/pan.js +3 -0
  247. package/dist/ui/icons/images/polygon-mask.js +3 -0
  248. package/dist/ui/icons/images/pressure.js +3 -0
  249. package/dist/ui/icons/images/randomise.js +3 -0
  250. package/dist/ui/icons/images/rectangle-mask.js +3 -0
  251. package/dist/ui/icons/images/redo.js +3 -0
  252. package/dist/ui/icons/images/rotate-90-left.js +3 -0
  253. package/dist/ui/icons/images/rotate-90-right.js +3 -0
  254. package/dist/ui/icons/images/smoothing-bezier-cubic.js +3 -0
  255. package/dist/ui/icons/images/smoothing-bezier-quad.js +3 -0
  256. package/dist/ui/icons/images/smoothing-linear.js +3 -0
  257. package/dist/ui/icons/images/star.js +3 -0
  258. package/dist/ui/icons/images/sun.js +3 -0
  259. package/dist/ui/icons/images/tilt.js +3 -0
  260. package/dist/ui/icons/images/undo.js +3 -0
  261. package/dist/ui/icons/images/unlinked.js +3 -0
  262. package/dist/ui/icons/images/unlocked.js +3 -0
  263. package/dist/ui/icons/images/visible.js +3 -0
  264. package/dist/ui/icons/images/zoom-in.js +3 -0
  265. package/dist/ui/icons/images/zoom-out.js +3 -0
  266. package/dist/ui/icons/images/zoom.js +3 -0
  267. package/dist/ui/icons/index.d.ts +42 -0
  268. package/dist/ui/icons/index.js +130 -0
  269. package/dist/ui/index.d.ts +21 -0
  270. package/dist/ui/index.js +22 -0
  271. package/dist/ui/layout/index.d.ts +88 -0
  272. package/dist/ui/layout/index.js +96 -0
  273. package/dist/ui/styles/index.d.ts +13 -0
  274. package/dist/ui/styles/index.js +195 -0
  275. package/dist/ui/tokens/index.d.ts +29 -0
  276. package/dist/ui/tokens/index.js +114 -0
  277. package/locales/en-GB.json +179 -0
  278. package/locales/en-US.json +179 -0
  279. package/locales/fr.json +179 -0
  280. package/locales/ja.json +179 -0
  281. package/locales/ko.json +179 -0
  282. package/locales/zh-Hans.json +179 -0
  283. package/locales/zh-Hant.json +179 -0
  284. package/package.json +97 -3
@@ -0,0 +1,394 @@
1
+ import Layer, { createOffscreenCanvas } from "../Layer/index.js";
2
+ import History from "../History/index.js";
3
+ import { contentBounds, copyArea } from "../../transform/Transformer/index.js";
4
+ import { moveItem } from "../../utils/array/index.js";
5
+ // src/lib/classes/LayerStack/index.ts
6
+ /** Most layers a stack can have unless another limit is given */
7
+ var DEFAULT_MAX_LAYERS = 5;
8
+ /**
9
+ * Manages one user's ordered list of layers. Index 0 is the top-most layer.
10
+ *
11
+ * Dispatches a `change` event whenever layers or the active layer change.
12
+ *
13
+ * The user's actions are recorded in `history` so they can be undone: creating, duplicating, deleting and moving layers,
14
+ * changing a layer's blend mode or opacity, and changes to image data (only the changed area).
15
+ *
16
+ * @example
17
+ * const stack = new LayerStack(2000, 2000);
18
+ * stack.create(); // "Layer 2", placed above and selected
19
+ * stack.addEventListener("change", render);
20
+ */
21
+ var LayerStack = class extends EventTarget {
22
+ /** Canvas width in pixels */
23
+ width;
24
+ /** Canvas height in pixels */
25
+ height;
26
+ /** The user's undoable actions on these layers */
27
+ history = new History();
28
+ items = [];
29
+ activeId;
30
+ createdCount = 0;
31
+ /** The layer the change being announced is about, when only its pixels changed (see `changedLayerId`) */
32
+ onlyChanged;
33
+ createCanvas;
34
+ /** Most layers the stack can have */
35
+ maxLayers;
36
+ constructor(width, height, createCanvas = createOffscreenCanvas, maxLayers = 5) {
37
+ super();
38
+ this.width = width;
39
+ this.height = height;
40
+ this.createCanvas = createCanvas;
41
+ this.maxLayers = maxLayers;
42
+ const first = this.newLayer(this.nextDefaultName());
43
+ this.items = [first];
44
+ this.activeId = first.id;
45
+ }
46
+ /** Layers from top to bottom */
47
+ get layers() {
48
+ return this.items;
49
+ }
50
+ /** The selected layer that drawing applies to */
51
+ get active() {
52
+ return this.get(this.activeId);
53
+ }
54
+ /** Whether another layer can be created */
55
+ get canCreate() {
56
+ return this.items.length < this.maxLayers;
57
+ }
58
+ /**
59
+ * Get a layer by ID. Throws if not found.
60
+ */
61
+ get(id) {
62
+ const layer = this.items.find((l) => l.id === id);
63
+ if (!layer) throw new Error(`Layer ${id} not found`);
64
+ return layer;
65
+ }
66
+ /**
67
+ * Get a layer's index (0 = top). Throws if not found.
68
+ */
69
+ indexOf(id) {
70
+ return this.items.indexOf(this.get(id));
71
+ }
72
+ /**
73
+ * Select a layer.
74
+ */
75
+ select(id) {
76
+ this.activeId = this.get(id).id;
77
+ this.notify();
78
+ }
79
+ /**
80
+ * Create an empty layer above the active layer and select it.
81
+ */
82
+ create() {
83
+ this.assertCanCreate();
84
+ const layer = this.newLayer(this.nextDefaultName());
85
+ this.insertAboveActive(layer);
86
+ return layer;
87
+ }
88
+ /**
89
+ * Duplicate a layer (settings and image data) above the active layer and select it.
90
+ */
91
+ duplicate(id) {
92
+ this.assertCanCreate();
93
+ const source = this.get(id);
94
+ const { name, blendMode, opacity, visible, alphaLock } = source.toConfig();
95
+ const layer = new Layer({
96
+ name: `${name} copy`,
97
+ blendMode,
98
+ opacity,
99
+ visible,
100
+ alphaLock
101
+ }, this.width, this.height, this.createCanvas);
102
+ layer.copyImageFrom(source);
103
+ const previousId = this.activeId;
104
+ this.activeId = source.id;
105
+ this.insertAboveActive(layer, previousId);
106
+ return layer;
107
+ }
108
+ /**
109
+ * Replace all layers with new, empty ones (e.g. to restore a saved drawing into), top first, and select the top one.
110
+ * Returns the new layers.
111
+ *
112
+ * @example
113
+ * const [top, bottom] = stack.replace([{ name: "Sketch" }, { name: "Background", opacity: 0.5 }]);
114
+ */
115
+ replace(configs) {
116
+ if (configs.length === 0) throw new Error("Cannot have no layers");
117
+ if (configs.length > this.maxLayers) throw new Error(`Cannot have more than ${this.maxLayers} layers`);
118
+ this.items = configs.map((config) => new Layer(config, this.width, this.height, this.createCanvas));
119
+ this.activeId = this.items[0].id;
120
+ this.createdCount = this.items.length;
121
+ this.history.clear();
122
+ this.notify();
123
+ return this.items;
124
+ }
125
+ /**
126
+ * While a `change` event is being dispatched: the layer it's about, when only that layer's image data changed (from
127
+ * `markChanged`, e.g. a brush stroke); undefined when anything may have changed.
128
+ */
129
+ get changedLayerId() {
130
+ return this.onlyChanged;
131
+ }
132
+ /**
133
+ * Tell listeners a layer's image data changed (e.g. after a brush stroke), so thumbnails and the map redraw.
134
+ */
135
+ markChanged(id) {
136
+ this.get(id);
137
+ this.onlyChanged = id;
138
+ try {
139
+ this.notify();
140
+ } finally {
141
+ this.onlyChanged = void 0;
142
+ }
143
+ }
144
+ /**
145
+ * Remove all image data from a layer, as one undoable action.
146
+ */
147
+ clear(id) {
148
+ const layer = this.get(id);
149
+ const { data } = layer.context.getImageData(0, 0, this.width, this.height);
150
+ const area = contentBounds(data, this.width, this.height);
151
+ if (area) this.recordImage(id, area);
152
+ layer.clear();
153
+ this.notify();
154
+ }
155
+ /**
156
+ * Record a layer's image data in an area just before it changes, so the change can be undone. Pass `before` when the
157
+ * layer has already changed (a copy of the area's image data from before the change, the size of the area).
158
+ *
159
+ * @example
160
+ * stack.recordImage(layer.id, { x: 10, y: 10, width: 50, height: 20 });
161
+ * layer.context.fillRect(10, 10, 50, 20);
162
+ * stack.markChanged(layer.id);
163
+ */
164
+ recordImage(id, area, before) {
165
+ if (!this.history.recording) return;
166
+ const pixels = before ?? copyArea(this.createCanvas, this.get(id).canvas, area);
167
+ this.history.push(this.imageEntry(id, area, pixels));
168
+ }
169
+ /**
170
+ * Change a layer's image data within an area as one undoable action, then tell listeners.
171
+ *
172
+ * @example
173
+ * stack.changeImage(layer.id, area, (ctx) => ctx.clearRect(area.x, area.y, area.width, area.height));
174
+ */
175
+ changeImage(id, area, change) {
176
+ this.recordImage(id, area);
177
+ change(this.get(id).context);
178
+ this.notify();
179
+ }
180
+ /**
181
+ * Whether a layer can be merged into the layer under it: there is one, and both are visible.
182
+ */
183
+ canMergeDown(id) {
184
+ const below = this.items[this.indexOf(id) + 1];
185
+ return below !== void 0 && this.get(id).visible && below.visible;
186
+ }
187
+ /**
188
+ * Merge a layer into the layer under it as one undoable action, making the two one layer: its pixels are drawn onto
189
+ * the layer under it as they look (with its opacity and blend mode, whatever the alpha lock), and it is deleted. The
190
+ * merged layer keeps the lower layer's name and settings, and is selected. Does nothing unless `canMergeDown`.
191
+ *
192
+ * @example
193
+ * stack.mergeDown(stack.active.id);
194
+ */
195
+ mergeDown(id) {
196
+ if (!this.canMergeDown(id)) return;
197
+ const layer = this.get(id);
198
+ const index = this.indexOf(id);
199
+ const below = this.items[index + 1];
200
+ const { data } = layer.context.getImageData(0, 0, this.width, this.height);
201
+ const area = contentBounds(data, this.width, this.height);
202
+ const before = area && copyArea(this.createCanvas, below.canvas, area);
203
+ const merge = () => {
204
+ if (area) {
205
+ const ctx = below.context;
206
+ const { x, y, width, height } = area;
207
+ ctx.save();
208
+ ctx.globalAlpha = layer.opacity;
209
+ ctx.globalCompositeOperation = layer.blendMode;
210
+ ctx.drawImage(layer.canvas, x, y, width, height, x, y, width, height);
211
+ ctx.restore();
212
+ }
213
+ this.items = this.items.filter((l) => l !== layer);
214
+ this.activeId = below.id;
215
+ this.notify();
216
+ };
217
+ merge();
218
+ this.history.push({
219
+ undo: () => {
220
+ if (area && before) {
221
+ const ctx = below.context;
222
+ ctx.clearRect(area.x, area.y, area.width, area.height);
223
+ ctx.drawImage(before, area.x, area.y);
224
+ }
225
+ this.insertAt(layer, index);
226
+ },
227
+ redo: merge
228
+ });
229
+ }
230
+ /**
231
+ * Delete a layer. The last remaining layer cannot be deleted. If the active layer is deleted, the layer below it (or above, if it was the bottom) is selected.
232
+ */
233
+ remove(id) {
234
+ if (this.items.length === 1) throw new Error("Cannot delete the only layer");
235
+ const layer = this.get(id);
236
+ const index = this.indexOf(id);
237
+ this.history.push({
238
+ undo: () => this.insertAt(layer, index),
239
+ redo: () => this.remove(id)
240
+ });
241
+ this.items = this.items.filter((l) => l.id !== id);
242
+ if (this.activeId === id) this.activeId = this.items[Math.min(index, this.items.length - 1)].id;
243
+ this.notify();
244
+ }
245
+ /**
246
+ * Move a layer to a new index (0 = top). Layers in between shift to fill the gap.
247
+ */
248
+ move(id, toIndex) {
249
+ const fromIndex = this.indexOf(id);
250
+ this.items = moveItem(this.items, fromIndex, toIndex);
251
+ const movedTo = this.indexOf(id);
252
+ if (movedTo !== fromIndex) this.history.push({
253
+ undo: () => this.moveAndSelect(id, fromIndex),
254
+ redo: () => this.moveAndSelect(id, movedTo)
255
+ });
256
+ this.notify();
257
+ }
258
+ /**
259
+ * Move a layer up one place.
260
+ */
261
+ moveUp(id) {
262
+ this.move(id, this.indexOf(id) - 1);
263
+ }
264
+ /**
265
+ * Move a layer down one place.
266
+ */
267
+ moveDown(id) {
268
+ this.move(id, this.indexOf(id) + 1);
269
+ }
270
+ /**
271
+ * Move a layer to the top.
272
+ */
273
+ moveToTop(id) {
274
+ this.move(id, 0);
275
+ }
276
+ /**
277
+ * Move a layer to the bottom.
278
+ */
279
+ moveToBottom(id) {
280
+ this.move(id, this.items.length - 1);
281
+ }
282
+ /**
283
+ * Rename a layer.
284
+ */
285
+ rename(id, name) {
286
+ this.get(id).name = name;
287
+ this.notify();
288
+ }
289
+ /**
290
+ * Change a layer's settings (anything except ID and name). Changes to blend mode and opacity can be undone; visibility
291
+ * and alpha lock cannot.
292
+ */
293
+ update(id, changes) {
294
+ const layer = this.get(id);
295
+ const before = {
296
+ blendMode: layer.blendMode,
297
+ opacity: layer.opacity
298
+ };
299
+ Object.assign(layer, changes);
300
+ const after = {
301
+ blendMode: layer.blendMode,
302
+ opacity: layer.opacity
303
+ };
304
+ if (before.blendMode !== after.blendMode || before.opacity !== after.opacity) {
305
+ const apply = (settings) => {
306
+ Object.assign(this.get(id), settings);
307
+ this.select(id);
308
+ };
309
+ this.history.push({
310
+ undo: () => apply(before),
311
+ redo: () => apply(after)
312
+ });
313
+ }
314
+ this.notify();
315
+ }
316
+ /**
317
+ * Move a layer to an index and select it.
318
+ */
319
+ moveAndSelect(id, index) {
320
+ this.items = moveItem(this.items, this.indexOf(id), index);
321
+ this.select(id);
322
+ }
323
+ /**
324
+ * An undoable change of image data: undo and redo both swap the stored pixels with the layer's, then select the layer.
325
+ */
326
+ imageEntry(id, area, before) {
327
+ let stored = before;
328
+ const swap = () => {
329
+ const layer = this.get(id);
330
+ const current = copyArea(this.createCanvas, layer.canvas, area);
331
+ const ctx = layer.context;
332
+ ctx.clearRect(area.x, area.y, area.width, area.height);
333
+ ctx.drawImage(stored, area.x, area.y);
334
+ stored = current;
335
+ this.select(id);
336
+ };
337
+ return {
338
+ undo: swap,
339
+ redo: swap
340
+ };
341
+ }
342
+ /**
343
+ * Create a layer with the stack's size and canvas factory.
344
+ */
345
+ newLayer(name) {
346
+ return new Layer({ name }, this.width, this.height, this.createCanvas);
347
+ }
348
+ /**
349
+ * Get the next default layer name, e.g. "Layer 2".
350
+ */
351
+ nextDefaultName() {
352
+ this.createdCount += 1;
353
+ return `Layer ${this.createdCount}`;
354
+ }
355
+ /**
356
+ * Throw if the layer limit has been reached.
357
+ */
358
+ assertCanCreate() {
359
+ if (!this.canCreate) throw new Error(`Cannot have more than ${this.maxLayers} layers`);
360
+ }
361
+ /**
362
+ * Insert a layer directly above the active layer and select it, as one undoable action. Undo selects `previousId`.
363
+ */
364
+ insertAboveActive(layer, previousId = this.activeId) {
365
+ const index = this.indexOf(this.activeId);
366
+ this.insertAt(layer, index);
367
+ this.history.push({
368
+ undo: () => {
369
+ this.remove(layer.id);
370
+ this.select(previousId);
371
+ },
372
+ redo: () => this.insertAt(layer, index)
373
+ });
374
+ }
375
+ /**
376
+ * Insert a layer at an index (0 = top) and select it.
377
+ */
378
+ insertAt(layer, index) {
379
+ this.items = [
380
+ ...this.items.slice(0, index),
381
+ layer,
382
+ ...this.items.slice(index)
383
+ ];
384
+ this.activeId = layer.id;
385
+ this.notify();
386
+ }
387
+ /**
388
+ * Dispatch a `change` event.
389
+ */
390
+ notify() {
391
+ this.dispatchEvent(new Event("change"));
392
+ }
393
+ };
394
+ export { DEFAULT_MAX_LAYERS, LayerStack as default };