@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,149 @@
1
+ /** Default number of log entries kept */
2
+ export declare const DEFAULT_STROKE_LOG_LIMIT = 200;
3
+ /**
4
+ * What a stroke log entry is about:
5
+ * - `start`: a stroke started
6
+ * - `end`: a stroke finished, with its measurements
7
+ * - `cancel`: a stroke in progress was abandoned, with its measurements so far
8
+ * - `ignore`: a pointer press didn't start a stroke
9
+ * - `gesture`: a touch gesture did an action (e.g. undo)
10
+ * - `work`: other work that kept the page busy (e.g. syncing the drawing)
11
+ */
12
+ export type StrokeLogKind = "start" | "end" | "cancel" | "ignore" | "gesture" | "work";
13
+ /** A logged event */
14
+ export interface StrokeLogEntry {
15
+ /** Time the entry was logged (Unix epoch milliseconds) */
16
+ at: number;
17
+ kind: StrokeLogKind;
18
+ /** What happened, e.g. the pointer type, why a stroke was cancelled, or the work done */
19
+ detail: string;
20
+ /** Measurements by name: times in milliseconds, otherwise counts */
21
+ measures: Record<string, number>;
22
+ }
23
+ /** What was on screen when a stroke started */
24
+ export interface StrokeScene {
25
+ /** Layers drawn (visible, including other people's drawings) */
26
+ layers: number;
27
+ /** Zoom as a percentage */
28
+ zoomPct: number;
29
+ }
30
+ /**
31
+ * Records when brush and eraser strokes start, end, are cancelled or are ignored, with timings for each stroke, plus
32
+ * other work done meanwhile (see `BackgroundWork`), for the debug panel. Times are in the same clock as `event.timeStamp` and
33
+ * `performance.now()`. Keeps only the most recent entries.
34
+ *
35
+ * Each finished or cancelled stroke measures (in ms unless noted):
36
+ * - `gap`: time since the previous stroke ended
37
+ * - `wait`: how long the press waited before it was handled (the page was busy)
38
+ * - `begin`: time taken to start the stroke
39
+ * - `moves`: pointer events handled (count), `maxMoveWait`: longest one waited, `moveMs`: total time handling them
40
+ * - `frames`: frames drawn (count), `maxFrame`: longest frame to draw, `maxFrameGap`: longest time between frames
41
+ * (including after the last one), `overlays`: times the brush cursor was drawn (count)
42
+ * - `end`: time taken to write the stroke into its layer
43
+ * - `duration`: from press to release
44
+ * - `layers`: layers drawn (count) and `zoomPct`: the zoom as a percentage, when the stroke started (if given)
45
+ *
46
+ * Records nothing while switched off (`enabled`); work given to `measure` still runs.
47
+ *
48
+ * Dispatches a `change` event when entries are added or cleared.
49
+ *
50
+ * @example
51
+ * const log = new StrokeLog();
52
+ * log.begin("pen", event.timeStamp, 2);
53
+ * log.move(event.timeStamp, 0.5);
54
+ * log.end(event.timeStamp, 3); // entry with kind "end" and its measures
55
+ */
56
+ export default class StrokeLog extends EventTarget {
57
+ /** Maximum number of entries kept */
58
+ readonly limit: number;
59
+ /** Current time in milliseconds (replaceable for tests) */
60
+ now: () => number;
61
+ /** Whether anything is recorded */
62
+ enabled: boolean;
63
+ private items;
64
+ private stroke?;
65
+ /** When the last stroke ended or was cancelled (event time) */
66
+ private lastEnd?;
67
+ constructor(limit?: number);
68
+ /** Logged entries, oldest first */
69
+ get entries(): readonly StrokeLogEntry[];
70
+ /** Whether a stroke is being measured */
71
+ get drawing(): boolean;
72
+ /**
73
+ * A stroke started: the pointer type, when it pressed (event time), how long starting the stroke took, and what was on
74
+ * screen.
75
+ */
76
+ begin(pointerType: string, eventTime: number, beginMs: number, scene?: StrokeScene): void;
77
+ /**
78
+ * The stroke continued: when the pointer moved (event time) and how long handling it took.
79
+ */
80
+ move(eventTime: number, moveMs: number): void;
81
+ /**
82
+ * A frame was drawn, taking `ms`; counted towards the stroke in progress, if any.
83
+ */
84
+ frame(ms: number): void;
85
+ /**
86
+ * The brush cursor was drawn; counted towards the stroke in progress, if any.
87
+ */
88
+ overlay(): void;
89
+ /**
90
+ * The stroke finished: when the pointer released (event time) and how long writing it into the layer took.
91
+ */
92
+ end(eventTime: number, endMs: number): void;
93
+ /**
94
+ * The stroke in progress was abandoned, and why.
95
+ */
96
+ cancel(reason: string): void;
97
+ /**
98
+ * A pointer press didn't start a stroke, and why.
99
+ */
100
+ ignore(pointerType: string, reason: string): void;
101
+ /**
102
+ * A touch gesture did an action.
103
+ */
104
+ gesture(gesture: string, action: string): void;
105
+ /**
106
+ * Other work was done, with its measurements: `ms` for how long it kept the page busy, and `total` for how long work
107
+ * that finishes later took altogether (which can include waiting that doesn't keep the page busy).
108
+ */
109
+ work(name: string, measures: {
110
+ ms?: number;
111
+ total?: number;
112
+ }): void;
113
+ /**
114
+ * Run some work and return its result, logging how long it kept the page busy, or for work that finishes later (a
115
+ * promise), how long it took altogether once it settles.
116
+ *
117
+ * @example
118
+ * log.measure("map", () => drawMap()); // logs "map" with how long it took
119
+ * log.measure("save", () => save()); // logs "save" with how long it took once saved (or failed)
120
+ */
121
+ measure<T>(name: string, run: () => T): T;
122
+ /**
123
+ * Remove all entries.
124
+ */
125
+ clear(): void;
126
+ /** Log a finished or cancelled stroke with its measurements, and stop measuring it */
127
+ private finish;
128
+ /** Add an entry, rounding its measurements (unless switched off) */
129
+ private add;
130
+ }
131
+ /** The stroke log's CSV columns after time, event and detail: every measurement, in order */
132
+ export declare const STROKE_LOG_COLUMNS: readonly ["gap", "wait", "begin", "moves", "maxMoveWait", "moveMs", "frames", "maxFrame", "maxFrameGap", "overlays", "end", "duration", "layers", "zoomPct", "ms", "total"];
133
+ /**
134
+ * A CSV field, quoted if it holds a comma, quote or line break.
135
+ *
136
+ * @example
137
+ * csvField('a "b", c'); // '"a ""b"", c"'
138
+ */
139
+ export declare const csvField: (value: string) => string;
140
+ /**
141
+ * Stroke log entries as CSV, oldest first, to paste somewhere for analysis: `#` lines describing the context first (e.g.
142
+ * device and canvas), then a header row and one row per entry. Times are ISO 8601; measurements are in milliseconds
143
+ * except counts (`moves`, `frames`, `layers`) and `zoomPct`; empty fields don't apply to that entry.
144
+ *
145
+ * @example
146
+ * strokeLogCsv(log.entries, { version: "1.13.0" });
147
+ * // "# version: 1.13.0\ntime,event,detail,gap,…\n2026-10-31T20:37:55.545Z,start,mouse,6.7,0.2,…"
148
+ */
149
+ export declare const strokeLogCsv: (entries: readonly StrokeLogEntry[], context?: Record<string, string | number>) => string;
@@ -0,0 +1,257 @@
1
+ // src/lib/classes/StrokeLog/index.ts
2
+ /** Default number of log entries kept */
3
+ var DEFAULT_STROKE_LOG_LIMIT = 200;
4
+ /** Round a measurement to 0.1 */
5
+ var round = (value) => Math.round(value * 10) / 10;
6
+ /**
7
+ * Records when brush and eraser strokes start, end, are cancelled or are ignored, with timings for each stroke, plus
8
+ * other work done meanwhile (see `BackgroundWork`), for the debug panel. Times are in the same clock as `event.timeStamp` and
9
+ * `performance.now()`. Keeps only the most recent entries.
10
+ *
11
+ * Each finished or cancelled stroke measures (in ms unless noted):
12
+ * - `gap`: time since the previous stroke ended
13
+ * - `wait`: how long the press waited before it was handled (the page was busy)
14
+ * - `begin`: time taken to start the stroke
15
+ * - `moves`: pointer events handled (count), `maxMoveWait`: longest one waited, `moveMs`: total time handling them
16
+ * - `frames`: frames drawn (count), `maxFrame`: longest frame to draw, `maxFrameGap`: longest time between frames
17
+ * (including after the last one), `overlays`: times the brush cursor was drawn (count)
18
+ * - `end`: time taken to write the stroke into its layer
19
+ * - `duration`: from press to release
20
+ * - `layers`: layers drawn (count) and `zoomPct`: the zoom as a percentage, when the stroke started (if given)
21
+ *
22
+ * Records nothing while switched off (`enabled`); work given to `measure` still runs.
23
+ *
24
+ * Dispatches a `change` event when entries are added or cleared.
25
+ *
26
+ * @example
27
+ * const log = new StrokeLog();
28
+ * log.begin("pen", event.timeStamp, 2);
29
+ * log.move(event.timeStamp, 0.5);
30
+ * log.end(event.timeStamp, 3); // entry with kind "end" and its measures
31
+ */
32
+ var StrokeLog = class extends EventTarget {
33
+ /** Maximum number of entries kept */
34
+ limit;
35
+ /** Current time in milliseconds (replaceable for tests) */
36
+ now = () => performance.now();
37
+ /** Whether anything is recorded */
38
+ enabled = true;
39
+ items = [];
40
+ stroke;
41
+ /** When the last stroke ended or was cancelled (event time) */
42
+ lastEnd;
43
+ constructor(limit = 200) {
44
+ super();
45
+ this.limit = limit;
46
+ }
47
+ /** Logged entries, oldest first */
48
+ get entries() {
49
+ return this.items;
50
+ }
51
+ /** Whether a stroke is being measured */
52
+ get drawing() {
53
+ return this.stroke !== void 0;
54
+ }
55
+ /**
56
+ * A stroke started: the pointer type, when it pressed (event time), how long starting the stroke took, and what was on
57
+ * screen.
58
+ */
59
+ begin(pointerType, eventTime, beginMs, scene) {
60
+ if (!this.enabled) return;
61
+ const handled = this.now() - beginMs;
62
+ this.stroke = {
63
+ scene,
64
+ pointerType,
65
+ startTime: eventTime,
66
+ startWait: Math.max(0, handled - eventTime),
67
+ beginMs,
68
+ moves: 0,
69
+ maxMoveWait: 0,
70
+ moveMs: 0,
71
+ frames: 0,
72
+ maxFrameMs: 0,
73
+ lastFrameAt: handled,
74
+ maxFrameGap: 0,
75
+ overlays: 0,
76
+ gap: this.lastEnd === void 0 ? void 0 : eventTime - this.lastEnd
77
+ };
78
+ this.add("start", pointerType, {
79
+ wait: this.stroke.startWait,
80
+ ...this.stroke.gap === void 0 ? {} : { gap: this.stroke.gap },
81
+ ...scene
82
+ });
83
+ }
84
+ /**
85
+ * The stroke continued: when the pointer moved (event time) and how long handling it took.
86
+ */
87
+ move(eventTime, moveMs) {
88
+ const stroke = this.stroke;
89
+ if (!stroke) return;
90
+ const handled = this.now() - moveMs;
91
+ stroke.moves++;
92
+ stroke.moveMs += moveMs;
93
+ stroke.maxMoveWait = Math.max(stroke.maxMoveWait, handled - eventTime);
94
+ }
95
+ /**
96
+ * A frame was drawn, taking `ms`; counted towards the stroke in progress, if any.
97
+ */
98
+ frame(ms) {
99
+ const stroke = this.stroke;
100
+ if (!stroke) return;
101
+ const now = this.now();
102
+ stroke.frames++;
103
+ stroke.maxFrameMs = Math.max(stroke.maxFrameMs, ms);
104
+ stroke.maxFrameGap = Math.max(stroke.maxFrameGap, now - stroke.lastFrameAt);
105
+ stroke.lastFrameAt = now;
106
+ }
107
+ /**
108
+ * The brush cursor was drawn; counted towards the stroke in progress, if any.
109
+ */
110
+ overlay() {
111
+ if (this.stroke) this.stroke.overlays++;
112
+ }
113
+ /**
114
+ * The stroke finished: when the pointer released (event time) and how long writing it into the layer took.
115
+ */
116
+ end(eventTime, endMs) {
117
+ const stroke = this.stroke;
118
+ if (!stroke) return;
119
+ this.finish("end", stroke.pointerType, eventTime, { end: endMs });
120
+ }
121
+ /**
122
+ * The stroke in progress was abandoned, and why.
123
+ */
124
+ cancel(reason) {
125
+ const stroke = this.stroke;
126
+ if (!stroke) return;
127
+ this.finish("cancel", `${stroke.pointerType}: ${reason}`, this.now(), {});
128
+ }
129
+ /**
130
+ * A pointer press didn't start a stroke, and why.
131
+ */
132
+ ignore(pointerType, reason) {
133
+ this.add("ignore", `${pointerType}: ${reason}`, {});
134
+ }
135
+ /**
136
+ * A touch gesture did an action.
137
+ */
138
+ gesture(gesture, action) {
139
+ this.add("gesture", `${gesture}: ${action}`, {});
140
+ }
141
+ /**
142
+ * Other work was done, with its measurements: `ms` for how long it kept the page busy, and `total` for how long work
143
+ * that finishes later took altogether (which can include waiting that doesn't keep the page busy).
144
+ */
145
+ work(name, measures) {
146
+ this.add("work", name, measures);
147
+ }
148
+ /**
149
+ * Run some work and return its result, logging how long it kept the page busy, or for work that finishes later (a
150
+ * promise), how long it took altogether once it settles.
151
+ *
152
+ * @example
153
+ * log.measure("map", () => drawMap()); // logs "map" with how long it took
154
+ * log.measure("save", () => save()); // logs "save" with how long it took once saved (or failed)
155
+ */
156
+ measure(name, run) {
157
+ if (!this.enabled) return run();
158
+ const start = this.now();
159
+ const result = run();
160
+ if (result instanceof Promise) {
161
+ const done = () => this.work(name, { total: this.now() - start });
162
+ result.then(done, done);
163
+ } else this.work(name, { ms: this.now() - start });
164
+ return result;
165
+ }
166
+ /**
167
+ * Remove all entries.
168
+ */
169
+ clear() {
170
+ this.items = [];
171
+ this.dispatchEvent(new Event("change"));
172
+ }
173
+ /** Log a finished or cancelled stroke with its measurements, and stop measuring it */
174
+ finish(kind, detail, eventTime, extra) {
175
+ const stroke = this.stroke;
176
+ this.stroke = void 0;
177
+ this.lastEnd = eventTime;
178
+ this.add(kind, detail, {
179
+ ...stroke.gap === void 0 ? {} : { gap: stroke.gap },
180
+ wait: stroke.startWait,
181
+ begin: stroke.beginMs,
182
+ moves: stroke.moves,
183
+ maxMoveWait: stroke.maxMoveWait,
184
+ moveMs: stroke.moveMs,
185
+ frames: stroke.frames,
186
+ maxFrame: stroke.maxFrameMs,
187
+ maxFrameGap: Math.max(stroke.maxFrameGap, this.now() - stroke.lastFrameAt),
188
+ overlays: stroke.overlays,
189
+ ...extra,
190
+ duration: eventTime - stroke.startTime,
191
+ ...stroke.scene
192
+ });
193
+ }
194
+ /** Add an entry, rounding its measurements (unless switched off) */
195
+ add(kind, detail, measures) {
196
+ if (!this.enabled) return;
197
+ const rounded = Object.fromEntries(Object.entries(measures).map(([name, value]) => [name, round(value)]));
198
+ this.items = [...this.items, {
199
+ at: Date.now(),
200
+ kind,
201
+ detail,
202
+ measures: rounded
203
+ }].slice(-this.limit);
204
+ this.dispatchEvent(new Event("change"));
205
+ }
206
+ };
207
+ /** The stroke log's CSV columns after time, event and detail: every measurement, in order */
208
+ var STROKE_LOG_COLUMNS = [
209
+ "gap",
210
+ "wait",
211
+ "begin",
212
+ "moves",
213
+ "maxMoveWait",
214
+ "moveMs",
215
+ "frames",
216
+ "maxFrame",
217
+ "maxFrameGap",
218
+ "overlays",
219
+ "end",
220
+ "duration",
221
+ "layers",
222
+ "zoomPct",
223
+ "ms",
224
+ "total"
225
+ ];
226
+ /**
227
+ * A CSV field, quoted if it holds a comma, quote or line break.
228
+ *
229
+ * @example
230
+ * csvField('a "b", c'); // '"a ""b"", c"'
231
+ */
232
+ var csvField = (value) => /[",\n\r]/.test(value) ? `"${value.replaceAll("\"", "\"\"")}"` : value;
233
+ /**
234
+ * Stroke log entries as CSV, oldest first, to paste somewhere for analysis: `#` lines describing the context first (e.g.
235
+ * device and canvas), then a header row and one row per entry. Times are ISO 8601; measurements are in milliseconds
236
+ * except counts (`moves`, `frames`, `layers`) and `zoomPct`; empty fields don't apply to that entry.
237
+ *
238
+ * @example
239
+ * strokeLogCsv(log.entries, { version: "1.13.0" });
240
+ * // "# version: 1.13.0\ntime,event,detail,gap,…\n2026-10-31T20:37:55.545Z,start,mouse,6.7,0.2,…"
241
+ */
242
+ var strokeLogCsv = (entries, context = {}) => [
243
+ ...Object.entries(context).map(([name, value]) => `# ${name}: ${value}`),
244
+ [
245
+ "time",
246
+ "event",
247
+ "detail",
248
+ ...STROKE_LOG_COLUMNS
249
+ ].join(","),
250
+ ...entries.map(({ at, kind, detail, measures }) => [
251
+ new Date(at).toISOString(),
252
+ kind,
253
+ csvField(detail),
254
+ ...STROKE_LOG_COLUMNS.map((name) => name in measures ? String(measures[name]) : "")
255
+ ].join(","))
256
+ ].join("\n");
257
+ export { DEFAULT_STROKE_LOG_LIMIT, STROKE_LOG_COLUMNS, csvField, StrokeLog as default, strokeLogCsv };
@@ -0,0 +1,108 @@
1
+ /** A point in screen (CSS pixel) or canvas (image pixel) coordinates */
2
+ export interface Point {
3
+ x: number;
4
+ y: number;
5
+ }
6
+ /** A rectangle in canvas pixels */
7
+ export interface Rect {
8
+ x: number;
9
+ y: number;
10
+ width: number;
11
+ height: number;
12
+ }
13
+ /** Space reserved on each side of the screen by the interface (e.g. toolbar and panels), in CSS pixels */
14
+ export interface Insets {
15
+ top: number;
16
+ right: number;
17
+ bottom: number;
18
+ left: number;
19
+ }
20
+ /** Zoom steps used by the zoom tool, from 1/16 to 32× */
21
+ export declare const ZOOM_LEVELS: readonly [0.0625, 0.125, 0.25, number, 0.5, number, 1, 1.5, 2, 3, 4, 6, 8, 12, 16, 24, 32];
22
+ /** Smallest zoom */
23
+ export declare const MIN_ZOOM: number;
24
+ /** Largest zoom */
25
+ export declare const MAX_ZOOM: number;
26
+ /** Screen space (CSS pixels) of canvas that must stay visible so it can't be panned out of reach */
27
+ export declare const MIN_VISIBLE = 64;
28
+ /** Space left around the canvas when fitting it to the screen, in CSS pixels */
29
+ export declare const FIT_PADDING = 24;
30
+ /**
31
+ * The user's view of the canvas: zoom level and position. Converts between screen and canvas coordinates.
32
+ *
33
+ * The canvas pixel at `(0, 0)` is drawn at screen position `(offsetX, offsetY)`, and one canvas pixel is `zoom` CSS pixels wide.
34
+ *
35
+ * Dispatches a `change` event whenever the view moves or zooms.
36
+ *
37
+ * @example
38
+ * const view = new Viewport(2000, 2000);
39
+ * view.resize(1280, 800);
40
+ * view.fit();
41
+ * view.zoomAt({ x: 640, y: 400 }, 2);
42
+ * view.screenToCanvas({ x: 640, y: 400 }); // canvas point under the screen centre
43
+ */
44
+ export default class Viewport extends EventTarget {
45
+ /** Canvas width in pixels */
46
+ readonly canvasWidth: number;
47
+ /** Canvas height in pixels */
48
+ readonly canvasHeight: number;
49
+ /** Screen width in CSS pixels */
50
+ width: number;
51
+ /** Screen height in CSS pixels */
52
+ height: number;
53
+ /** CSS pixels per canvas pixel */
54
+ zoom: number;
55
+ /** Screen X of the canvas's left edge */
56
+ offsetX: number;
57
+ /** Screen Y of the canvas's top edge */
58
+ offsetY: number;
59
+ constructor(canvasWidth: number, canvasHeight: number);
60
+ /**
61
+ * Convert a screen point to canvas pixels.
62
+ */
63
+ screenToCanvas({ x, y }: Point): Point;
64
+ /**
65
+ * Convert a canvas point to screen (CSS) pixels.
66
+ */
67
+ canvasToScreen({ x, y }: Point): Point;
68
+ /**
69
+ * The part of the canvas currently visible on screen, clipped to the canvas.
70
+ */
71
+ visibleRect(): Rect;
72
+ /**
73
+ * Set the screen size (e.g. when the window resizes). Keeps the view in reach.
74
+ */
75
+ resize(width: number, height: number): void;
76
+ /**
77
+ * Zoom and centre so the whole canvas fits in the space not covered by the interface (never zooming in past 100%).
78
+ */
79
+ fit(insets?: Insets): void;
80
+ /**
81
+ * 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).
82
+ */
83
+ centreOn({ x, y }: Point, insets?: Insets): void;
84
+ /**
85
+ * Move the view by a distance in screen pixels (e.g. while dragging with the pan tool).
86
+ */
87
+ panBy(dx: number, dy: number): void;
88
+ /**
89
+ * Zoom to a level, keeping the canvas point under `anchor` (a screen point) in place.
90
+ */
91
+ zoomAt(anchor: Point, zoom: number): void;
92
+ /**
93
+ * Zoom in one step around a screen point (zoom tool click).
94
+ */
95
+ zoomIn(anchor: Point): void;
96
+ /**
97
+ * Zoom out one step around a screen point (zoom tool Alt+click).
98
+ */
99
+ zoomOut(anchor: Point): void;
100
+ /**
101
+ * Keep at least `MIN_VISIBLE` pixels of the canvas on screen in each direction.
102
+ */
103
+ private constrain;
104
+ /**
105
+ * Dispatch a `change` event.
106
+ */
107
+ private notify;
108
+ }