@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,194 @@
1
+ // src/lib/classes/Viewport/index.ts
2
+ /** Zoom steps used by the zoom tool, from 1/16 to 32× */
3
+ var ZOOM_LEVELS = [
4
+ .0625,
5
+ .125,
6
+ .25,
7
+ 1 / 3,
8
+ .5,
9
+ 2 / 3,
10
+ 1,
11
+ 1.5,
12
+ 2,
13
+ 3,
14
+ 4,
15
+ 6,
16
+ 8,
17
+ 12,
18
+ 16,
19
+ 24,
20
+ 32
21
+ ];
22
+ /** Smallest zoom */
23
+ var MIN_ZOOM = ZOOM_LEVELS[0];
24
+ /** Largest zoom */
25
+ var MAX_ZOOM = ZOOM_LEVELS[ZOOM_LEVELS.length - 1];
26
+ /** Screen space (CSS pixels) of canvas that must stay visible so it can't be panned out of reach */
27
+ var MIN_VISIBLE = 64;
28
+ /** Space left around the canvas when fitting it to the screen, in CSS pixels */
29
+ var FIT_PADDING = 24;
30
+ var NO_INSETS = {
31
+ top: 0,
32
+ right: 0,
33
+ bottom: 0,
34
+ left: 0
35
+ };
36
+ /**
37
+ * Clamp a value between min and max.
38
+ */
39
+ var clamp = (value, min, max) => Math.max(min, Math.min(max, value));
40
+ /**
41
+ * The user's view of the canvas: zoom level and position. Converts between screen and canvas coordinates.
42
+ *
43
+ * The canvas pixel at `(0, 0)` is drawn at screen position `(offsetX, offsetY)`, and one canvas pixel is `zoom` CSS pixels wide.
44
+ *
45
+ * Dispatches a `change` event whenever the view moves or zooms.
46
+ *
47
+ * @example
48
+ * const view = new Viewport(2000, 2000);
49
+ * view.resize(1280, 800);
50
+ * view.fit();
51
+ * view.zoomAt({ x: 640, y: 400 }, 2);
52
+ * view.screenToCanvas({ x: 640, y: 400 }); // canvas point under the screen centre
53
+ */
54
+ var Viewport = class extends EventTarget {
55
+ /** Canvas width in pixels */
56
+ canvasWidth;
57
+ /** Canvas height in pixels */
58
+ canvasHeight;
59
+ /** Screen width in CSS pixels */
60
+ width = 0;
61
+ /** Screen height in CSS pixels */
62
+ height = 0;
63
+ /** CSS pixels per canvas pixel */
64
+ zoom = 1;
65
+ /** Screen X of the canvas's left edge */
66
+ offsetX = 0;
67
+ /** Screen Y of the canvas's top edge */
68
+ offsetY = 0;
69
+ constructor(canvasWidth, canvasHeight) {
70
+ super();
71
+ this.canvasWidth = canvasWidth;
72
+ this.canvasHeight = canvasHeight;
73
+ }
74
+ /**
75
+ * Convert a screen point to canvas pixels.
76
+ */
77
+ screenToCanvas({ x, y }) {
78
+ return {
79
+ x: (x - this.offsetX) / this.zoom,
80
+ y: (y - this.offsetY) / this.zoom
81
+ };
82
+ }
83
+ /**
84
+ * Convert a canvas point to screen (CSS) pixels.
85
+ */
86
+ canvasToScreen({ x, y }) {
87
+ return {
88
+ x: x * this.zoom + this.offsetX,
89
+ y: y * this.zoom + this.offsetY
90
+ };
91
+ }
92
+ /**
93
+ * The part of the canvas currently visible on screen, clipped to the canvas.
94
+ */
95
+ visibleRect() {
96
+ const topLeft = this.screenToCanvas({
97
+ x: 0,
98
+ y: 0
99
+ });
100
+ const bottomRight = this.screenToCanvas({
101
+ x: this.width,
102
+ y: this.height
103
+ });
104
+ const x = clamp(topLeft.x, 0, this.canvasWidth);
105
+ const y = clamp(topLeft.y, 0, this.canvasHeight);
106
+ return {
107
+ x,
108
+ y,
109
+ width: clamp(bottomRight.x, 0, this.canvasWidth) - x,
110
+ height: clamp(bottomRight.y, 0, this.canvasHeight) - y
111
+ };
112
+ }
113
+ /**
114
+ * Set the screen size (e.g. when the window resizes). Keeps the view in reach.
115
+ */
116
+ resize(width, height) {
117
+ this.width = width;
118
+ this.height = height;
119
+ this.constrain();
120
+ this.notify();
121
+ }
122
+ /**
123
+ * Zoom and centre so the whole canvas fits in the space not covered by the interface (never zooming in past 100%).
124
+ */
125
+ fit(insets = NO_INSETS) {
126
+ const availableWidth = Math.max(1, this.width - insets.left - insets.right - 48);
127
+ const availableHeight = Math.max(1, this.height - insets.top - insets.bottom - 48);
128
+ this.zoom = clamp(Math.min(availableWidth / this.canvasWidth, availableHeight / this.canvasHeight, 1), MIN_ZOOM, MAX_ZOOM);
129
+ this.offsetX = insets.left + (this.width - insets.left - insets.right - this.canvasWidth * this.zoom) / 2;
130
+ this.offsetY = insets.top + (this.height - insets.top - insets.bottom - this.canvasHeight * this.zoom) / 2;
131
+ this.notify();
132
+ }
133
+ /**
134
+ * Move the view so a canvas point is in the middle of the space not covered by the interface (e.g. clicking the canvas map).
135
+ */
136
+ centreOn({ x, y }, insets = NO_INSETS) {
137
+ const centreX = insets.left + (this.width - insets.left - insets.right) / 2;
138
+ const centreY = insets.top + (this.height - insets.top - insets.bottom) / 2;
139
+ this.offsetX = centreX - x * this.zoom;
140
+ this.offsetY = centreY - y * this.zoom;
141
+ this.constrain();
142
+ this.notify();
143
+ }
144
+ /**
145
+ * Move the view by a distance in screen pixels (e.g. while dragging with the pan tool).
146
+ */
147
+ panBy(dx, dy) {
148
+ this.offsetX += dx;
149
+ this.offsetY += dy;
150
+ this.constrain();
151
+ this.notify();
152
+ }
153
+ /**
154
+ * Zoom to a level, keeping the canvas point under `anchor` (a screen point) in place.
155
+ */
156
+ zoomAt(anchor, zoom) {
157
+ const canvasPoint = this.screenToCanvas(anchor);
158
+ this.zoom = clamp(zoom, MIN_ZOOM, MAX_ZOOM);
159
+ this.offsetX = anchor.x - canvasPoint.x * this.zoom;
160
+ this.offsetY = anchor.y - canvasPoint.y * this.zoom;
161
+ this.constrain();
162
+ this.notify();
163
+ }
164
+ /**
165
+ * Zoom in one step around a screen point (zoom tool click).
166
+ */
167
+ zoomIn(anchor) {
168
+ this.zoomAt(anchor, ZOOM_LEVELS.find((level) => level > this.zoom + 1e-9) ?? MAX_ZOOM);
169
+ }
170
+ /**
171
+ * Zoom out one step around a screen point (zoom tool Alt+click).
172
+ */
173
+ zoomOut(anchor) {
174
+ this.zoomAt(anchor, [...ZOOM_LEVELS].reverse().find((level) => level < this.zoom - 1e-9) ?? MIN_ZOOM);
175
+ }
176
+ /**
177
+ * Keep at least `MIN_VISIBLE` pixels of the canvas on screen in each direction.
178
+ */
179
+ constrain() {
180
+ const canvasScreenWidth = this.canvasWidth * this.zoom;
181
+ const canvasScreenHeight = this.canvasHeight * this.zoom;
182
+ const keepX = Math.min(64, canvasScreenWidth);
183
+ const keepY = Math.min(64, canvasScreenHeight);
184
+ this.offsetX = clamp(this.offsetX, keepX - canvasScreenWidth, this.width - keepX);
185
+ this.offsetY = clamp(this.offsetY, keepY - canvasScreenHeight, this.height - keepY);
186
+ }
187
+ /**
188
+ * Dispatch a `change` event.
189
+ */
190
+ notify() {
191
+ this.dispatchEvent(new Event("change"));
192
+ }
193
+ };
194
+ export { FIT_PADDING, MAX_ZOOM, MIN_VISIBLE, MIN_ZOOM, ZOOM_LEVELS, Viewport as default };
@@ -0,0 +1,17 @@
1
+ import type { PaintCanvasDocument } from "./schema";
2
+ export * from "./schema";
3
+ /** Result of checking a document */
4
+ export type DocumentValidationResult = {
5
+ valid: true;
6
+ value: PaintCanvasDocument;
7
+ } | {
8
+ valid: false;
9
+ errors: string[];
10
+ };
11
+ /**
12
+ * Check a document (e.g. read from a file) against `documentSchema`, and that its selected layer exists.
13
+ *
14
+ * @example
15
+ * isValidDocument(JSON.parse(text)).valid; // true
16
+ */
17
+ export declare const isValidDocument: (input: unknown) => DocumentValidationResult;
@@ -0,0 +1,25 @@
1
+ import { formatErrors } from "../presets/index.js";
2
+ import { validateDocument } from "../validation/validators.generated.js";
3
+ import { DOCUMENT_BLEND_MODES, DOCUMENT_FORMAT, DOCUMENT_VERSION, MAX_DOCUMENT_IMAGE_LENGTH, MAX_DOCUMENT_LAYERS, MAX_DOCUMENT_SHEETS, MAX_DOCUMENT_SIZE, documentSchema } from "./schema.js";
4
+ // src/lib/document/index.ts
5
+ /**
6
+ * Check a document (e.g. read from a file) against `documentSchema`, and that its selected layer exists.
7
+ *
8
+ * @example
9
+ * isValidDocument(JSON.parse(text)).valid; // true
10
+ */
11
+ var isValidDocument = (input) => {
12
+ if (!validateDocument(input)) return {
13
+ valid: false,
14
+ errors: formatErrors(validateDocument.errors)
15
+ };
16
+ if (input.activeLayer >= input.layers.length) return {
17
+ valid: false,
18
+ errors: ["/activeLayer must be one of the layers"]
19
+ };
20
+ return {
21
+ valid: true,
22
+ value: input
23
+ };
24
+ };
25
+ export { DOCUMENT_BLEND_MODES, DOCUMENT_FORMAT, DOCUMENT_VERSION, MAX_DOCUMENT_IMAGE_LENGTH, MAX_DOCUMENT_LAYERS, MAX_DOCUMENT_SHEETS, MAX_DOCUMENT_SIZE, documentSchema, isValidDocument };
@@ -0,0 +1,132 @@
1
+ /** The `format` of a PaintCanvas document */
2
+ export declare const DOCUMENT_FORMAT = "paintcanvas-document";
3
+ /** Version of the document format */
4
+ export declare const DOCUMENT_VERSION = 1;
5
+ /** Largest canvas width or height a document may have */
6
+ export declare const MAX_DOCUMENT_SIZE = 10000;
7
+ /** Most layers in a document (a canvas may allow fewer, see `maxLayers`) */
8
+ export declare const MAX_DOCUMENT_LAYERS = 100;
9
+ /** Most drawings from elsewhere in a document */
10
+ export declare const MAX_DOCUMENT_SHEETS = 100;
11
+ /** Longest image in a document (characters): a PNG data URL of up to 100 MB */
12
+ export declare const MAX_DOCUMENT_IMAGE_LENGTH: number;
13
+ /** Blend modes a layer may have (see `BlendMode`) */
14
+ export declare const DOCUMENT_BLEND_MODES: readonly ["source-over", "screen", "lighten", "multiply", "darken", "overlay"];
15
+ /** A layer in a document */
16
+ export interface DocumentLayer {
17
+ name: string;
18
+ visible: boolean;
19
+ opacity: number;
20
+ blendMode: (typeof DOCUMENT_BLEND_MODES)[number];
21
+ alphaLock: boolean;
22
+ /** The layer's pixels as a PNG data URL, the size of the canvas */
23
+ image: string;
24
+ }
25
+ /** A drawing from elsewhere in a document (see `ExternalSheets`) */
26
+ export interface DocumentSheet {
27
+ /** Where it came from */
28
+ source: string;
29
+ /** Its pixels as a PNG data URL, the size of the canvas */
30
+ image: string;
31
+ }
32
+ /** A saved drawing */
33
+ export interface PaintCanvasDocument {
34
+ format: typeof DOCUMENT_FORMAT;
35
+ version: typeof DOCUMENT_VERSION;
36
+ width: number;
37
+ height: number;
38
+ /** The user's layers, top first */
39
+ layers: DocumentLayer[];
40
+ /** Index of the selected layer in `layers` */
41
+ activeLayer: number;
42
+ /** Drawings from elsewhere, top first */
43
+ external: DocumentSheet[];
44
+ }
45
+ /** JSON schema for a document */
46
+ export declare const documentSchema: {
47
+ readonly $id: "paintcanvas/document";
48
+ readonly type: "object";
49
+ readonly additionalProperties: false;
50
+ readonly required: readonly ["format", "version", "width", "height", "layers", "activeLayer", "external"];
51
+ readonly properties: {
52
+ readonly format: {
53
+ readonly const: "paintcanvas-document";
54
+ };
55
+ readonly version: {
56
+ readonly const: 1;
57
+ };
58
+ readonly width: {
59
+ readonly type: "integer";
60
+ readonly minimum: 1;
61
+ readonly maximum: 10000;
62
+ };
63
+ readonly height: {
64
+ readonly type: "integer";
65
+ readonly minimum: 1;
66
+ readonly maximum: 10000;
67
+ };
68
+ readonly layers: {
69
+ readonly type: "array";
70
+ readonly minItems: 1;
71
+ readonly maxItems: 100;
72
+ readonly items: {
73
+ readonly type: "object";
74
+ readonly additionalProperties: false;
75
+ readonly required: readonly ["name", "visible", "opacity", "blendMode", "alphaLock", "image"];
76
+ readonly properties: {
77
+ readonly name: {
78
+ readonly type: "string";
79
+ readonly minLength: 1;
80
+ readonly maxLength: 64;
81
+ readonly pattern: "\\S";
82
+ };
83
+ readonly visible: {
84
+ readonly type: "boolean";
85
+ };
86
+ readonly opacity: {
87
+ readonly type: "number";
88
+ readonly minimum: 0;
89
+ readonly maximum: 1;
90
+ };
91
+ readonly blendMode: {
92
+ readonly enum: readonly ["source-over", "screen", "lighten", "multiply", "darken", "overlay"];
93
+ };
94
+ readonly alphaLock: {
95
+ readonly type: "boolean";
96
+ };
97
+ readonly image: {
98
+ readonly type: "string";
99
+ readonly pattern: "^data:image/png;base64,[A-Za-z0-9+/]*={0,2}$";
100
+ readonly maxLength: number;
101
+ };
102
+ };
103
+ };
104
+ };
105
+ readonly activeLayer: {
106
+ readonly type: "integer";
107
+ readonly minimum: 0;
108
+ readonly maximum: number;
109
+ };
110
+ readonly external: {
111
+ readonly type: "array";
112
+ readonly maxItems: 100;
113
+ readonly items: {
114
+ readonly type: "object";
115
+ readonly additionalProperties: false;
116
+ readonly required: readonly ["source", "image"];
117
+ readonly properties: {
118
+ readonly source: {
119
+ readonly type: "string";
120
+ readonly minLength: 1;
121
+ readonly maxLength: 2048;
122
+ };
123
+ readonly image: {
124
+ readonly type: "string";
125
+ readonly pattern: "^data:image/png;base64,[A-Za-z0-9+/]*={0,2}$";
126
+ readonly maxLength: number;
127
+ };
128
+ };
129
+ };
130
+ };
131
+ };
132
+ };
@@ -0,0 +1,110 @@
1
+ // src/lib/document/schema.ts
2
+ /** The `format` of a PaintCanvas document */
3
+ var DOCUMENT_FORMAT = "paintcanvas-document";
4
+ /** Version of the document format */
5
+ var DOCUMENT_VERSION = 1;
6
+ /** Largest canvas width or height a document may have */
7
+ var MAX_DOCUMENT_SIZE = 1e4;
8
+ /** Most layers in a document (a canvas may allow fewer, see `maxLayers`) */
9
+ var MAX_DOCUMENT_LAYERS = 100;
10
+ /** Most drawings from elsewhere in a document */
11
+ var MAX_DOCUMENT_SHEETS = 100;
12
+ /** Longest image in a document (characters): a PNG data URL of up to 100 MB */
13
+ var MAX_DOCUMENT_IMAGE_LENGTH = Math.ceil(104857600 / 3) * 4 + 22;
14
+ /** Blend modes a layer may have (see `BlendMode`) */
15
+ var DOCUMENT_BLEND_MODES = [
16
+ "source-over",
17
+ "screen",
18
+ "lighten",
19
+ "multiply",
20
+ "darken",
21
+ "overlay"
22
+ ];
23
+ var image = {
24
+ type: "string",
25
+ pattern: "^data:image/png;base64,[A-Za-z0-9+/]*={0,2}$",
26
+ maxLength: MAX_DOCUMENT_IMAGE_LENGTH
27
+ };
28
+ var size = {
29
+ type: "integer",
30
+ minimum: 1,
31
+ maximum: MAX_DOCUMENT_SIZE
32
+ };
33
+ /** JSON schema for a document */
34
+ var documentSchema = {
35
+ $id: "paintcanvas/document",
36
+ type: "object",
37
+ additionalProperties: false,
38
+ required: [
39
+ "format",
40
+ "version",
41
+ "width",
42
+ "height",
43
+ "layers",
44
+ "activeLayer",
45
+ "external"
46
+ ],
47
+ properties: {
48
+ format: { const: DOCUMENT_FORMAT },
49
+ version: { const: 1 },
50
+ width: size,
51
+ height: size,
52
+ layers: {
53
+ type: "array",
54
+ minItems: 1,
55
+ maxItems: 100,
56
+ items: {
57
+ type: "object",
58
+ additionalProperties: false,
59
+ required: [
60
+ "name",
61
+ "visible",
62
+ "opacity",
63
+ "blendMode",
64
+ "alphaLock",
65
+ "image"
66
+ ],
67
+ properties: {
68
+ name: {
69
+ type: "string",
70
+ minLength: 1,
71
+ maxLength: 64,
72
+ pattern: "\\S"
73
+ },
74
+ visible: { type: "boolean" },
75
+ opacity: {
76
+ type: "number",
77
+ minimum: 0,
78
+ maximum: 1
79
+ },
80
+ blendMode: { enum: DOCUMENT_BLEND_MODES },
81
+ alphaLock: { type: "boolean" },
82
+ image
83
+ }
84
+ }
85
+ },
86
+ activeLayer: {
87
+ type: "integer",
88
+ minimum: 0,
89
+ maximum: 99
90
+ },
91
+ external: {
92
+ type: "array",
93
+ maxItems: 100,
94
+ items: {
95
+ type: "object",
96
+ additionalProperties: false,
97
+ required: ["source", "image"],
98
+ properties: {
99
+ source: {
100
+ type: "string",
101
+ minLength: 1,
102
+ maxLength: 2048
103
+ },
104
+ image
105
+ }
106
+ }
107
+ }
108
+ }
109
+ };
110
+ export { DOCUMENT_BLEND_MODES, DOCUMENT_FORMAT, DOCUMENT_VERSION, MAX_DOCUMENT_IMAGE_LENGTH, MAX_DOCUMENT_LAYERS, MAX_DOCUMENT_SHEETS, MAX_DOCUMENT_SIZE, documentSchema };
@@ -0,0 +1,131 @@
1
+ /** Translated text, by key */
2
+ export type Strings = Record<string, string>;
3
+ /** Loads a locale's strings (e.g. a dynamic `import()` of its JSON file, so it's only downloaded when used) */
4
+ export type LocaleLoader = () => Promise<Strings | {
5
+ default: Strings;
6
+ }>;
7
+ /** The locale used when no other matches, and whose strings fill in any missing from other locales */
8
+ export declare const FALLBACK_LOCALE = "en-US";
9
+ /** Locales whose files come with PaintCanvas (in `locales/`, named by their IETF BCP 47 tag) */
10
+ export declare const BUILT_IN_LOCALES: Record<string, LocaleLoader>;
11
+ /**
12
+ * The best available locale for the requested ones (e.g. the device's `navigator.languages`, most preferred first):
13
+ * the same tag, then the same language and script (`zh-TW` → `zh-Hant`), then the same language (British regions
14
+ * prefer `en-GB`, other English `en-US`), otherwise `fallback`.
15
+ *
16
+ * @example
17
+ * matchLocale(["zh-TW", "en"], ["en-US", "zh-Hans", "zh-Hant"]); // "zh-Hant"
18
+ * matchLocale(["en-AU"], ["en-US", "en-GB"]); // "en-GB"
19
+ * matchLocale(["fr-FR"], ["en-US", "ja"]); // "en-US"
20
+ */
21
+ export declare const matchLocale: (requested: readonly string[], available: readonly string[], fallback?: string) => string;
22
+ /**
23
+ * Put values into a string's `{name}` placeholders. Placeholders without a value are left as they are.
24
+ *
25
+ * @example
26
+ * interpolate("Delete \"{name}\"?", { name: "Sketch" }); // 'Delete "Sketch"?'
27
+ */
28
+ export declare const interpolate: (text: string, values?: Record<string, string | number>) => string;
29
+ /**
30
+ * A locale's language name, written in that language (so people can find their own), with its first letter in
31
+ * capitals. Gives the tag itself if the browser can't name it.
32
+ *
33
+ * @example
34
+ * languageName("ja"); // "日本語"
35
+ * languageName("fr"); // "Français"
36
+ * languageName("en-GB"); // "English (United Kingdom)"
37
+ */
38
+ export declare const languageName: (locale: string) => string;
39
+ /** Options for translations */
40
+ export interface TranslationsOptions {
41
+ /** Locales that can be used, and how to load each (default: all the locales that come with PaintCanvas) */
42
+ locales?: Record<string, LocaleLoader>;
43
+ /** Locale used when no other matches (default `FALLBACK_LOCALE`) */
44
+ fallback?: string;
45
+ /** The fallback locale's strings, available straight away (default: PaintCanvas's US English) */
46
+ fallbackStrings?: Strings;
47
+ }
48
+ /**
49
+ * The text the UI shows, in the chosen language. Starts in the fallback locale; `useDeviceLocale` switches to the best
50
+ * match for the device, and `setLocale` to any available locale. Locale files load when first used, without reloading
51
+ * the page. Strings missing from a locale come from the fallback locale, then the key itself.
52
+ *
53
+ * Other projects can add their own strings and locales with `addLocales` and `addStrings`. A locale's strings from
54
+ * every set load together and are merged in the order the sets were added (PaintCanvas's first), so a key in a later
55
+ * set replaces the same key in an earlier one. Keys are prefixed by project by convention: `paintCanvas.` for
56
+ * PaintCanvas's, and e.g. `liveCanvas.` for an app's.
57
+ *
58
+ * Dispatches a `change` event when the locale or its strings change, so the UI can show the new text.
59
+ *
60
+ * @example
61
+ * const translations = new Translations({ locales: { "en-US": ..., ja: () => import("./ja.json") } });
62
+ * await translations.useDeviceLocale();
63
+ * translations.t("paintCanvas.layers.delete", { name: "Sketch" });
64
+ */
65
+ export default class Translations extends EventTarget {
66
+ private readonly fallback;
67
+ /** Sets of strings, in the order they were added */
68
+ private readonly sources;
69
+ /** Each locale's strings from every set loaded so far, merged in order */
70
+ private readonly merged;
71
+ private current;
72
+ /** The locale most recently asked for, so a slow load can't override a later choice */
73
+ private requested?;
74
+ constructor(options?: TranslationsOptions);
75
+ /** The locale in use (an IETF BCP 47 tag, e.g. `ja`) */
76
+ get locale(): string;
77
+ /** Locales that can be used: the fallback, and every locale a set of strings can load */
78
+ get locales(): string[];
79
+ /**
80
+ * Switch to the best available locale for the device's languages (`navigator.languages`).
81
+ */
82
+ useDeviceLocale(languages?: readonly string[]): Promise<void>;
83
+ /**
84
+ * Switch to a locale (the best available match for the tag), loading its strings from every set if needed.
85
+ * Dispatches `change` once they're ready. If loading fails, the current locale stays and this rejects.
86
+ *
87
+ * @example
88
+ * await translations.setLocale("zh-TW"); // uses zh-Hant
89
+ */
90
+ setLocale(tag: string): Promise<void>;
91
+ /**
92
+ * Add a set of strings for some locales, loaded when first used, after the sets already added (so its keys replace
93
+ * theirs). Its locales become available. The locale in use and the fallback load straight away, then `change` is
94
+ * dispatched if the set has strings for either.
95
+ *
96
+ * @example
97
+ * await translations.addLocales({
98
+ * "en-US": () => import("./locales/en-US.json"),
99
+ * fr: () => import("./locales/fr.json"),
100
+ * });
101
+ * translations.t("liveCanvas.share");
102
+ */
103
+ addLocales(loaders: Record<string, LocaleLoader>): Promise<void>;
104
+ /**
105
+ * Add or replace strings for a locale (e.g. the implementer's own wording or extra keys), after the sets already
106
+ * added. Dispatches `change` if it's the locale in use or the fallback.
107
+ *
108
+ * @example
109
+ * translations.addStrings("en-US", { "paintCanvas.tool.brush": "Paintbrush" });
110
+ */
111
+ addStrings(locale: string, strings: Strings): void;
112
+ /**
113
+ * Load a locale's strings from every set, including sets added while loading, then merge them.
114
+ */
115
+ private load;
116
+ /**
117
+ * Merge a locale's strings loaded so far from every set, in the order the sets were added.
118
+ */
119
+ private merge;
120
+ /**
121
+ * Translate a key, putting values into its `{name}` placeholders. With a numeric `count`, the key's plural form for
122
+ * the locale is used when there is one (e.g. `paintCanvas.presets.imported.one` and
123
+ * `paintCanvas.presets.imported.other`). Falls back to the fallback locale, then the key.
124
+ *
125
+ * @example
126
+ * translations.t("paintCanvas.presets.imported", { count: 3 }); // "Imported 3 brush presets"
127
+ */
128
+ t(key: string, values?: Record<string, string | number>): string;
129
+ }
130
+ /** The translations PaintCanvas's UI uses, unless an element is given others */
131
+ export declare const translations: Translations;