@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,584 @@
1
+ import style_default from "./style.js";
2
+ import { PcElement, defineElement, emit } from "../../base/index.js";
3
+ import ChangeController from "../../ChangeController/index.js";
4
+ import PaintCanvas, { Panel } from "../../../lib/classes/PaintCanvas/index.js";
5
+ import KeyboardShortcuts, { SHORTCUT_EVENT } from "../../../lib/classes/KeyboardShortcuts/index.js";
6
+ import { isTouchDevice } from "../../../lib/classes/InputSettings/index.js";
7
+ import IndexedDbLayerStore from "../../../lib/storage/index.js";
8
+ import { AUTOSAVE_ERROR_EVENT, AutosaveState } from "../../../lib/classes/LayerAutosave/index.js";
9
+ import { isOwnAuthor } from "../../../lib/brushes/authors/index.js";
10
+ import { renderScreenshot, saveFile, screenshotName } from "../../../lib/rendering/screenshot/index.js";
11
+ import { DEFAULT_SIDE_MODES, STATE_EVENT, parseSideModes, sideModeFor } from "../../layout/index.js";
12
+ import { hasToolSettings } from "../PcToolSettings/index.js";
13
+ import { html, nothing, unsafeCSS } from "lit";
14
+ // src/ui/elements/PaintCanvas/index.ts
15
+ /** No space kept clear */
16
+ var NO_INSETS = {
17
+ top: 0,
18
+ right: 0,
19
+ bottom: 0,
20
+ left: 0
21
+ };
22
+ /** The choice that takes the drawing over from another window */
23
+ var TAKE_OVER_CHOICE = "take-over";
24
+ /** Elements that can take keyboard focus */
25
+ var FOCUSABLE = "button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])";
26
+ /**
27
+ * A ready-made painting app: the drawing elements arranged around the canvas, sized to its container (not the
28
+ * window), with slots to add or replace content:
29
+ *
30
+ * - `header`: across the top (empty by default)
31
+ * - `left`: `<pc-toolbar>` by default
32
+ * - `top`: `<pc-tool-settings>` (for tools that have settings) by default
33
+ * - `right`: empty by default (e.g. a `<pc-panel>` with a layers menu or chat)
34
+ * - `center`: `<pc-canvas>` by default
35
+ * - `overlay`: drawn over the canvas; `<pc-canvas-map>` by default
36
+ * - `bottom`: under the canvas (empty by default)
37
+ * - `footer`: across the bottom; `<pc-stats>` by default
38
+ * - `menu`: the app menu the toolbar's menu button opens (the button shows only with a menu)
39
+ *
40
+ * Content given for a slot replaces its default; an empty element clears it. Empty slots take no space. Hiding the
41
+ * interface (Tab key) leaves only the canvas and overlay. A `<pc-layers-menu>` given in a side (e.g. in a `<pc-panel>`)
42
+ * replaces the toolbar's layers menu button.
43
+ *
44
+ * Each side has a mode, chosen by the layout's width from `left-modes` and `right-modes` (default `"docked,
45
+ * rail@600"`): `docked` beside the canvas; `rail`, a column of buttons (one per `<pc-panel>`) that open each panel over
46
+ * the canvas; or `floating`, the side over the canvas as a palette that collapses to a button.
47
+ *
48
+ * Reports its state for styling as attributes (`narrow`, `input="pen|touch|mouse"`, `ui-hidden`, `left-mode`,
49
+ * `right-mode`) and as `pc-state-change` events (see `LayoutState`). Creates its own canvas unless given one, and
50
+ * handles keyboard shortcuts unless `shortcuts` is false.
51
+ *
52
+ * With a `store` name, the user's layers are kept in the browser (IndexedDB) under that name, so the drawing survives
53
+ * reloading the page (see `LayerAutosave`). While another window is drawing on it, the layout is blocked by a message
54
+ * that offers to draw here instead. Fires `pc-toast` when the drawing can't be loaded or saved.
55
+ *
56
+ * @example
57
+ * html`<paint-canvas style="height: 600px"></paint-canvas>`
58
+ * html`<paint-canvas .app=${canvas} right-modes="docked, rail@800">
59
+ * <pc-panel slot="right" icon="layers" label="Layers"><pc-layers-menu .app=${canvas}></pc-layers-menu></pc-panel>
60
+ * </paint-canvas>`
61
+ */
62
+ var PaintCanvasElement = class extends PcElement {
63
+ static properties = {
64
+ ...PcElement.properties,
65
+ app: { attribute: false },
66
+ leftModes: {
67
+ type: String,
68
+ attribute: "left-modes"
69
+ },
70
+ rightModes: {
71
+ type: String,
72
+ attribute: "right-modes"
73
+ },
74
+ narrowWidth: {
75
+ type: Number,
76
+ attribute: "narrow-width"
77
+ },
78
+ menuText: {
79
+ type: String,
80
+ attribute: "menu-text"
81
+ },
82
+ menuLabel: {
83
+ type: String,
84
+ attribute: "menu-label"
85
+ },
86
+ shortcuts: { type: Boolean },
87
+ store: { type: String },
88
+ width: { state: true },
89
+ input: { state: true },
90
+ configuring: { state: true },
91
+ collapsed: { state: true },
92
+ openPanel: { state: true }
93
+ };
94
+ static styles = [PcElement.styles, unsafeCSS(style_default)];
95
+ /** Saves files (replaceable for tests) */
96
+ saveFile = saveFile;
97
+ changes = new ChangeController(this, () => [this.app, this.app?.autosave]);
98
+ /** The autosave the layout started for `store`, if any */
99
+ ownAutosave;
100
+ /** The autosave whose errors are shown */
101
+ watchedAutosave;
102
+ /** Whether drawing was blocked when shortcuts were last watched */
103
+ blockedShortcuts = false;
104
+ /** The panels in each side's slot */
105
+ panels = {
106
+ left: [],
107
+ right: []
108
+ };
109
+ /** Whether each side's slot has a layers menu (the toolbar's isn't needed then) */
110
+ sideLayers = {
111
+ left: false,
112
+ right: false
113
+ };
114
+ /** Whether the implementer gave a menu */
115
+ hasMenu = false;
116
+ /** Slots the implementer gave content for (their defaults aren't created) */
117
+ filled = {};
118
+ /** Keyboard shortcuts, while handled */
119
+ keys;
120
+ /** The state last reported, to report only changes */
121
+ reported;
122
+ /** Space the floating sides cover over the canvas */
123
+ floatingInsets = NO_INSETS;
124
+ /** The rail button that opened the open panel, to return focus to */
125
+ opener;
126
+ resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(([entry]) => {
127
+ this.width = Math.round(entry?.contentRect.width ?? 0);
128
+ }) : void 0;
129
+ panelObserver = new MutationObserver(() => this.requestUpdate());
130
+ onPointerDown = (event) => {
131
+ const kind = event.pointerType;
132
+ this.input = kind === "pen" || kind === "touch" ? kind : "mouse";
133
+ };
134
+ onAutosaveError = () => emit(this, "pc-toast", {
135
+ message: this.t("autosave.failed"),
136
+ kind: "error"
137
+ });
138
+ onShortcut = (event) => {
139
+ if (event.detail.type === "save") this.saveScreenshot();
140
+ };
141
+ onDocumentPointerDown = (event) => {
142
+ const panel = this.openPanel;
143
+ if (!panel) return;
144
+ const path = event.composedPath();
145
+ if (!path.includes(panel) && !(this.opener && path.includes(this.opener))) this.closePanel(false);
146
+ };
147
+ onKeyDown = (event) => {
148
+ if (event.key !== "Escape" || !this.openPanel) return;
149
+ event.preventDefault();
150
+ this.closePanel(true);
151
+ };
152
+ constructor() {
153
+ super();
154
+ this.leftModes = DEFAULT_SIDE_MODES;
155
+ this.rightModes = DEFAULT_SIDE_MODES;
156
+ this.narrowWidth = 600;
157
+ this.menuText = "☰";
158
+ this.shortcuts = true;
159
+ this.width = 0;
160
+ this.input = isTouchDevice() ? "touch" : "mouse";
161
+ this.collapsed = {
162
+ left: true,
163
+ right: true
164
+ };
165
+ }
166
+ /** Whether another window is drawing on the stored drawing, so this one is blocked */
167
+ get blocked() {
168
+ return this.app?.autosave?.state === AutosaveState.Elsewhere;
169
+ }
170
+ /** The layout's state now (see `LayoutState`) */
171
+ get state() {
172
+ return {
173
+ width: this.width,
174
+ narrow: this.width < this.narrowWidth,
175
+ input: this.input,
176
+ uiHidden: this.app?.interfaceVisible === false,
177
+ leftMode: this.modeOf("left"),
178
+ rightMode: this.modeOf("right")
179
+ };
180
+ }
181
+ /** A side's mode for the layout's width */
182
+ modeOf(side) {
183
+ return sideModeFor(parseSideModes(side === "left" ? this.leftModes : this.rightModes), this.width);
184
+ }
185
+ connectedCallback() {
186
+ this.app ??= new PaintCanvas();
187
+ super.connectedCallback();
188
+ this.resizeObserver?.observe(this);
189
+ this.panelObserver.observe(this, {
190
+ subtree: true,
191
+ attributes: true,
192
+ attributeFilter: [
193
+ "icon",
194
+ "label",
195
+ "badge"
196
+ ]
197
+ });
198
+ this.addEventListener("pointerdown", this.onPointerDown, true);
199
+ this.addEventListener("keydown", this.onKeyDown);
200
+ document.addEventListener("pointerdown", this.onDocumentPointerDown, true);
201
+ this.watchShortcuts();
202
+ }
203
+ disconnectedCallback() {
204
+ super.disconnectedCallback();
205
+ this.resizeObserver?.disconnect();
206
+ this.panelObserver.disconnect();
207
+ this.removeEventListener("pointerdown", this.onPointerDown, true);
208
+ this.removeEventListener("keydown", this.onKeyDown);
209
+ document.removeEventListener("pointerdown", this.onDocumentPointerDown, true);
210
+ this.unwatchShortcuts();
211
+ }
212
+ willUpdate(changed) {
213
+ super.willUpdate(changed);
214
+ if (changed.has("app") || changed.has("store")) this.useStore(changed.get("app"));
215
+ this.watchAutosaveErrors();
216
+ if (this.isConnected && (changed.has("app") || changed.has("shortcuts") || this.blocked !== this.blockedShortcuts)) this.watchShortcuts();
217
+ const panel = this.openPanel;
218
+ if (panel && this.modeOf(this.sideOf(panel)) !== "rail") this.closePanel(false);
219
+ }
220
+ updated() {
221
+ const state = this.state;
222
+ this.toggleAttribute("narrow", state.narrow);
223
+ this.toggleAttribute("ui-hidden", state.uiHidden);
224
+ this.setAttribute("input", state.input);
225
+ this.setAttribute("left-mode", state.leftMode);
226
+ this.setAttribute("right-mode", state.rightMode);
227
+ const report = JSON.stringify(state);
228
+ if (report !== this.reported) {
229
+ this.reported = report;
230
+ this.dispatchEvent(new CustomEvent(STATE_EVENT, { detail: state }));
231
+ }
232
+ this.measureFloatingSides();
233
+ }
234
+ /** Keep the canvas clear of floating sides when fitting it */
235
+ measureFloatingSides() {
236
+ const width = (side) => this.renderRoot.querySelector(`.side.${side} .float:not(.collapsed)`)?.getBoundingClientRect().width ?? 0;
237
+ const insets = {
238
+ ...NO_INSETS,
239
+ left: width("left"),
240
+ right: width("right")
241
+ };
242
+ if (insets.left === this.floatingInsets.left && insets.right === this.floatingInsets.right) return;
243
+ this.floatingInsets = insets;
244
+ this.requestUpdate();
245
+ }
246
+ /**
247
+ * Keep the canvas's layers under the `store` name, replacing an autosave the layout started earlier (also on the
248
+ * previous canvas). An autosave the implementer started is left alone when there's no name.
249
+ */
250
+ useStore(previousApp) {
251
+ const app = this.app;
252
+ const own = this.ownAutosave;
253
+ if (own && previousApp?.autosave === own) previousApp.useLayerStore(void 0);
254
+ const name = this.store || void 0;
255
+ if (name === void 0) {
256
+ if (own && app.autosave === own) this.ownAutosave = app.useLayerStore(void 0);
257
+ return;
258
+ }
259
+ if (app.autosave?.store.name !== name) this.ownAutosave = app.useLayerStore(new IndexedDbLayerStore(name));
260
+ }
261
+ /** Show errors from the canvas's current autosave */
262
+ watchAutosaveErrors() {
263
+ const autosave = this.app?.autosave;
264
+ if (autosave === this.watchedAutosave) return;
265
+ this.watchedAutosave?.removeEventListener(AUTOSAVE_ERROR_EVENT, this.onAutosaveError);
266
+ this.watchedAutosave = autosave;
267
+ autosave?.addEventListener(AUTOSAVE_ERROR_EVENT, this.onAutosaveError);
268
+ }
269
+ /** Handle keyboard shortcuts for the current canvas, if asked to (not while blocked) */
270
+ watchShortcuts() {
271
+ this.unwatchShortcuts();
272
+ this.blockedShortcuts = this.blocked;
273
+ if (!this.shortcuts || !this.app || this.blockedShortcuts) return;
274
+ this.keys = new KeyboardShortcuts(this.app);
275
+ this.keys.attach(window);
276
+ window.addEventListener(SHORTCUT_EVENT, this.onShortcut);
277
+ }
278
+ /** Stop handling keyboard shortcuts */
279
+ unwatchShortcuts() {
280
+ this.keys?.detach();
281
+ this.keys = void 0;
282
+ window.removeEventListener(SHORTCUT_EVENT, this.onShortcut);
283
+ }
284
+ /**
285
+ * Save the whole canvas as a PNG on white, at full size, including a stroke or transform in progress. Returns how it
286
+ * was saved, or undefined if cancelled.
287
+ */
288
+ async saveScreenshot() {
289
+ const app = this.app;
290
+ const blob = await renderScreenshot(app.drawables, app.width, app.height, app.transform.preview ?? app.strokes.preview);
291
+ return this.saveFile(blob, screenshotName(void 0, app.names.filePrefix));
292
+ }
293
+ /** The brush or eraser settings */
294
+ toolSettings(kind) {
295
+ const app = this.app;
296
+ return kind === "eraser" ? app.eraser : app.brush;
297
+ }
298
+ /**
299
+ * Open the configuration window for the current brush or eraser settings. When the selected preset is one of the
300
+ * user's own, the window can delete it.
301
+ */
302
+ configure(kind) {
303
+ const app = this.app;
304
+ const settings = this.toolSettings(kind);
305
+ const selected = settings.selectedPreset;
306
+ const own = selected && !app.isBuiltInPreset(selected.id) && isOwnAuthor(selected.author, app.authorHandle, app.names.unknownAuthor);
307
+ this.configuring = {
308
+ kind,
309
+ config: {
310
+ ...settings.preset,
311
+ width: settings.width,
312
+ height: settings.height
313
+ },
314
+ ...own ? { editingId: selected.id } : {}
315
+ };
316
+ }
317
+ /**
318
+ * Open the configuration window to make a new preset, starting from the current brush or eraser settings.
319
+ */
320
+ newPreset(kind) {
321
+ const settings = this.toolSettings(kind);
322
+ this.app?.closePanel();
323
+ this.configuring = {
324
+ kind,
325
+ config: {
326
+ ...settings.preset,
327
+ name: this.t("brushConfig.newPresetName"),
328
+ width: settings.width,
329
+ height: settings.height
330
+ }
331
+ };
332
+ }
333
+ /** Which side a panel is in */
334
+ sideOf(panel) {
335
+ return panel.slot === "left" ? "left" : "right";
336
+ }
337
+ /**
338
+ * Open a panel from its rail button (closing any other), or close it if it's open. Focus moves into the panel.
339
+ */
340
+ togglePanel(panel, opener) {
341
+ if (this.openPanel === panel) {
342
+ this.closePanel(true);
343
+ return;
344
+ }
345
+ if (this.openPanel) this.closePanel(false);
346
+ this.openPanel = panel;
347
+ this.opener = opener;
348
+ panel.toggleAttribute("data-pc-open", true);
349
+ const focusable = panel.querySelector(FOCUSABLE);
350
+ if (focusable) focusable.focus();
351
+ else {
352
+ panel.tabIndex = -1;
353
+ panel.focus();
354
+ }
355
+ }
356
+ /**
357
+ * Close the panel open from a rail, moving focus back to its rail button if asked.
358
+ */
359
+ closePanel(returnFocus) {
360
+ const panel = this.openPanel;
361
+ if (!panel) return;
362
+ panel.removeAttribute("data-pc-open");
363
+ this.openPanel = void 0;
364
+ if (returnFocus) this.opener?.focus();
365
+ this.opener = void 0;
366
+ }
367
+ /** Follow which slots have content given (their defaults aren't needed) */
368
+ onSlotChange(event) {
369
+ const slot = event.target;
370
+ this.filled = {
371
+ ...this.filled,
372
+ [slot.name]: slot.assignedElements().length > 0
373
+ };
374
+ this.requestUpdate();
375
+ }
376
+ /** A slot, with its default content unless content was given for it */
377
+ slotWithDefault(name, fallback) {
378
+ return html`<slot name=${name} @slotchange=${this.onSlotChange}
379
+ >${this.filled[name] ? nothing : fallback}</slot
380
+ >`;
381
+ }
382
+ /** Follow the panels given for a side, and whether it has a layers menu */
383
+ onSideSlotChange(side, event) {
384
+ const assigned = event.target.assignedElements();
385
+ this.panels = {
386
+ ...this.panels,
387
+ [side]: assigned.filter((element) => element.localName === "pc-panel")
388
+ };
389
+ this.sideLayers = {
390
+ ...this.sideLayers,
391
+ [side]: assigned.some((element) => element.matches("pc-layers-menu, :has(pc-layers-menu)"))
392
+ };
393
+ this.onSlotChange(event);
394
+ }
395
+ /** Follow whether a menu was given */
396
+ onMenuSlotChange(event) {
397
+ this.hasMenu = event.target.assignedElements().length > 0;
398
+ this.requestUpdate();
399
+ }
400
+ /** Whether a side has a layers menu, replacing the toolbar's */
401
+ get layersInSide() {
402
+ return this.sideLayers.left || this.sideLayers.right;
403
+ }
404
+ /** The toolbar shown on the left unless the implementer gives other content */
405
+ renderToolbar(app) {
406
+ return html`<pc-toolbar
407
+ .app=${app}
408
+ .menu=${this.hasMenu}
409
+ .layers=${!this.layersInSide}
410
+ .menuText=${this.menuText}
411
+ .menuLabel=${this.menuLabel}
412
+ ></pc-toolbar>`;
413
+ }
414
+ /** A side's rail buttons, one per panel */
415
+ renderRail(side, label) {
416
+ const panels = this.panels[side];
417
+ if (panels.length === 0) return nothing;
418
+ return html`<div
419
+ class="rail"
420
+ part="${side}-rail"
421
+ role="toolbar"
422
+ aria-orientation="vertical"
423
+ aria-label=${label}
424
+ >
425
+ ${panels.map((panel) => html`<button
426
+ type="button"
427
+ title=${panel.label}
428
+ aria-label=${panel.badge ? this.t("layout.new", { name: panel.label }) : panel.label}
429
+ aria-expanded=${String(this.openPanel === panel)}
430
+ @click=${(event) => this.togglePanel(panel, event.currentTarget)}
431
+ >
432
+ <img src=${this.iconUrl(panel.icon)} alt="" />
433
+ ${panel.badge ? html`<span class="badge"></span>` : nothing}
434
+ </button>`)}
435
+ </div>`;
436
+ }
437
+ /** A side, in its mode */
438
+ renderSide(side, app, blocked) {
439
+ const mode = this.modeOf(side);
440
+ const label = this.t(side === "left" ? "layout.tools" : "layout.panels");
441
+ const slot = html`<slot
442
+ name=${side}
443
+ @slotchange=${(event) => this.onSideSlotChange(side, event)}
444
+ >${side === "left" && !this.filled.left ? this.renderToolbar(app) : nothing}</slot
445
+ >`;
446
+ let content = slot;
447
+ if (mode === "rail") content = side === "left" ? html`${slot}${this.renderRail(side, label)}` : html`${this.renderRail(side, label)}${slot}`;
448
+ else if (mode === "floating") {
449
+ const collapsed = this.collapsed[side];
450
+ const arrows = side === "left" ? ["»", "«"] : ["«", "»"];
451
+ content = html`<div
452
+ class=${collapsed ? "float collapsed" : "float"}
453
+ part="${side}-float"
454
+ >
455
+ <button
456
+ type="button"
457
+ class="collapse"
458
+ aria-expanded=${String(!collapsed)}
459
+ aria-label=${this.t(collapsed ? "layout.show" : "layout.hide", { name: label })}
460
+ @click=${() => this.collapsed = {
461
+ ...this.collapsed,
462
+ [side]: !collapsed
463
+ }}
464
+ >
465
+ ${collapsed ? arrows[0] : arrows[1]}
466
+ </button>
467
+ <div class="content" ?hidden=${collapsed}>${slot}</div>
468
+ </div>`;
469
+ }
470
+ return html`<div
471
+ class="side ${side}"
472
+ part=${side}
473
+ data-mode=${mode}
474
+ role="region"
475
+ aria-label=${label}
476
+ ?inert=${blocked}
477
+ >
478
+ ${content}
479
+ </div>`;
480
+ }
481
+ /** The menus the toolbar opens, floating over the canvas beside it */
482
+ renderPanels(app) {
483
+ const brushMenu = (kind) => html`<pc-brush-menu
484
+ class="panel top-panel"
485
+ kind=${kind}
486
+ .app=${app}
487
+ @pc-new-preset=${() => this.newPreset(kind)}
488
+ ></pc-brush-menu>`;
489
+ return html`${app.panel === Panel.Brush ? brushMenu("brush") : nothing}
490
+ ${app.panel === Panel.Eraser ? brushMenu("eraser") : nothing}
491
+ ${app.panel === Panel.Layers && !this.layersInSide ? html`<pc-layers-menu class="panel" .app=${app}></pc-layers-menu>` : nothing}
492
+ <div class="panel menu-panel" ?open=${app.panel === Panel.Menu}>
493
+ <slot name="menu" @slotchange=${this.onMenuSlotChange}></slot>
494
+ </div>`;
495
+ }
496
+ /** The brush configuration window, while open */
497
+ renderConfig(app) {
498
+ const configuring = this.configuring;
499
+ if (!configuring) return nothing;
500
+ return html`<pc-brush-config
501
+ .app=${app}
502
+ kind=${configuring.kind}
503
+ .config=${configuring.config}
504
+ editingId=${configuring.editingId ?? nothing}
505
+ @pc-save=${(event) => {
506
+ this.toolSettings(configuring.kind).apply(event.detail);
507
+ this.configuring = void 0;
508
+ }}
509
+ @pc-close=${() => this.configuring = void 0}
510
+ ></pc-brush-config>`;
511
+ }
512
+ /** The message blocking the layout while another window is drawing on the stored drawing */
513
+ renderBlocked(app) {
514
+ const choices = [{
515
+ value: TAKE_OVER_CHOICE,
516
+ label: this.t("autosave.takeOver")
517
+ }];
518
+ return html`<pc-dialog
519
+ class="elsewhere"
520
+ heading=${this.t("autosave.elsewhere")}
521
+ message=${this.t("autosave.elsewhereMessage")}
522
+ .choices=${choices}
523
+ @pc-choose=${(event) => {
524
+ if (event.detail.value === TAKE_OVER_CHOICE) app.autosave?.takeOver();
525
+ }}
526
+ ></pc-dialog>`;
527
+ }
528
+ render() {
529
+ const app = this.app;
530
+ const showsToolSettings = app.interfaceVisible && hasToolSettings(app.tool);
531
+ const blocked = this.blocked;
532
+ return html`<header part="header" ?inert=${blocked}>
533
+ <slot name="header"></slot>
534
+ </header>
535
+ ${this.renderSide("left", app, blocked)}
536
+ <div
537
+ class="top"
538
+ part="top"
539
+ role="region"
540
+ aria-label=${this.t("layout.toolSettings")}
541
+ ?inert=${blocked}
542
+ >
543
+ ${this.slotWithDefault("top", showsToolSettings ? html`<pc-tool-settings
544
+ .app=${app}
545
+ @pc-configure-brush=${(event) => this.configure(event.detail)}
546
+ ></pc-tool-settings>` : nothing)}
547
+ </div>
548
+ <div
549
+ class="center"
550
+ part="center"
551
+ role="region"
552
+ aria-label=${this.t("layout.canvas")}
553
+ ?inert=${blocked}
554
+ >
555
+ ${this.slotWithDefault("center", html`<pc-canvas
556
+ .app=${app}
557
+ .insets=${this.floatingInsets}
558
+ ></pc-canvas>`)}
559
+ <div class="overlay" part="overlay">
560
+ ${this.slotWithDefault("overlay", html`<pc-canvas-map
561
+ .app=${app}
562
+ .insets=${this.floatingInsets}
563
+ ></pc-canvas-map>`)}
564
+ </div>
565
+ ${app.interfaceVisible ? this.renderPanels(app) : nothing}
566
+ </div>
567
+ <div
568
+ class="bottom"
569
+ part="bottom"
570
+ role="region"
571
+ aria-label=${this.t("layout.status")}
572
+ ?inert=${blocked}
573
+ >
574
+ <slot name="bottom"></slot>
575
+ </div>
576
+ ${this.renderSide("right", app, blocked)}
577
+ <footer part="footer" ?inert=${blocked}>
578
+ ${this.slotWithDefault("footer", html`<pc-stats .app=${app}></pc-stats>`)}
579
+ </footer>
580
+ ${this.renderConfig(app)} ${blocked ? this.renderBlocked(app) : nothing}`;
581
+ }
582
+ };
583
+ defineElement("paint-canvas", PaintCanvasElement);
584
+ export { PaintCanvasElement };
@@ -0,0 +1,3 @@
1
+ // src/ui/elements/PaintCanvas/style.css?inline
2
+ var style_default = "/* The layout: header across the top, footer across the bottom, sides left and right, and between them the tool\n settings, the canvas and the bottom bar. Empty areas take no space. */\n:host {\n position: relative;\n display: grid;\n grid-template:\n \"header header header\" auto\n \"left top right\" auto\n \"left center right\" 1fr\n \"left bottom right\" auto\n \"footer footer footer\" auto / auto 1fr auto;\n overflow: hidden;\n background: var(--pc-bar);\n font-family: var(--pc-font);\n font-size: var(--pc-font-size);\n}\nheader {\n grid-area: header;\n}\n.top {\n grid-area: top;\n min-width: 0;\n}\n.center {\n position: relative;\n grid-area: center;\n min-width: 0;\n min-height: 0;\n overflow: hidden;\n}\n.bottom {\n grid-area: bottom;\n min-width: 0;\n}\nfooter {\n grid-area: footer;\n}\n.side {\n position: relative;\n z-index: 3;\n display: flex;\n min-height: 0;\n}\n.side.left {\n grid-area: left;\n}\n.side.right {\n grid-area: right;\n}\n\n/* Hiding the interface (Tab key) leaves only the canvas */\n:host([ui-hidden]) header,\n:host([ui-hidden]) .top,\n:host([ui-hidden]) .bottom,\n:host([ui-hidden]) footer,\n:host([ui-hidden]) .side {\n display: none;\n}\n\n/* The canvas fills the middle; things drawn over it go in the overlay */\n.center ::slotted([slot=\"center\"]),\n.center pc-canvas {\n position: absolute;\n inset: 0;\n}\n.overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n.overlay > * {\n pointer-events: auto;\n}\npc-canvas-map {\n position: absolute;\n right: 20px;\n bottom: 20px;\n}\n\n/* Menus opened from the toolbar float over the canvas, beside the toolbar */\n.panel {\n position: absolute;\n z-index: 4;\n left: 0;\n bottom: 0;\n max-height: 100%;\n overflow: auto;\n}\n.panel.top-panel {\n top: 0;\n bottom: auto;\n}\n.menu-panel:not([open]) {\n display: none;\n}\n\n/* Rail: a column of buttons, one per panel; the open panel floats over the canvas beside it */\n.rail {\n display: flex;\n flex-direction: column;\n gap: var(--pc-space-1);\n padding: var(--pc-space-1);\n border-inline: 1px solid var(--pc-border);\n background: var(--pc-bar);\n}\n.rail button {\n position: relative;\n display: inline-flex;\n padding: var(--pc-space-1);\n border: 1px solid transparent;\n border-radius: var(--pc-radius-small);\n background: transparent;\n cursor: pointer;\n}\n.rail button:hover {\n background: var(--pc-surface-hover);\n}\n.rail button[aria-expanded=\"true\"] {\n border-color: var(--pc-accent);\n background: var(--pc-accent);\n}\n.rail button:focus-visible {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n}\n.rail img {\n width: var(--pc-icon-size);\n height: var(--pc-icon-size);\n filter: var(--pc-icon-filter);\n}\n.rail .badge {\n position: absolute;\n top: -2px;\n right: -2px;\n width: 10px;\n height: 10px;\n border: 2px solid var(--pc-bar);\n border-radius: 50%;\n background: var(--pc-red);\n}\n.side[data-mode=\"rail\"] ::slotted(pc-panel) {\n display: none;\n}\n.side[data-mode=\"rail\"] ::slotted(pc-panel[data-pc-open]) {\n position: absolute;\n z-index: 5;\n top: 0;\n bottom: 0;\n display: flex;\n width: var(--pc-floating-panel-width, min(360px, 85vw));\n background: var(--pc-surface-raised);\n box-shadow: var(--pc-shadow);\n}\n/* When narrow, a layers menu fills the panel opened from the rail (a floating side sizes itself to its content) */\n:host([narrow]) .side[data-mode=\"rail\"] ::slotted(pc-panel) {\n --pc-layers-menu-width: 100%;\n}\n.side.left[data-mode=\"rail\"] ::slotted(pc-panel[data-pc-open]) {\n left: 100%;\n}\n.side.right[data-mode=\"rail\"] ::slotted(pc-panel[data-pc-open]) {\n right: 100%;\n}\n\n/* Floating: the side floats over the canvas as a palette, and collapses to a button */\n.side[data-mode=\"floating\"] {\n /* Beside the canvas only, so it floats over the canvas and not the tool settings */\n grid-row: 3;\n width: 0;\n}\n.float {\n position: absolute;\n z-index: 5;\n top: var(--pc-space-2);\n bottom: var(--pc-space-2);\n display: flex;\n flex-direction: column;\n gap: var(--pc-space-1);\n max-width: var(--pc-floating-panel-width, min(360px, 85vw));\n border-radius: var(--pc-radius);\n background: var(--pc-surface-raised);\n box-shadow: var(--pc-shadow);\n overflow: hidden;\n}\n.side.left .float {\n left: var(--pc-space-2);\n}\n.side.right .float {\n right: var(--pc-space-2);\n}\n.float.collapsed {\n bottom: auto;\n}\n.float .collapse {\n align-self: flex-end;\n min-width: var(--pc-control-size);\n min-height: var(--pc-control-size);\n border: 0;\n background: transparent;\n color: var(--pc-text);\n font-size: 18px;\n cursor: pointer;\n}\n.float .collapse:focus-visible {\n outline: var(--pc-focus-width) solid var(--pc-focus);\n}\n.float .content {\n display: flex;\n min-height: 0;\n flex: 1;\n}\n.float .content[hidden] {\n display: none;\n}\n\n/* While another window is drawing, its message covers only the layout (not the whole page) */\npc-dialog.elsewhere {\n position: absolute;\n}\n";
3
+ export { style_default as default };