@lvl99dotcom/paintcanvas 0.0.0-stage → 0.2.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 +94 -0
  86. package/dist/lib/i18n/index.js +182 -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 +188 -0
  144. package/dist/locales/en-US.js +188 -0
  145. package/dist/locales/fr.js +188 -0
  146. package/dist/locales/ja.js +188 -0
  147. package/dist/locales/ko.js +188 -0
  148. package/dist/locales/zh-Hans.js +188 -0
  149. package/dist/locales/zh-Hant.js +188 -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 +463 -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 +150 -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 +141 -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 +543 -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 +186 -0
  278. package/locales/en-US.json +186 -0
  279. package/locales/fr.json +186 -0
  280. package/locales/ja.json +186 -0
  281. package/locales/ko.json +186 -0
  282. package/locales/zh-Hans.json +186 -0
  283. package/locales/zh-Hant.json +186 -0
  284. package/package.json +97 -3
@@ -0,0 +1,130 @@
1
+ import { nothing } from "lit";
2
+ import { PcElement } from "../../base";
3
+ import "../PcBrushTest";
4
+ import type PaintCanvas from "../../../lib/classes/PaintCanvas";
5
+ import { SmoothingType, type BrushConfig } from "../../../lib/brushes/types";
6
+ import { type ShapeImage } from "../../../lib/brushes/shapeImage";
7
+ /** Asks the user to confirm; replaceable for tests */
8
+ export type ConfirmFn = (message: string) => boolean;
9
+ /** Asks the user for text; replaceable for tests */
10
+ export type PromptFn = (message: string, value: string) => string | null;
11
+ /** The smoothing choices, in the order shown: value, translation key of the name, and icon name */
12
+ export declare const SMOOTHING_CHOICES: readonly [{
13
+ readonly value: SmoothingType.Linear;
14
+ readonly label: "brushConfig.smoothingLinear";
15
+ readonly icon: "smoothingLinear";
16
+ }, {
17
+ readonly value: SmoothingType.BezierQuad;
18
+ readonly label: "brushConfig.smoothingQuadratic";
19
+ readonly icon: "smoothingBezierQuad";
20
+ }, {
21
+ readonly value: SmoothingType.BezierCubic;
22
+ readonly label: "brushConfig.smoothingCubic";
23
+ readonly icon: "smoothingBezierCubic";
24
+ }];
25
+ /**
26
+ * The brush (or eraser) configuration window: a testing area, the shape (upload or paste), smoothing, width, height,
27
+ * opacity, rotation, offsets, sample rate, spacing and snapping to the pixel grid. Width, height, opacity, rotation and the offsets can vary with
28
+ * pressure, tilt and randomness; when any is on, the setting has a min and max.
29
+ *
30
+ * Changes stay in the window until "Save" (fires `pc-save` with the settings, to apply to the current tool) or "Add to
31
+ * presets" (saves them as one of the user's presets, asking before replacing one with the same name, and selects it).
32
+ * "Delete preset…" shows when editing one of the user's presets. Fires `pc-close` when closed, and `pc-toast` with a
33
+ * message after adding or deleting a preset, or when a shape can't be used. Styling is structural only.
34
+ *
35
+ * @example
36
+ * html`<pc-brush-config .app=${app} kind="brush" .config=${app.brush.preset} @pc-save=${apply}></pc-brush-config>`
37
+ */
38
+ export declare class PcBrushConfig extends PcElement {
39
+ static properties: {
40
+ app: {
41
+ attribute: boolean;
42
+ };
43
+ kind: {
44
+ type: StringConstructor;
45
+ };
46
+ config: {
47
+ attribute: boolean;
48
+ };
49
+ editingId: {
50
+ type: StringConstructor;
51
+ };
52
+ draft: {
53
+ state: boolean;
54
+ };
55
+ busy: {
56
+ state: boolean;
57
+ };
58
+ translations: {
59
+ attribute: boolean;
60
+ };
61
+ icons: {
62
+ attribute: boolean;
63
+ };
64
+ theme: {
65
+ type: StringConstructor;
66
+ reflect: boolean;
67
+ };
68
+ };
69
+ static styles: import("lit").CSSResultGroup[];
70
+ app?: PaintCanvas;
71
+ /** Which tool's settings are configured */
72
+ kind: "brush" | "eraser";
73
+ /** The settings to start from */
74
+ config?: BrushConfig;
75
+ /** The ID of the user's own preset being edited, if any (shows "Delete preset…") */
76
+ editingId?: string;
77
+ /** The settings as changed in the window */
78
+ draft?: BrushConfig;
79
+ /** Whether a preset is being saved or deleted */
80
+ busy: boolean;
81
+ confirm: ConfirmFn;
82
+ prompt: PromptFn;
83
+ /** Turns files into brush shapes (replaceable for tests) */
84
+ readShape: (file: File) => Promise<ShapeImage>;
85
+ /** Reads a brush shape from the clipboard (replaceable for tests) */
86
+ readClipboard: () => Promise<ShapeImage>;
87
+ private readonly onKeyDown;
88
+ constructor();
89
+ connectedCallback(): void;
90
+ disconnectedCallback(): void;
91
+ protected willUpdate(changed: Map<PropertyKey, unknown>): void;
92
+ /** Fire an event that crosses shadow DOM boundaries */
93
+ private emit;
94
+ /** Change the draft settings (only shown, and so only changed, once there is a draft) */
95
+ private change;
96
+ /**
97
+ * Change the width; with constrained proportions, the height and its variation follow.
98
+ */
99
+ private changeWidth;
100
+ /** Apply changes to a setting */
101
+ private changeParam;
102
+ /**
103
+ * Close the window without applying anything.
104
+ */
105
+ close(): void;
106
+ /**
107
+ * Apply the settings to the current tool.
108
+ */
109
+ save(): void;
110
+ /** Use a shape, showing why if it can't be used */
111
+ private useShape;
112
+ /**
113
+ * Save the settings as one of the user's presets, asking for its name, and before replacing one of theirs with the
114
+ * same name. The new preset is selected.
115
+ */
116
+ addToPresets(): Promise<void>;
117
+ /**
118
+ * Delete the preset being edited, after confirming.
119
+ */
120
+ deletePreset(): Promise<void>;
121
+ /** Render a number input */
122
+ private number;
123
+ /** Render a range slider */
124
+ private slider;
125
+ /** Render a setting that can vary: its value (or min and max), and its pressure, tilt and randomise icon toggles */
126
+ private renderParam;
127
+ /** Render the shape and smoothing row */
128
+ private renderShape;
129
+ render(): import("lit").TemplateResult<1> | typeof nothing;
130
+ }
@@ -0,0 +1,463 @@
1
+ import style_default from "./style.js";
2
+ import { ICON_CONTROLS_CSS, PILLS_CSS, WINDOWS_CSS } from "../../styles/index.js";
3
+ import { iconLabel } from "../../icons/index.js";
4
+ import { PcElement, defineElement } from "../../base/index.js";
5
+ import { SmoothingType } from "../../../lib/brushes/types/index.js";
6
+ import { PARAM_LIMITS, VARIATION_SOURCES, readRange, toggleSource, writeRange } from "../../../lib/brushes/configRanges/index.js";
7
+ import { SHAPE_ERRORS, ShapeImageError, readClipboardShape, readShapeImage } from "../../../lib/brushes/shapeImage/index.js";
8
+ import "../../../lib/brushes/StrokeSampler/index.js";
9
+ import { html, nothing, unsafeCSS } from "lit";
10
+ import { styleMap } from "lit/directives/style-map.js";
11
+ // src/ui/elements/PcBrushConfig/index.ts
12
+ /** The smoothing choices, in the order shown: value, translation key of the name, and icon name */
13
+ var SMOOTHING_CHOICES = [
14
+ {
15
+ value: SmoothingType.Linear,
16
+ label: "brushConfig.smoothingLinear",
17
+ icon: "smoothingLinear"
18
+ },
19
+ {
20
+ value: SmoothingType.BezierQuad,
21
+ label: "brushConfig.smoothingQuadratic",
22
+ icon: "smoothingBezierQuad"
23
+ },
24
+ {
25
+ value: SmoothingType.BezierCubic,
26
+ label: "brushConfig.smoothingCubic",
27
+ icon: "smoothingBezierCubic"
28
+ }
29
+ ];
30
+ /** Icon names of the variation sources */
31
+ var SOURCE_ICONS = {
32
+ pressure: "pressure",
33
+ tilt: "tilt",
34
+ randomise: "randomise"
35
+ };
36
+ /** Translation keys of the variation sources' names */
37
+ var SOURCE_LABELS = {
38
+ pressure: "toolSettings.pressure",
39
+ tilt: "toolSettings.tilt",
40
+ randomise: "brushConfig.randomise"
41
+ };
42
+ /** Translation keys of the brush shape error messages */
43
+ var SHAPE_ERROR_KEYS = {
44
+ [SHAPE_ERRORS.type]: "shape.notPng",
45
+ [SHAPE_ERRORS.size]: "shape.tooLarge",
46
+ [SHAPE_ERRORS.decode]: "shape.readFailed",
47
+ [SHAPE_ERRORS.noImage]: "shape.noPngToPaste"
48
+ };
49
+ /** Largest stamp spacing in pixels */
50
+ var MAX_SPACING = 200;
51
+ /** Read a number input, or undefined if it isn't a number */
52
+ var numberValue = (event) => {
53
+ const value = event.target.valueAsNumber;
54
+ return Number.isFinite(value) ? value : void 0;
55
+ };
56
+ /**
57
+ * The brush (or eraser) configuration window: a testing area, the shape (upload or paste), smoothing, width, height,
58
+ * opacity, rotation, offsets, sample rate, spacing and snapping to the pixel grid. Width, height, opacity, rotation and the offsets can vary with
59
+ * pressure, tilt and randomness; when any is on, the setting has a min and max.
60
+ *
61
+ * Changes stay in the window until "Save" (fires `pc-save` with the settings, to apply to the current tool) or "Add to
62
+ * presets" (saves them as one of the user's presets, asking before replacing one with the same name, and selects it).
63
+ * "Delete preset…" shows when editing one of the user's presets. Fires `pc-close` when closed, and `pc-toast` with a
64
+ * message after adding or deleting a preset, or when a shape can't be used. Styling is structural only.
65
+ *
66
+ * @example
67
+ * html`<pc-brush-config .app=${app} kind="brush" .config=${app.brush.preset} @pc-save=${apply}></pc-brush-config>`
68
+ */
69
+ var PcBrushConfig = class extends PcElement {
70
+ static properties = {
71
+ ...PcElement.properties,
72
+ app: { attribute: false },
73
+ kind: { type: String },
74
+ config: { attribute: false },
75
+ editingId: { type: String },
76
+ draft: { state: true },
77
+ busy: { state: true }
78
+ };
79
+ static styles = [
80
+ PcElement.styles,
81
+ unsafeCSS(style_default),
82
+ unsafeCSS(WINDOWS_CSS),
83
+ unsafeCSS(ICON_CONTROLS_CSS),
84
+ unsafeCSS(PILLS_CSS)
85
+ ];
86
+ confirm = (message) => window.confirm(message);
87
+ prompt = (message, value) => window.prompt(message, value);
88
+ /** Turns files into brush shapes (replaceable for tests) */
89
+ readShape = readShapeImage;
90
+ /** Reads a brush shape from the clipboard (replaceable for tests) */
91
+ readClipboard = () => readClipboardShape();
92
+ onKeyDown = (event) => {
93
+ if (event.key === "Escape") this.close();
94
+ };
95
+ constructor() {
96
+ super();
97
+ this.kind = "brush";
98
+ this.busy = false;
99
+ }
100
+ connectedCallback() {
101
+ super.connectedCallback();
102
+ window.addEventListener("keydown", this.onKeyDown);
103
+ }
104
+ disconnectedCallback() {
105
+ super.disconnectedCallback();
106
+ window.removeEventListener("keydown", this.onKeyDown);
107
+ }
108
+ willUpdate(changed) {
109
+ super.willUpdate(changed);
110
+ if (changed.has("config") && this.config) this.draft = structuredClone(this.config);
111
+ }
112
+ /** Fire an event that crosses shadow DOM boundaries */
113
+ emit(type, detail) {
114
+ this.dispatchEvent(new CustomEvent(type, {
115
+ detail,
116
+ bubbles: true,
117
+ composed: true
118
+ }));
119
+ }
120
+ /** Change the draft settings (only shown, and so only changed, once there is a draft) */
121
+ change(changes) {
122
+ this.draft = {
123
+ ...this.draft,
124
+ ...changes
125
+ };
126
+ }
127
+ /**
128
+ * Change the width; with constrained proportions, the height and its variation follow.
129
+ */
130
+ changeWidth(changes) {
131
+ const draft = this.draft;
132
+ if (!draft.constrainProportions) {
133
+ this.change(changes);
134
+ return;
135
+ }
136
+ const ratio = (draft.height ?? draft.width ?? 1) / (draft.width ?? 1);
137
+ const width = changes.width ?? draft.width ?? 1;
138
+ this.change({
139
+ ...changes,
140
+ height: Math.max(1, Math.round(width * ratio)),
141
+ heightConfig: changes.widthConfig ?? draft.widthConfig
142
+ });
143
+ }
144
+ /** Apply changes to a setting */
145
+ changeParam(param, changes) {
146
+ if (param === "width") this.changeWidth(changes);
147
+ else this.change(changes);
148
+ }
149
+ /**
150
+ * Close the window without applying anything.
151
+ */
152
+ close() {
153
+ this.emit("pc-close");
154
+ }
155
+ /**
156
+ * Apply the settings to the current tool.
157
+ */
158
+ save() {
159
+ if (this.draft) this.emit("pc-save", this.draft);
160
+ }
161
+ /** Use a shape, showing why if it can't be used */
162
+ async useShape(read) {
163
+ try {
164
+ const shape = await read();
165
+ const width = this.draft?.width ?? shape.width;
166
+ this.change({
167
+ image: shape.image,
168
+ height: Math.max(1, Math.round(width * shape.height / shape.width))
169
+ });
170
+ } catch (error) {
171
+ this.emit("pc-toast", {
172
+ message: this.t(error instanceof ShapeImageError && SHAPE_ERROR_KEYS[error.message] || "brushConfig.shapeFailed"),
173
+ kind: "error"
174
+ });
175
+ }
176
+ }
177
+ /**
178
+ * Save the settings as one of the user's presets, asking for its name, and before replacing one of theirs with the
179
+ * same name. The new preset is selected.
180
+ */
181
+ async addToPresets() {
182
+ const app = this.app;
183
+ const draft = this.draft;
184
+ if (!app || !draft) return;
185
+ const name = this.prompt(this.t("brushConfig.presetName"), draft.name ?? "")?.trim();
186
+ if (!name) return;
187
+ const existing = app.customPresets.findOwn(name, app.authorHandle, app.names.unknownAuthor);
188
+ if (existing && !this.confirm(this.t("presets.replaceConfirm", { name }))) return;
189
+ const preset = {
190
+ ...app.newPreset(draft, name),
191
+ ...existing ? { id: existing.id } : {}
192
+ };
193
+ this.busy = true;
194
+ try {
195
+ await app.customPresets.save(preset);
196
+ (this.kind === "eraser" ? app.eraser : app.brush).select(preset.id);
197
+ this.emit("pc-toast", {
198
+ message: this.t("presets.saved", { name }),
199
+ kind: "success"
200
+ });
201
+ this.close();
202
+ } catch {
203
+ this.emit("pc-toast", {
204
+ message: this.t("presets.saveFailed", { name }),
205
+ kind: "error"
206
+ });
207
+ } finally {
208
+ this.busy = false;
209
+ }
210
+ }
211
+ /**
212
+ * Delete the preset being edited, after confirming.
213
+ */
214
+ async deletePreset() {
215
+ const app = this.app;
216
+ const id = this.editingId;
217
+ if (!app || !id) return;
218
+ const name = app.customPresets.get(id)?.name ?? "this preset";
219
+ if (!this.confirm(this.t("presets.deleteConfirm", { name }))) return;
220
+ this.busy = true;
221
+ try {
222
+ await app.customPresets.remove(id);
223
+ this.emit("pc-toast", {
224
+ message: this.t("presets.deleted", { name }),
225
+ kind: "success"
226
+ });
227
+ this.close();
228
+ } catch {
229
+ this.emit("pc-toast", {
230
+ message: this.t("presets.deleteFailed", { name }),
231
+ kind: "error"
232
+ });
233
+ } finally {
234
+ this.busy = false;
235
+ }
236
+ }
237
+ /** Render a number input */
238
+ number(className, value, limits, onChange, disabled = false) {
239
+ return html`<input
240
+ type="number"
241
+ class=${className}
242
+ min=${limits.min}
243
+ max=${limits.max}
244
+ ?disabled=${disabled || this.busy}
245
+ .value=${String(Math.round(value))}
246
+ @change=${(e) => {
247
+ const next = numberValue(e);
248
+ if (next !== void 0) onChange(next);
249
+ }}
250
+ />`;
251
+ }
252
+ /** Render a range slider */
253
+ slider(className, value, limits, onChange, disabled = false) {
254
+ return html`<input
255
+ type="range"
256
+ class=${className}
257
+ min=${limits.min}
258
+ max=${limits.max}
259
+ ?disabled=${disabled || this.busy}
260
+ .value=${String(value)}
261
+ @input=${(e) => onChange(e.target.valueAsNumber)}
262
+ />`;
263
+ }
264
+ /** Render a setting that can vary: its value (or min and max), and its pressure, tilt and randomise icon toggles */
265
+ renderParam(draft, param, label, disabled = false) {
266
+ const range = readRange(draft, param);
267
+ const limits = PARAM_LIMITS[param];
268
+ const isOffset = param === "offsetX" || param === "offsetY";
269
+ const write = (values) => this.changeParam(param, writeRange(draft, param, values));
270
+ const values = range.varies ? html`${this.number(`${param} min`, range.min, limits, (min) => write({ min }), disabled)}
271
+ ${this.slider(`${param} min`, range.min, limits, (min) => write({ min }), disabled)}
272
+ ${this.slider(`${param} max`, range.max, limits, (max) => write({ max }), disabled)}
273
+ ${this.number(`${param} max`, range.max, limits, (max) => write({ max }), disabled)}` : html`${this.number(param, range.value, limits, (value) => write({ value }), disabled || isOffset)}
274
+ ${this.slider(param, range.value, limits, (value) => write({ value }), disabled || isOffset)}`;
275
+ return html`<div class="row" data-param=${param}>
276
+ <span class="label">${label}</span>
277
+ ${values}
278
+ ${VARIATION_SOURCES.map((source) => html`<label
279
+ class="icon-toggle"
280
+ title=${this.t(SOURCE_LABELS[source])}
281
+ >
282
+ <input
283
+ type="checkbox"
284
+ class=${`${param} ${source}`}
285
+ aria-label=${this.t("brushConfig.variation", {
286
+ param: label,
287
+ source: this.t(SOURCE_LABELS[source])
288
+ })}
289
+ .checked=${range.sources[source]}
290
+ ?disabled=${disabled || this.busy}
291
+ @change=${(e) => this.changeParam(param, toggleSource(draft, param, source, e.target.checked))}
292
+ />
293
+ ${iconLabel(this.iconUrl(SOURCE_ICONS[source]), this.t(SOURCE_LABELS[source]))}
294
+ </label>`)}
295
+ </div>`;
296
+ }
297
+ /** Render the shape and smoothing row */
298
+ renderShape(draft) {
299
+ const smoothing = draft.smoothingType === SmoothingType.None || draft.smoothingType === void 0 ? SmoothingType.Linear : draft.smoothingType;
300
+ return html`<div class="row shape-row">
301
+ <span class="label">${this.t("brushConfig.shape")}</span>
302
+ <span
303
+ class=${draft.image ? "shape-preview image" : "shape-preview"}
304
+ style=${styleMap({ maskImage: typeof draft.image === "string" ? `url("${draft.image}")` : void 0 })}
305
+ ></span>
306
+ <button
307
+ type="button"
308
+ class="pill upload"
309
+ ?disabled=${this.busy}
310
+ @click=${() => this.renderRoot.querySelector("input.shape")?.click()}
311
+ >
312
+ ${this.t("brushConfig.upload")}
313
+ </button>
314
+ <!-- Chosen through "Upload" -->
315
+ <label class="visually-hidden">
316
+ Upload a shape (PNG)
317
+ <input
318
+ type="file"
319
+ class="shape"
320
+ tabindex="-1"
321
+ accept="image/png"
322
+ ?disabled=${this.busy}
323
+ @change=${(e) => {
324
+ const input = e.target;
325
+ const file = input.files?.[0];
326
+ input.value = "";
327
+ if (file) this.useShape(() => this.readShape(file));
328
+ }}
329
+ />
330
+ </label>
331
+ <button
332
+ type="button"
333
+ class="icon-button paste"
334
+ title=${this.t("brushConfig.paste")}
335
+ ?disabled=${this.busy}
336
+ @click=${() => void this.useShape(this.readClipboard)}
337
+ >
338
+ ${iconLabel(this.iconUrl("clipboard"), this.t("brushConfig.paste"))}
339
+ </button>
340
+ <fieldset class="smoothing">
341
+ <legend>${this.t("brushConfig.smoothing")}</legend>
342
+ ${SMOOTHING_CHOICES.map((choice) => html`<label class="icon-toggle" title=${this.t(choice.label)}>
343
+ <input
344
+ type="radio"
345
+ name="smoothing"
346
+ .value=${String(choice.value)}
347
+ .checked=${smoothing === choice.value}
348
+ ?disabled=${this.busy}
349
+ @change=${() => this.change({ smoothingType: choice.value })}
350
+ />
351
+ ${iconLabel(this.iconUrl(choice.icon), this.t(choice.label))}
352
+ </label>`)}
353
+ </fieldset>
354
+ </div>`;
355
+ }
356
+ render() {
357
+ const draft = this.draft;
358
+ if (!draft) return nothing;
359
+ const constrained = draft.constrainProportions ?? false;
360
+ const sampleLimits = {
361
+ min: 5,
362
+ max: 400
363
+ };
364
+ const spacingLimits = {
365
+ min: 0,
366
+ max: MAX_SPACING
367
+ };
368
+ return html`<div
369
+ role="dialog"
370
+ aria-modal="true"
371
+ aria-label=${this.t(this.kind === "eraser" ? "brushConfig.eraserTitle" : "brushConfig.brushTitle")}
372
+ >
373
+ <div class="row header">
374
+ <label>
375
+ ${this.t("brushConfig.name")}
376
+ <input
377
+ class="name"
378
+ .value=${draft.name ?? ""}
379
+ ?disabled=${this.busy}
380
+ @input=${(e) => this.change({ name: e.target.value })}
381
+ />
382
+ </label>
383
+ <button
384
+ type="button"
385
+ class="close dismiss"
386
+ @click=${() => this.close()}
387
+ >
388
+ ${this.t("common.dismiss")}
389
+ </button>
390
+ </div>
391
+ <pc-brush-test .config=${draft} .tips=${this.app?.tips}></pc-brush-test>
392
+ ${this.renderShape(draft)}
393
+ ${this.renderParam(draft, "width", this.t("common.width"))}
394
+ <div class="row">
395
+ <span class="label"></span>
396
+ <label
397
+ class="icon-toggle"
398
+ title=${this.t("toolSettings.constrainProportions")}
399
+ >
400
+ <input
401
+ type="checkbox"
402
+ class="constrain"
403
+ .checked=${constrained}
404
+ ?disabled=${this.busy}
405
+ @change=${(e) => this.change({ constrainProportions: e.target.checked })}
406
+ />
407
+ ${iconLabel(this.iconUrl(constrained ? "linked" : "unlinked"), this.t("toolSettings.constrainProportions"))}
408
+ </label>
409
+ </div>
410
+ ${this.renderParam(draft, "height", this.t("common.height"), constrained)}
411
+ ${this.renderParam(draft, "alpha", this.t("common.opacity"))}
412
+ ${this.renderParam(draft, "rotation", this.t("brushConfig.rotation"))}
413
+ ${this.renderParam(draft, "offsetX", this.t("brushConfig.offsetX"))}
414
+ ${this.renderParam(draft, "offsetY", this.t("brushConfig.offsetY"))}
415
+ <div class="row">
416
+ <span class="label">${this.t("brushConfig.sampleRate")}</span>
417
+ ${this.number("sample-rate", draft.sampleRate ?? 5, sampleLimits, (sampleRate) => this.change({ sampleRate }))}
418
+ ${this.slider("sample-rate", draft.sampleRate ?? 5, sampleLimits, (sampleRate) => this.change({ sampleRate }))}
419
+ <span class="label">${this.t("brushConfig.spacing")}</span>
420
+ ${this.number("spacing", draft.spacing ?? 5, spacingLimits, (spacing) => this.change({ spacing }))}
421
+ ${this.slider("spacing", draft.spacing ?? 5, spacingLimits, (spacing) => this.change({ spacing }))}
422
+ </div>
423
+ <label>
424
+ <input
425
+ type="checkbox"
426
+ class="snap"
427
+ .checked=${draft.snapToPixelGrid ?? false}
428
+ ?disabled=${this.busy}
429
+ @change=${(e) => this.change({ snapToPixelGrid: e.target.checked })}
430
+ />
431
+ ${this.t("brushConfig.snapToPixelGrid")}
432
+ </label>
433
+ <div class="row">
434
+ ${this.editingId ? html`<button
435
+ type="button"
436
+ class="delete"
437
+ ?disabled=${this.busy}
438
+ @click=${() => void this.deletePreset()}
439
+ >
440
+ ${this.t("brushConfig.deletePreset")}
441
+ </button>` : nothing}
442
+ <button
443
+ type="button"
444
+ class="add affirmative"
445
+ ?disabled=${this.busy}
446
+ @click=${() => void this.addToPresets()}
447
+ >
448
+ ${this.t("brushConfig.addToPresets")}
449
+ </button>
450
+ <button
451
+ type="button"
452
+ class="save affirmative"
453
+ ?disabled=${this.busy}
454
+ @click=${() => this.save()}
455
+ >
456
+ ${this.t("common.save")}
457
+ </button>
458
+ </div>
459
+ </div>`;
460
+ }
461
+ };
462
+ defineElement("pc-brush-config", PcBrushConfig);
463
+ export { PcBrushConfig, SMOOTHING_CHOICES };
@@ -0,0 +1,3 @@
1
+ // src/ui/elements/PcBrushConfig/style.css?inline
2
+ var style_default = ":host {\n position: fixed;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--pc-backdrop);\n}\n[role=\"dialog\"] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n width: min(560px, 95vw);\n max-height: 95vh;\n overflow: auto;\n padding: 8px;\n background: var(--pc-surface);\n}\n.row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n}\n.row > .label {\n width: 6em;\n}\ninput[type=\"number\"] {\n width: 4.5em;\n}\ninput[type=\"range\"] {\n flex: 1;\n min-width: 60px;\n}\n.shape-preview {\n display: inline-block;\n width: 40px;\n height: 40px;\n background: var(--pc-text);\n border-radius: 50%;\n}\n.shape-preview.image {\n border-radius: 0;\n mask-repeat: no-repeat;\n mask-size: contain;\n mask-position: center;\n}\n/* The name, with \"Close\" on the right */\n.header .close {\n margin-left: auto;\n}\n/* The shape (preview, Upload and Paste) and the smoothing icons on one row */\n.shape-row {\n flex-wrap: nowrap;\n}\n.shape-row .shape-preview {\n flex: none;\n}\nfieldset.smoothing {\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0 0 0 auto;\n padding: 0;\n border: 0;\n}\nfieldset.smoothing legend {\n float: left;\n margin-right: 4px;\n padding: 0;\n}\n";
3
+ export { style_default as default };