kritzel-stencil 0.3.15 → 0.3.17

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 (228) hide show
  1. package/LICENSE.md +50 -0
  2. package/dist/cjs/index-Xav9JFHg.js +2 -2
  3. package/dist/cjs/index.cjs.js +7 -1
  4. package/dist/cjs/{kritzel-active-users_42.cjs.entry.js → kritzel-active-users_44.cjs.entry.js} +711 -146
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/{schema.constants-DJQTjcy7.js → schema.constants-DrHO_CYF.js} +1169 -171
  7. package/dist/cjs/stencil.cjs.js +1 -1
  8. package/dist/collection/classes/core/core.class.js +24 -0
  9. package/dist/collection/classes/handlers/context-menu.handler.js +24 -2
  10. package/dist/collection/classes/managers/license.manager.js +285 -0
  11. package/dist/collection/classes/managers/localization.manager.js +189 -0
  12. package/dist/collection/classes/objects/custom-element.class.js +2 -0
  13. package/dist/collection/classes/objects/group.class.js +7 -2
  14. package/dist/collection/classes/objects/image.class.js +10 -7
  15. package/dist/collection/classes/objects/line.class.js +3 -0
  16. package/dist/collection/classes/objects/path.class.js +13 -12
  17. package/dist/collection/classes/objects/selection-group.class.js +7 -2
  18. package/dist/collection/classes/objects/shape.class.js +3 -0
  19. package/dist/collection/classes/objects/text.class.js +4 -1
  20. package/dist/collection/classes/registries/icon-registry.class.js +1 -0
  21. package/dist/collection/classes/tools/brush-tool.class.js +1 -1
  22. package/dist/collection/collection-manifest.json +3 -1
  23. package/dist/collection/components/core/kritzel-editor/kritzel-editor.css +16 -0
  24. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +462 -60
  25. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +458 -34
  26. package/dist/collection/components/core/kritzel-watermark/kritzel-watermark.css +29 -0
  27. package/dist/collection/components/core/kritzel-watermark/kritzel-watermark.js +83 -0
  28. package/dist/collection/components/shared/kritzel-avatar/kritzel-avatar.js +3 -3
  29. package/dist/collection/components/shared/kritzel-button/kritzel-button.js +2 -2
  30. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  31. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
  32. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  33. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +2 -1
  34. package/dist/collection/components/shared/kritzel-input/kritzel-input.js +1 -1
  35. package/dist/collection/components/shared/kritzel-master-detail/kritzel-master-detail.js +3 -3
  36. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  37. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  38. package/dist/collection/components/shared/kritzel-numeric-input/kritzel-numeric-input.js +1 -1
  39. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +1 -1
  40. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  41. package/dist/collection/components/shared/kritzel-slide-toggle/kritzel-slide-toggle.js +1 -1
  42. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  43. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +2 -1
  44. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +2 -2
  45. package/dist/collection/components/ui/kritzel-back-to-content/kritzel-back-to-content.js +1 -1
  46. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +41 -6
  47. package/dist/collection/components/ui/kritzel-current-user/kritzel-current-user.js +36 -1
  48. package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js +36 -1
  49. package/dist/collection/components/ui/kritzel-export/kritzel-export.js +44 -7
  50. package/dist/collection/components/ui/kritzel-login-dialog/kritzel-login-dialog.js +1 -1
  51. package/dist/collection/components/ui/kritzel-more-menu/kritzel-more-menu.js +36 -1
  52. package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +108 -14
  53. package/dist/collection/components/ui/kritzel-share-dialog/kritzel-share-dialog.js +38 -3
  54. package/dist/collection/components/ui/kritzel-tool-config/kritzel-tool-config.js +38 -3
  55. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +36 -1
  56. package/dist/collection/components/ui/kritzel-workspace-manager/kritzel-workspace-manager.js +38 -3
  57. package/dist/collection/components/ui/kritzel-zoom-panel/kritzel-zoom-panel.css +72 -0
  58. package/dist/collection/components/ui/kritzel-zoom-panel/kritzel-zoom-panel.js +173 -0
  59. package/dist/collection/constants/engine.constants.js +2 -0
  60. package/dist/collection/constants/license.constants.js +25 -0
  61. package/dist/collection/constants/version.js +1 -1
  62. package/dist/collection/helpers/localization.helper.js +25 -0
  63. package/dist/collection/helpers/math.helper.js +3 -0
  64. package/dist/collection/helpers/svg-export.helper.js +223 -26
  65. package/dist/collection/index.js +13 -0
  66. package/dist/collection/interfaces/localization.interface.js +1 -0
  67. package/dist/collection/locales/de-locale.js +119 -0
  68. package/dist/collection/locales/en-locale.js +120 -0
  69. package/dist/collection/locales/fr-locale.js +119 -0
  70. package/dist/collection/themes/dark-theme.js +18 -0
  71. package/dist/collection/themes/light-theme.js +18 -0
  72. package/dist/components/index.d.ts +4 -0
  73. package/dist/components/index.js +1 -1
  74. package/dist/components/kritzel-active-users.js +1 -1
  75. package/dist/components/kritzel-avatar.js +1 -1
  76. package/dist/components/kritzel-awareness-cursors.js +1 -1
  77. package/dist/components/kritzel-back-to-content.js +1 -1
  78. package/dist/components/kritzel-brush-style.js +1 -1
  79. package/dist/components/kritzel-button.js +1 -1
  80. package/dist/components/kritzel-color-palette.js +1 -1
  81. package/dist/components/kritzel-color.js +1 -1
  82. package/dist/components/kritzel-context-menu.js +1 -1
  83. package/dist/components/kritzel-controls.js +1 -1
  84. package/dist/components/kritzel-current-user-dialog.js +1 -1
  85. package/dist/components/kritzel-current-user.js +1 -1
  86. package/dist/components/kritzel-editor.js +1 -1
  87. package/dist/components/kritzel-engine.js +1 -1
  88. package/dist/components/kritzel-export.js +1 -1
  89. package/dist/components/kritzel-font-size.js +1 -1
  90. package/dist/components/kritzel-font.js +1 -1
  91. package/dist/components/kritzel-icon.js +1 -1
  92. package/dist/components/kritzel-input.js +1 -1
  93. package/dist/components/kritzel-login-dialog.js +1 -1
  94. package/dist/components/kritzel-master-detail.js +1 -1
  95. package/dist/components/kritzel-menu-item.js +1 -1
  96. package/dist/components/kritzel-menu.js +1 -1
  97. package/dist/components/kritzel-more-menu.js +1 -1
  98. package/dist/components/kritzel-numeric-input.js +1 -1
  99. package/dist/components/kritzel-opacity-slider.js +1 -1
  100. package/dist/components/kritzel-pill-tabs.js +1 -1
  101. package/dist/components/kritzel-portal.js +1 -1
  102. package/dist/components/kritzel-settings.js +1 -1
  103. package/dist/components/kritzel-share-dialog.js +1 -1
  104. package/dist/components/kritzel-slide-toggle.js +1 -1
  105. package/dist/components/kritzel-split-button.js +1 -1
  106. package/dist/components/kritzel-stroke-size.js +1 -1
  107. package/dist/components/kritzel-tool-config.js +1 -1
  108. package/dist/components/kritzel-tooltip.js +1 -1
  109. package/dist/components/kritzel-utility-panel.js +1 -1
  110. package/dist/components/kritzel-watermark.d.ts +11 -0
  111. package/dist/components/kritzel-watermark.js +1 -0
  112. package/dist/components/kritzel-workspace-manager.js +1 -1
  113. package/dist/components/kritzel-zoom-panel.d.ts +11 -0
  114. package/dist/components/kritzel-zoom-panel.js +1 -0
  115. package/dist/components/{p-B5xxfwKF.js → p-3HxnBrCM.js} +1 -1
  116. package/dist/components/p-6RjeGuvH.js +1 -0
  117. package/dist/components/p-7NsK0uHu.js +1 -0
  118. package/dist/components/{p-dcAernE1.js → p-BCNyR5Sw.js} +1 -1
  119. package/dist/components/{p-C2SX-XRr.js → p-BG6hOSrm.js} +1 -1
  120. package/dist/components/p-BKJSh8qQ.js +1 -0
  121. package/dist/components/{p-SptaSMno.js → p-BKvHg9cv.js} +1 -1
  122. package/dist/components/p-Bc55X65h.js +1 -0
  123. package/dist/components/p-BpnIvNvq.js +1 -0
  124. package/dist/components/p-BvRrA4hN.js +1 -0
  125. package/dist/components/{p-B2w8X7vn.js → p-BxpKq94F.js} +1 -1
  126. package/dist/components/{p-BFoK4W--.js → p-Bzv9Px8v.js} +1 -1
  127. package/dist/components/{p-COLHjboZ.js → p-C9HGoDHE.js} +1 -1
  128. package/dist/components/p-CEnEDaix.js +1 -0
  129. package/dist/components/p-CIcLzcfA.js +1 -0
  130. package/dist/components/p-CPtDfadX.js +1 -0
  131. package/dist/components/p-C_fKgKHu.js +9 -0
  132. package/dist/components/p-CdR76C4L.js +1 -0
  133. package/dist/components/p-Cu9KYyoq.js +1 -0
  134. package/dist/components/p-CyqRcqsO.js +1 -0
  135. package/dist/components/{p-UoPj5QjH.js → p-DDkmsPpV.js} +1 -1
  136. package/dist/components/{p-D-sRVAbQ.js → p-DI4vQRE3.js} +1 -1
  137. package/dist/components/{p-CJOhfMU5.js → p-DNdXJp8F.js} +1 -1
  138. package/dist/components/p-DX5K8xnh.js +1 -0
  139. package/dist/components/{p-DEy7zJCe.js → p-DZdgXCAx.js} +1 -1
  140. package/dist/components/p-DdH1cKED.js +1 -0
  141. package/dist/components/p-DdsSSqFY.js +1 -0
  142. package/dist/components/p-DgmtCdnL.js +1 -0
  143. package/dist/components/{p-BzYU3-MJ.js → p-DmWSRsjK.js} +1 -1
  144. package/dist/components/{p-Bj2laX89.js → p-Dz-Ti24X.js} +1 -1
  145. package/dist/components/{p-BiG1dxPS.js → p-F5_X4dZG.js} +1 -1
  146. package/dist/components/{p-x6doYeiI.js → p-IpoC5EEY.js} +1 -1
  147. package/dist/components/p-Jn6TNdfe.js +1 -0
  148. package/dist/components/{p-BfNHpqQ8.js → p-NuLP1xHe.js} +1 -1
  149. package/dist/components/{p-skWUIStn.js → p-SDZNC8GF.js} +1 -1
  150. package/dist/components/{p-BYmp9Ovv.js → p-U4oawa1x.js} +1 -1
  151. package/dist/components/{p-DM11KXUT.js → p-f8aW1ye7.js} +1 -1
  152. package/dist/components/p-v7dxxrL5.js +1 -0
  153. package/dist/components/p-vAeiXe6c.js +1 -0
  154. package/dist/esm/index-Dhio9uis.js +2 -2
  155. package/dist/esm/index.js +2 -2
  156. package/dist/esm/{kritzel-active-users_42.entry.js → kritzel-active-users_44.entry.js} +710 -147
  157. package/dist/esm/loader.js +1 -1
  158. package/dist/esm/{schema.constants-DiCnmIYK.js → schema.constants-DchTXG3V.js} +1163 -172
  159. package/dist/esm/stencil.js +1 -1
  160. package/dist/stencil/index.esm.js +1 -1
  161. package/dist/stencil/p-DchTXG3V.js +1 -0
  162. package/dist/stencil/p-c9a3807b.entry.js +9 -0
  163. package/dist/stencil/stencil.esm.js +1 -1
  164. package/dist/types/classes/core/core.class.d.ts +16 -0
  165. package/dist/types/classes/handlers/context-menu.handler.d.ts +13 -0
  166. package/dist/types/classes/managers/license.manager.d.ts +141 -0
  167. package/dist/types/classes/managers/localization.manager.d.ts +121 -0
  168. package/dist/types/classes/objects/custom-element.class.d.ts +2 -0
  169. package/dist/types/classes/objects/group.class.d.ts +6 -1
  170. package/dist/types/classes/objects/image.class.d.ts +1 -1
  171. package/dist/types/classes/objects/path.class.d.ts +3 -2
  172. package/dist/types/classes/objects/selection-group.class.d.ts +6 -1
  173. package/dist/types/classes/objects/shape.class.d.ts +2 -0
  174. package/dist/types/classes/objects/text.class.d.ts +2 -1
  175. package/dist/types/classes/tools/brush-tool.class.d.ts +1 -1
  176. package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +53 -1
  177. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +60 -8
  178. package/dist/types/components/core/kritzel-watermark/kritzel-watermark.d.ts +20 -0
  179. package/dist/types/components/ui/kritzel-controls/kritzel-controls.d.ts +3 -0
  180. package/dist/types/components/ui/kritzel-current-user/kritzel-current-user.d.ts +3 -0
  181. package/dist/types/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.d.ts +3 -0
  182. package/dist/types/components/ui/kritzel-export/kritzel-export.d.ts +4 -1
  183. package/dist/types/components/ui/kritzel-more-menu/kritzel-more-menu.d.ts +3 -0
  184. package/dist/types/components/ui/kritzel-settings/kritzel-settings.d.ts +16 -0
  185. package/dist/types/components/ui/kritzel-share-dialog/kritzel-share-dialog.d.ts +3 -0
  186. package/dist/types/components/ui/kritzel-tool-config/kritzel-tool-config.d.ts +3 -0
  187. package/dist/types/components/ui/kritzel-utility-panel/kritzel-utility-panel.d.ts +3 -0
  188. package/dist/types/components/ui/kritzel-workspace-manager/kritzel-workspace-manager.d.ts +3 -0
  189. package/dist/types/components/ui/kritzel-zoom-panel/kritzel-zoom-panel.d.ts +20 -0
  190. package/dist/types/components.d.ts +455 -36
  191. package/dist/types/constants/engine.constants.d.ts +2 -0
  192. package/dist/types/constants/license.constants.d.ts +25 -0
  193. package/dist/types/constants/version.d.ts +1 -1
  194. package/dist/types/helpers/localization.helper.d.ts +18 -0
  195. package/dist/types/helpers/math.helper.d.ts +1 -0
  196. package/dist/types/helpers/svg-export.helper.d.ts +81 -7
  197. package/dist/types/index.d.ts +15 -0
  198. package/dist/types/interfaces/context-menu-item.interface.d.ts +7 -1
  199. package/dist/types/interfaces/line-options.interface.d.ts +2 -0
  200. package/dist/types/interfaces/localization.interface.d.ts +143 -0
  201. package/dist/types/interfaces/object-change-event.interface.d.ts +7 -0
  202. package/dist/types/interfaces/path-options.interface.d.ts +2 -0
  203. package/dist/types/interfaces/settings.interface.d.ts +3 -0
  204. package/dist/types/interfaces/theme.interface.d.ts +27 -2
  205. package/dist/types/locales/de-locale.d.ts +5 -0
  206. package/dist/types/locales/en-locale.d.ts +6 -0
  207. package/dist/types/locales/fr-locale.d.ts +5 -0
  208. package/package.json +4 -7
  209. package/dist/components/p-2xYAGd0I.js +0 -1
  210. package/dist/components/p-B2Os1ya_.js +0 -1
  211. package/dist/components/p-BTEV1WwT.js +0 -1
  212. package/dist/components/p-BbactVA0.js +0 -1
  213. package/dist/components/p-BqwqGFQY.js +0 -1
  214. package/dist/components/p-C0TN5IAi.js +0 -1
  215. package/dist/components/p-CFgkUYoO.js +0 -1
  216. package/dist/components/p-COgo9OWy.js +0 -1
  217. package/dist/components/p-CUFKqzMC.js +0 -1
  218. package/dist/components/p-CUPYGT8c.js +0 -1
  219. package/dist/components/p-CcyIAi9S.js +0 -1
  220. package/dist/components/p-CmuNn1Tc.js +0 -1
  221. package/dist/components/p-DDYoDSrm.js +0 -1
  222. package/dist/components/p-DbB730vO.js +0 -1
  223. package/dist/components/p-Dc0a_Hb-.js +0 -9
  224. package/dist/components/p-DlwYHzSj.js +0 -1
  225. package/dist/components/p-FK7b3BGt.js +0 -1
  226. package/dist/components/p-J9_SwObO.js +0 -1
  227. package/dist/stencil/p-DiCnmIYK.js +0 -1
  228. package/dist/stencil/p-bbebe56c.entry.js +0 -9
@@ -0,0 +1,189 @@
1
+ import { LocalizationHelper } from "../../helpers/localization.helper";
2
+ import { EN_LOCALE } from "../../locales/en-locale";
3
+ import { DE_LOCALE } from "../../locales/de-locale";
4
+ import { FR_LOCALE } from "../../locales/fr-locale";
5
+ /** Key used to store the settings object in localStorage. */
6
+ const SETTINGS_STORAGE_KEY = 'kritzel-settings';
7
+ /** Default locale used when no stored preference exists. */
8
+ const DEFAULT_LOCALE = 'en';
9
+ /**
10
+ * Manages localization state and term resolution across the Kritzel editor.
11
+ *
12
+ * The manager is owned per {@link KritzelCore} instance (like the theme manager)
13
+ * so that multiple editors on the same page can use different languages in
14
+ * isolation.
15
+ *
16
+ * Term resolution follows a deterministic fallback chain for each key:
17
+ * 1. active locale's custom terms
18
+ * 2. active locale's built-in terms
19
+ * 3. fallback locale's custom terms
20
+ * 4. fallback locale's built-in terms
21
+ * 5. the key itself (last resort)
22
+ *
23
+ * Persistence of the selected locale is handled by the KritzelSettings
24
+ * component; this manager only reads the stored value.
25
+ */
26
+ export class KritzelLocalizationManager {
27
+ _core;
28
+ _storageKey;
29
+ /** Built-in locale definitions, keyed by locale code. */
30
+ _builtinLocales = new Map();
31
+ /** Consumer-registered locale definitions, keyed by locale code. */
32
+ _customLocales = new Map();
33
+ _currentLocale = DEFAULT_LOCALE;
34
+ _fallbackLocale = DEFAULT_LOCALE;
35
+ /**
36
+ * Creates a new KritzelLocalizationManager instance.
37
+ * Seeds the built-in locales and initializes the current locale from the
38
+ * settings object in localStorage (or the default locale).
39
+ *
40
+ * @param core - The KritzelCore instance this manager belongs to
41
+ */
42
+ constructor(core) {
43
+ this._core = core;
44
+ this._storageKey = core.editorId ? `${SETTINGS_STORAGE_KEY}-${core.editorId}` : SETTINGS_STORAGE_KEY;
45
+ this._builtinLocales.set(EN_LOCALE.code, EN_LOCALE);
46
+ this._builtinLocales.set(DE_LOCALE.code, DE_LOCALE);
47
+ this._builtinLocales.set(FR_LOCALE.code, FR_LOCALE);
48
+ this._currentLocale = this.getStoredLocale();
49
+ }
50
+ /**
51
+ * Gets the currently active locale code.
52
+ */
53
+ get currentLocale() {
54
+ return this._currentLocale;
55
+ }
56
+ /**
57
+ * Gets the fallback locale code used when a term is missing from the active locale.
58
+ */
59
+ get fallbackLocale() {
60
+ return this._fallbackLocale;
61
+ }
62
+ /**
63
+ * Sets the fallback locale code used when a term is missing from the active locale.
64
+ *
65
+ * @param code - The fallback locale code
66
+ */
67
+ setFallbackLocale(code) {
68
+ this._fallbackLocale = code;
69
+ }
70
+ /**
71
+ * Registers consumer-provided locale definitions. Definitions are merged by
72
+ * code; registering the same code again replaces the previous definition.
73
+ * A registered locale may provide a partial set of terms — missing terms are
74
+ * resolved through the fallback chain.
75
+ *
76
+ * @param locales - The locale definitions to register
77
+ */
78
+ registerLocales(locales) {
79
+ for (const locale of locales) {
80
+ this._customLocales.set(locale.code, locale);
81
+ }
82
+ }
83
+ /**
84
+ * Sets the active locale and triggers a re-render so UI strings update.
85
+ *
86
+ * @param code - The locale code to activate
87
+ */
88
+ setLocale(code) {
89
+ this._currentLocale = code;
90
+ this._core.rerender();
91
+ }
92
+ /**
93
+ * Gets the list of available locale codes (built-in and registered).
94
+ */
95
+ getAvailableLocales() {
96
+ return Array.from(new Set([...this._builtinLocales.keys(), ...this._customLocales.keys()]));
97
+ }
98
+ /**
99
+ * Gets the available locales as `{ code, label }` pairs for use in a selector.
100
+ * A registered locale's label takes precedence over the built-in label; the
101
+ * code is used as the label when none is provided.
102
+ */
103
+ getAvailableLocaleOptions() {
104
+ return this.getAvailableLocales().map(code => ({ code, label: this.getLocaleLabel(code) }));
105
+ }
106
+ /**
107
+ * Gets the display label for a locale code, falling back to the code itself.
108
+ *
109
+ * @param code - The locale code
110
+ */
111
+ getLocaleLabel(code) {
112
+ return this._customLocales.get(code)?.label ?? this._builtinLocales.get(code)?.label ?? code;
113
+ }
114
+ /**
115
+ * Resolves a term key to its translated string for the active locale and
116
+ * interpolates any `{placeholder}` tokens.
117
+ *
118
+ * @param key - The term key to resolve
119
+ * @param vars - Optional values for `{placeholder}` interpolation
120
+ * @returns The translated, interpolated string (or the key if unresolved)
121
+ *
122
+ * @example
123
+ * manager.translate('menu.copy'); // 'Copy'
124
+ * manager.translate('share.expires', { date: '5/1' }); // 'Expires 5/1'
125
+ */
126
+ translate(key, vars) {
127
+ const resolved = this._customLocales.get(this._currentLocale)?.terms[key] ??
128
+ this._builtinLocales.get(this._currentLocale)?.terms[key] ??
129
+ this._customLocales.get(this._fallbackLocale)?.terms[key] ??
130
+ this._builtinLocales.get(this._fallbackLocale)?.terms[key] ??
131
+ key;
132
+ return LocalizationHelper.interpolate(resolved, vars);
133
+ }
134
+ /**
135
+ * Resolves every known term key for the active locale into a flat map.
136
+ *
137
+ * The key set is the union of all keys defined across the built-in and
138
+ * registered locales, so consumer-added keys are included. Each value is
139
+ * resolved through the same fallback chain as {@link translate} (without
140
+ * interpolation, since these are shared, variable-free UI labels).
141
+ *
142
+ * @returns A map of every term key to its resolved string for the active locale
143
+ */
144
+ getAllTerms() {
145
+ const keys = new Set();
146
+ for (const locale of [...this._builtinLocales.values(), ...this._customLocales.values()]) {
147
+ for (const key of Object.keys(locale.terms)) {
148
+ keys.add(key);
149
+ }
150
+ }
151
+ const result = {};
152
+ for (const key of keys) {
153
+ result[key] = this.translate(key);
154
+ }
155
+ return result;
156
+ }
157
+ /**
158
+ * Reads the stored locale from the settings object in localStorage using this
159
+ * instance's namespaced key.
160
+ *
161
+ * @returns The stored locale code if valid, or the default locale
162
+ */
163
+ getStoredLocale() {
164
+ if (typeof localStorage === 'undefined') {
165
+ return DEFAULT_LOCALE;
166
+ }
167
+ const stored = localStorage.getItem(this._storageKey);
168
+ if (!stored) {
169
+ return DEFAULT_LOCALE;
170
+ }
171
+ try {
172
+ const parsed = JSON.parse(stored);
173
+ if (typeof parsed?.locale === 'string') {
174
+ return parsed.locale;
175
+ }
176
+ }
177
+ catch {
178
+ // Invalid JSON, use default
179
+ }
180
+ return DEFAULT_LOCALE;
181
+ }
182
+ /**
183
+ * Cleans up the localization manager state.
184
+ * Provided for lifecycle parity with other managers.
185
+ */
186
+ cleanup() {
187
+ this._customLocales.clear();
188
+ }
189
+ }
@@ -1,4 +1,5 @@
1
1
  import { KritzelBaseObject } from "./base-object.class";
2
+ import { KritzelMathHelper } from "../../helpers/math.helper";
2
3
  /**
3
4
  * Represents a custom HTML element that can be placed and manipulated on the Kritzel canvas.
4
5
  * Extends KritzelBaseObject to inherit common object functionality like positioning,
@@ -25,6 +26,7 @@ export class KritzelCustomElement extends KritzelBaseObject {
25
26
  if (config) {
26
27
  this.translateX = config.translateX || 0;
27
28
  this.translateY = config.translateY || 0;
29
+ this.rotation = KritzelMathHelper.degreesToRadians(config.rotation ?? 0);
28
30
  this.scale = config.scale || 1;
29
31
  this.element = config.element;
30
32
  this.height = config.height || 0;
@@ -1,5 +1,6 @@
1
1
  import { KritzelBaseObject } from "./base-object.class";
2
2
  import { KritzelClassHelper } from "../../helpers/class.helper";
3
+ import { KritzelMathHelper } from "../../helpers/math.helper";
3
4
  /**
4
5
  * KritzelGroup represents a permanent grouping of objects that act as a single unit.
5
6
  *
@@ -14,6 +15,10 @@ import { KritzelClassHelper } from "../../helpers/class.helper";
14
15
  */
15
16
  export class KritzelGroup extends KritzelBaseObject {
16
17
  __class__ = 'KritzelGroup';
18
+ constructor(config) {
19
+ super();
20
+ this.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
21
+ }
17
22
  /**
18
23
  * IDs of child objects within this group.
19
24
  * Children can be any KritzelBaseObject, including other KritzelGroups for nesting.
@@ -65,8 +70,8 @@ export class KritzelGroup extends KritzelBaseObject {
65
70
  * @param core - The KritzelCore instance providing access to store and state management.
66
71
  * @returns A new KritzelGroup instance configured with the core context.
67
72
  */
68
- static create(core) {
69
- const group = new KritzelGroup();
73
+ static create(core, config) {
74
+ const group = new KritzelGroup(config);
70
75
  group._core = core;
71
76
  group.id = group.generateId();
72
77
  group.workspaceId = core.store.state.activeWorkspace.id;
@@ -1,5 +1,6 @@
1
1
  import { AssetNotFoundError } from "../providers/assets/asset-resolver.class";
2
2
  import { KritzelBaseObject } from "./base-object.class";
3
+ import { KritzelMathHelper } from "../../helpers/math.helper";
3
4
  /**
4
5
  * Represents an image object on the canvas.
5
6
  *
@@ -103,6 +104,7 @@ export class KritzelImage extends KritzelBaseObject {
103
104
  this.y = config?.y || 0;
104
105
  this.translateX = config?.translateX || 0;
105
106
  this.translateY = config?.translateY || 0;
107
+ this.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
106
108
  this.scale = config?.scale || 1;
107
109
  this.width = config?.width || 0;
108
110
  this.height = config?.height || 0;
@@ -116,17 +118,18 @@ export class KritzelImage extends KritzelBaseObject {
116
118
  * @param core - The KritzelCore instance providing access to store and state management.
117
119
  * @returns A new KritzelImage instance configured with the core context.
118
120
  */
119
- static create(core) {
120
- const object = new KritzelImage();
121
+ static create(core, config) {
122
+ const object = new KritzelImage(config);
121
123
  object._core = core;
122
124
  object.id = object.generateId();
123
125
  object.workspaceId = core.store.state.activeWorkspace.id;
124
126
  object.userId = core.user?.id;
125
- object.x = 0;
126
- object.y = 0;
127
- object.translateX = 0;
128
- object.translateY = 0;
129
- object.scale = object._core.store.state.scale;
127
+ object.x = config?.x ?? 0;
128
+ object.y = config?.y ?? 0;
129
+ object.translateX = config?.translateX ?? 0;
130
+ object.translateY = config?.translateY ?? 0;
131
+ object.scale = config?.scale ?? object._core.store.state.scale;
132
+ object.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
130
133
  object.zIndex = core.store.currentZIndex;
131
134
  return object;
132
135
  }
@@ -1,6 +1,7 @@
1
1
  import { KritzelBaseObject } from "./base-object.class";
2
2
  import { KritzelGeometryHelper } from "../../helpers/geometry.helper";
3
3
  import { KritzelColorHelper } from "../../helpers/color.helper";
4
+ import { KritzelMathHelper } from "../../helpers/math.helper";
4
5
  export class KritzelLine extends KritzelBaseObject {
5
6
  __class__ = 'KritzelLine';
6
7
  startX;
@@ -60,6 +61,7 @@ export class KritzelLine extends KritzelBaseObject {
60
61
  this.controlY = config?.controlY;
61
62
  this.translateX = config?.translateX ?? 0;
62
63
  this.translateY = config?.translateY ?? 0;
64
+ this.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
63
65
  this.scale = config?.scale ?? 1;
64
66
  this.strokeWidth = config?.strokeWidth ?? 4;
65
67
  this.stroke = config?.stroke ?? { light: '#000000', dark: '#ffffff' };
@@ -90,6 +92,7 @@ export class KritzelLine extends KritzelBaseObject {
90
92
  object.controlY = options?.controlY;
91
93
  object.translateX = options?.translateX ?? 0;
92
94
  object.translateY = options?.translateY ?? 0;
95
+ object.rotation = KritzelMathHelper.degreesToRadians(options?.rotation ?? 0);
93
96
  object.scale = options?.scale ?? 1;
94
97
  object.strokeWidth = options?.strokeWidth ?? 4;
95
98
  object.stroke = options?.stroke ?? { light: '#000000', dark: '#ffffff' };
@@ -44,6 +44,7 @@ export class KritzelPath extends KritzelBaseObject {
44
44
  this.points = config?.points ?? [];
45
45
  this.translateX = config?.translateX ?? 0;
46
46
  this.translateY = config?.translateY ?? 0;
47
+ this.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
47
48
  this.scale = config?.scale ?? 1;
48
49
  this.strokeWidth = config?.strokeWidth ?? 8;
49
50
  this.fill = config?.fill ?? { light: '#000000', dark: '#ffffff' };
@@ -67,6 +68,7 @@ export class KritzelPath extends KritzelBaseObject {
67
68
  object.points = options?.points ?? [];
68
69
  object.translateX = options?.translateX ?? 0;
69
70
  object.translateY = options?.translateY ?? 0;
71
+ object.rotation = KritzelMathHelper.degreesToRadians(options?.rotation ?? 0);
70
72
  object.scale = options?.scale ?? 1;
71
73
  object.strokeWidth = options?.strokeWidth ?? 8;
72
74
  object.fill = options?.fill ?? { light: '#000000', dark: '#ffffff' };
@@ -116,8 +118,12 @@ export class KritzelPath extends KritzelBaseObject {
116
118
  this.height = Math.max(...this.points.map(p => p[1])) - Math.min(...this.points.map(p => p[1])) + this.strokeWidth;
117
119
  this.x = Math.min(...this.points.map(p => p[0])) - this.strokeWidth / 2;
118
120
  this.y = Math.min(...this.points.map(p => p[1])) - this.strokeWidth / 2;
119
- this.translateX = x;
120
- this.translateY = y;
121
+ if (x !== null) {
122
+ this.translateX = x;
123
+ }
124
+ if (y !== null) {
125
+ this.translateY = y;
126
+ }
121
127
  this._adjustedPoints = null;
122
128
  this._core.store.objects.update(this);
123
129
  }
@@ -323,19 +329,14 @@ export class KritzelPath extends KritzelBaseObject {
323
329
  }
324
330
  /**
325
331
  * Updates width, height, x, y, translateX, and translateY based on current points.
326
- * Accounts for rotation and stroke width in the calculations.
332
+ * Uses the unrotated local points and stroke width for base dimensions.
327
333
  * Called during initial setup to establish the path's dimensions and position.
328
334
  */
329
335
  updateDimensions() {
330
- const rotatedPoints = this.points.map(([x, y]) => {
331
- const rotatedX = x * Math.cos(this.rotation) - y * Math.sin(this.rotation);
332
- const rotatedY = x * Math.sin(this.rotation) + y * Math.cos(this.rotation);
333
- return [rotatedX, rotatedY];
334
- });
335
- const minX = Math.min(...rotatedPoints.map(p => p[0] - this.strokeWidth / 2));
336
- const minY = Math.min(...rotatedPoints.map(p => p[1] - this.strokeWidth / 2));
337
- const maxX = Math.max(...rotatedPoints.map(p => p[0] + this.strokeWidth / 2));
338
- const maxY = Math.max(...rotatedPoints.map(p => p[1] + this.strokeWidth / 2));
336
+ const minX = Math.min(...this.points.map(p => p[0])) - this.strokeWidth / 2;
337
+ const minY = Math.min(...this.points.map(p => p[1])) - this.strokeWidth / 2;
338
+ const maxX = Math.max(...this.points.map(p => p[0])) + this.strokeWidth / 2;
339
+ const maxY = Math.max(...this.points.map(p => p[1])) + this.strokeWidth / 2;
339
340
  this.width = maxX - minX + this.lineSlack;
340
341
  this.height = maxY - minY + this.lineSlack;
341
342
  this.x = minX;
@@ -1,6 +1,7 @@
1
1
  import { KritzelGeometryHelper } from "../../helpers/geometry.helper";
2
2
  import { KritzelBaseObject } from "./base-object.class";
3
3
  import { KritzelClassHelper } from "../../helpers/class.helper";
4
+ import { KritzelMathHelper } from "../../helpers/math.helper";
4
5
  /**
5
6
  * Represents a selection group that manages multiple selected objects on the canvas.
6
7
  * The selection group allows batch operations like move, resize, and rotate on multiple objects simultaneously.
@@ -8,6 +9,10 @@ import { KritzelClassHelper } from "../../helpers/class.helper";
8
9
  */
9
10
  export class KritzelSelectionGroup extends KritzelBaseObject {
10
11
  __class__ = 'KritzelSelectionGroup';
12
+ constructor(config) {
13
+ super();
14
+ this.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
15
+ }
11
16
  // Store only object IDs instead of full objects
12
17
  _objectIds = [];
13
18
  // Cached objects array - invalidated when objectIds changes
@@ -91,8 +96,8 @@ export class KritzelSelectionGroup extends KritzelBaseObject {
91
96
  * @param core - The KritzelCore instance to associate with this selection group
92
97
  * @returns A new KritzelSelectionGroup instance configured with default settings
93
98
  */
94
- static create(core) {
95
- const object = new KritzelSelectionGroup();
99
+ static create(core, config) {
100
+ const object = new KritzelSelectionGroup(config);
96
101
  object._core = core;
97
102
  object.id = object.generateId();
98
103
  object.workspaceId = core.store.state.activeWorkspace.id;
@@ -11,6 +11,7 @@ import { addListNodes } from "prosemirror-schema-list";
11
11
  import { keymap } from "prosemirror-keymap";
12
12
  import { baseKeymap } from "prosemirror-commands";
13
13
  import { KritzelColorHelper } from "../../helpers/color.helper";
14
+ import { KritzelMathHelper } from "../../helpers/math.helper";
14
15
  export class KritzelShape extends KritzelBaseObject {
15
16
  __class__ = 'KritzelShape';
16
17
  shapeType = ShapeType.Rectangle;
@@ -75,6 +76,7 @@ export class KritzelShape extends KritzelBaseObject {
75
76
  this.y = config.y ?? 0;
76
77
  this.translateX = config.translateX ?? 0;
77
78
  this.translateY = config.translateY ?? 0;
79
+ this.rotation = KritzelMathHelper.degreesToRadians(config.rotation ?? 0);
78
80
  this.width = config.width ?? 100;
79
81
  this.height = config.height ?? 100;
80
82
  this.shapeType = config.shapeType ?? ShapeType.Rectangle;
@@ -106,6 +108,7 @@ export class KritzelShape extends KritzelBaseObject {
106
108
  object.y = config?.y ?? 0;
107
109
  object.translateX = config?.translateX ?? 0;
108
110
  object.translateY = config?.translateY ?? 0;
111
+ object.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
109
112
  object.width = config?.width ?? 100;
110
113
  object.height = config?.height ?? 100;
111
114
  object.shapeType = config?.shapeType ?? ShapeType.Rectangle;
@@ -9,6 +9,7 @@ import { addListNodes } from "prosemirror-schema-list";
9
9
  import { keymap } from "prosemirror-keymap";
10
10
  import { baseKeymap } from "prosemirror-commands";
11
11
  import { KritzelColorHelper } from "../../helpers/color.helper";
12
+ import { KritzelMathHelper } from "../../helpers/math.helper";
12
13
  /**
13
14
  * Represents a text object on the canvas that supports rich text editing via ProseMirror.
14
15
  * Extends the base object class to inherit common object properties and behaviors.
@@ -73,6 +74,7 @@ export class KritzelText extends KritzelBaseObject {
73
74
  */
74
75
  constructor(config) {
75
76
  super();
77
+ this.rotation = KritzelMathHelper.degreesToRadians(config?.rotation ?? 0);
76
78
  // Always create the editor so setContent() works immediately
77
79
  this.editor = this.createEditor();
78
80
  if (config) {
@@ -111,7 +113,7 @@ export class KritzelText extends KritzelBaseObject {
111
113
  * @param scale - Optional scale factor (defaults to current viewport scale).
112
114
  * @returns A new, fully initialized KritzelText instance.
113
115
  */
114
- static create(core, fontSize, fontFamily, scale) {
116
+ static create(core, fontSize, fontFamily, scale, rotation) {
115
117
  const object = new KritzelText();
116
118
  object._core = core;
117
119
  object.id = object.generateId();
@@ -121,6 +123,7 @@ export class KritzelText extends KritzelBaseObject {
121
123
  object.fontFamily = fontFamily || 'Arial';
122
124
  object.translateX = 0;
123
125
  object.translateY = 0;
126
+ object.rotation = KritzelMathHelper.degreesToRadians(rotation ?? 0);
124
127
  const coreScale = core.store.state.scale;
125
128
  const effectiveScale = coreScale < 0 ? coreScale : 1;
126
129
  object.width = object.initialWidth / effectiveScale;
@@ -82,6 +82,7 @@ KritzelIconRegistry.registerIcons({
82
82
  'undo': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-undo-icon lucide-undo"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>',
83
83
  'redo': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-redo-icon lucide-redo"><path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"/></svg>',
84
84
  'plus': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus-icon lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>',
85
+ 'minus': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-minus-icon lucide-minus"><path d="M5 12h14"/></svg>',
85
86
  'ellipsis-vertical': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ellipsis-vertical-icon lucide-ellipsis-vertical"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>',
86
87
  'x': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
87
88
  'check': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>',
@@ -28,7 +28,7 @@ export class KritzelBrushTool extends KritzelBaseTool {
28
28
  * websocket traffic without visible quality loss — `perfect-freehand`
29
29
  * already smooths the rendered stroke.
30
30
  */
31
- static MIN_POINT_DISTANCE_PX = 5;
31
+ static MIN_POINT_DISTANCE_PX = 3;
32
32
  /** Tracks the ID of the path currently being drawn */
33
33
  _currentPathId = null;
34
34
  /**
@@ -11,6 +11,7 @@
11
11
  "components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js",
12
12
  "components/core/kritzel-cursor-trail/kritzel-cursor-trail.js",
13
13
  "components/core/kritzel-editor/kritzel-editor.js",
14
+ "components/core/kritzel-watermark/kritzel-watermark.js",
14
15
  "components/shared/kritzel-avatar/kritzel-avatar.js",
15
16
  "components/shared/kritzel-button/kritzel-button.js",
16
17
  "components/shared/kritzel-color/kritzel-color.js",
@@ -42,7 +43,8 @@
42
43
  "components/ui/kritzel-share-dialog/kritzel-share-dialog.js",
43
44
  "components/ui/kritzel-tool-config/kritzel-tool-config.js",
44
45
  "components/ui/kritzel-utility-panel/kritzel-utility-panel.js",
45
- "components/ui/kritzel-workspace-manager/kritzel-workspace-manager.js"
46
+ "components/ui/kritzel-workspace-manager/kritzel-workspace-manager.js",
47
+ "components/ui/kritzel-zoom-panel/kritzel-zoom-panel.js"
46
48
  ],
47
49
  "mixins": [],
48
50
  "compiler": {
@@ -2,6 +2,7 @@ kritzel-editor{
2
2
  display: flex;
3
3
  margin: 0;
4
4
  position: relative;
5
+ container-type: inline-size;
5
6
  overflow: hidden;
6
7
  width: 100%;
7
8
  height: 100%;
@@ -52,6 +53,21 @@ kritzel-controls.keyboard-open {
52
53
  gap: 8px;
53
54
  }
54
55
 
56
+ .bottom-left-buttons {
57
+ position: absolute;
58
+ left: var(--kritzel-editor-top-left-buttons-left, 14px);
59
+ bottom: var(--kritzel-editor-controls-bottom, 14px);
60
+ display: flex;
61
+ align-items: flex-end;
62
+ }
63
+
64
+ /* Hide the zoom panel when the editor container is narrow. */
65
+ @container (max-width: 767px) {
66
+ .bottom-left-buttons {
67
+ display: none;
68
+ }
69
+ }
70
+
55
71
  .top-right-button {
56
72
  display: flex;
57
73
  align-items: center;