kritzel-stencil 0.3.8 → 0.3.10

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 (182) hide show
  1. package/dist/cjs/index.cjs.js +2 -1
  2. package/dist/cjs/kritzel-active-users_42.cjs.entry.js +301 -126
  3. package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
  4. package/dist/cjs/loader.cjs.js +1 -1
  5. package/dist/cjs/{schema.constants-rCfWpcBV.js → schema.constants-BNMNpzvA.js} +77 -12
  6. package/dist/cjs/stencil.cjs.js +1 -1
  7. package/dist/collection/classes/core/core.class.js +3 -0
  8. package/dist/collection/classes/handlers/context-menu.handler.js +54 -0
  9. package/dist/collection/classes/managers/theme.manager.js +47 -5
  10. package/dist/collection/classes/objects/selection-box.class.js +2 -2
  11. package/dist/collection/classes/objects/selection-group.class.js +3 -3
  12. package/dist/collection/classes/objects/text.class.js +8 -0
  13. package/dist/collection/classes/registries/icon-registry.class.js +2 -1
  14. package/dist/collection/classes/tools/text-tool.class.js +2 -0
  15. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js +1 -1
  16. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
  17. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +168 -17
  18. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +83 -1
  19. package/dist/collection/components/shared/kritzel-avatar/kritzel-avatar.js +3 -3
  20. package/dist/collection/components/shared/kritzel-brush-style/kritzel-brush-style.js +1 -1
  21. package/dist/collection/components/shared/kritzel-button/kritzel-button.js +2 -2
  22. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +8 -8
  23. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +7 -7
  24. package/dist/collection/components/shared/kritzel-dropdown/kritzel-dropdown.js +7 -7
  25. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  26. package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.js +1 -1
  27. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  28. package/dist/collection/components/shared/kritzel-input/kritzel-input.js +1 -1
  29. package/dist/collection/components/shared/kritzel-line-endings/kritzel-line-endings.js +2 -2
  30. package/dist/collection/components/shared/kritzel-master-detail/kritzel-master-detail.js +3 -3
  31. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  32. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  33. package/dist/collection/components/shared/kritzel-numeric-input/kritzel-numeric-input.js +1 -1
  34. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +2 -1
  35. package/dist/collection/components/shared/kritzel-pill-tabs/kritzel-pill-tabs.js +1 -1
  36. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  37. package/dist/collection/components/shared/kritzel-shape-fill/kritzel-shape-fill.js +2 -2
  38. package/dist/collection/components/shared/kritzel-slide-toggle/kritzel-slide-toggle.js +1 -1
  39. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  40. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
  41. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +2 -2
  42. package/dist/collection/components/ui/kritzel-back-to-content/kritzel-back-to-content.js +1 -1
  43. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +94 -48
  44. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +1 -1
  45. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +15 -14
  46. package/dist/collection/components/ui/kritzel-current-user/kritzel-current-user.js +1 -1
  47. package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js +1 -1
  48. package/dist/collection/components/ui/kritzel-export/kritzel-export.js +1 -1
  49. package/dist/collection/components/ui/kritzel-login-dialog/kritzel-login-dialog.js +1 -1
  50. package/dist/collection/components/ui/kritzel-more-menu/kritzel-more-menu.js +1 -1
  51. package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +28 -9
  52. package/dist/collection/components/ui/kritzel-share-dialog/kritzel-share-dialog.js +2 -2
  53. package/dist/collection/components/ui/kritzel-tool-config/kritzel-tool-config.js +6 -6
  54. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  55. package/dist/collection/constants/color-palette.constants.js +4 -1
  56. package/dist/collection/constants/version.js +1 -1
  57. package/dist/collection/index.js +2 -0
  58. package/dist/collection/themes/dark-theme.js +4 -0
  59. package/dist/collection/themes/light-theme.js +4 -0
  60. package/dist/components/index.js +1 -1
  61. package/dist/components/kritzel-active-users.js +1 -1
  62. package/dist/components/kritzel-avatar.js +1 -1
  63. package/dist/components/kritzel-awareness-cursors.js +1 -1
  64. package/dist/components/kritzel-back-to-content.js +1 -1
  65. package/dist/components/kritzel-brush-style.js +1 -1
  66. package/dist/components/kritzel-button.js +1 -1
  67. package/dist/components/kritzel-color-palette.js +1 -1
  68. package/dist/components/kritzel-color.js +1 -1
  69. package/dist/components/kritzel-context-menu.js +1 -1
  70. package/dist/components/kritzel-controls.js +1 -1
  71. package/dist/components/kritzel-current-user-dialog.js +1 -1
  72. package/dist/components/kritzel-current-user.js +1 -1
  73. package/dist/components/kritzel-cursor-trail.js +1 -1
  74. package/dist/components/kritzel-dropdown.js +1 -1
  75. package/dist/components/kritzel-editor.js +1 -1
  76. package/dist/components/kritzel-engine.js +1 -1
  77. package/dist/components/kritzel-export.js +1 -1
  78. package/dist/components/kritzel-font-family.js +1 -1
  79. package/dist/components/kritzel-font-size.js +1 -1
  80. package/dist/components/kritzel-font.js +1 -1
  81. package/dist/components/kritzel-icon.js +1 -1
  82. package/dist/components/kritzel-input.js +1 -1
  83. package/dist/components/kritzel-line-endings.js +1 -1
  84. package/dist/components/kritzel-login-dialog.js +1 -1
  85. package/dist/components/kritzel-master-detail.js +1 -1
  86. package/dist/components/kritzel-menu-item.js +1 -1
  87. package/dist/components/kritzel-menu.js +1 -1
  88. package/dist/components/kritzel-more-menu.js +1 -1
  89. package/dist/components/kritzel-numeric-input.js +1 -1
  90. package/dist/components/kritzel-opacity-slider.js +1 -1
  91. package/dist/components/kritzel-pill-tabs.js +1 -1
  92. package/dist/components/kritzel-portal.js +1 -1
  93. package/dist/components/kritzel-settings.js +1 -1
  94. package/dist/components/kritzel-shape-fill.js +1 -1
  95. package/dist/components/kritzel-share-dialog.js +1 -1
  96. package/dist/components/kritzel-slide-toggle.js +1 -1
  97. package/dist/components/kritzel-split-button.js +1 -1
  98. package/dist/components/kritzel-stroke-size.js +1 -1
  99. package/dist/components/kritzel-tool-config.js +1 -1
  100. package/dist/components/kritzel-tooltip.js +1 -1
  101. package/dist/components/kritzel-utility-panel.js +1 -1
  102. package/dist/components/kritzel-workspace-manager.js +1 -1
  103. package/dist/components/{p-CJjwjpMH.js → p-BFgWBbpu.js} +1 -1
  104. package/dist/components/{p-CqAkznU_.js → p-BI_UUiTr.js} +1 -1
  105. package/dist/components/p-BPEn0_hr.js +1 -0
  106. package/dist/components/{p-Cz2gQKbL.js → p-B_JH91jB.js} +1 -1
  107. package/dist/components/{p-BV3EJRtU.js → p-Bp3kdH4l.js} +1 -1
  108. package/dist/components/p-C0wFAtT_.js +1 -0
  109. package/dist/components/p-C8ggg-5h.js +1 -0
  110. package/dist/components/{p-B638ZH7S.js → p-CARNM9pf.js} +1 -1
  111. package/dist/components/p-CB7ynHtI.js +1 -0
  112. package/dist/components/{p-DDBaFNFi.js → p-CJ2V42sz.js} +1 -1
  113. package/dist/components/{p-A7Ult9iv.js → p-CJERvHdy.js} +1 -1
  114. package/dist/components/{p-CrSLn46K.js → p-CKY7AvGR.js} +1 -1
  115. package/dist/components/{p-C4vg_-vg.js → p-COIxq81R.js} +1 -1
  116. package/dist/components/p-CT2IjyIk.js +1 -0
  117. package/dist/components/{p-B5a3arJg.js → p-CWgI1dA0.js} +1 -1
  118. package/dist/components/{p-0cs6zQLB.js → p-CYR9wbJg.js} +1 -1
  119. package/dist/components/{p-CrmWVXea.js → p-Cr7xOsIZ.js} +1 -1
  120. package/dist/components/{p-qBqQhAmh.js → p-CxtTuKCy.js} +1 -1
  121. package/dist/components/{p-DEd2L0e3.js → p-D0aom7Yu.js} +1 -1
  122. package/dist/components/{p-DwHZN643.js → p-D15NO5kE.js} +1 -1
  123. package/dist/components/p-DH-H7om7.js +1 -0
  124. package/dist/components/{p-PMiFTdm6.js → p-DJLJfKY2.js} +1 -1
  125. package/dist/components/{p-W0nK9EQJ.js → p-DLlIaDNn.js} +2 -2
  126. package/dist/components/{p-DXO_ppUK.js → p-DRB3TZzI.js} +1 -1
  127. package/dist/components/{p-CaKSDRid.js → p-DXgUuzXW.js} +1 -1
  128. package/dist/components/{p-ihbmwmHg.js → p-DdmJquQr.js} +1 -1
  129. package/dist/components/{p-Czaea0WP.js → p-DfH7YY2C.js} +1 -1
  130. package/dist/components/{p-CTj2UdbS.js → p-DgtrNOWm.js} +1 -1
  131. package/dist/components/{p-D6KNaj_Y.js → p-DhAM4qeQ.js} +1 -1
  132. package/dist/components/{p-DMfU0hHe.js → p-DmTG0Y5h.js} +1 -1
  133. package/dist/components/{p-BMsKd6TF.js → p-Dov3qOAR.js} +1 -1
  134. package/dist/components/{p-CvCTQQcJ.js → p-Dw9sKOsb.js} +1 -1
  135. package/dist/components/{p-CSODtZrV.js → p-Dx_xz_El.js} +1 -1
  136. package/dist/components/{p-BVEYAGm1.js → p-IiG44Unz.js} +1 -1
  137. package/dist/components/{p-DsxW_miC.js → p-K7ySy791.js} +1 -1
  138. package/dist/components/{p-Bda1I4pR.js → p-KVG5rztB.js} +1 -1
  139. package/dist/components/{p-C_OSXZqJ.js → p-KjtNlFTl.js} +1 -1
  140. package/dist/components/{p-DVEfOb8T.js → p-RnuCSIt-.js} +1 -1
  141. package/dist/components/{p-Z9_amVdR.js → p-ZgZqbJ58.js} +1 -1
  142. package/dist/components/{p-C4bAtxyk.js → p-guqEWGgV.js} +1 -1
  143. package/dist/components/{p-DemKKw9U.js → p-u0b2RJAn.js} +1 -1
  144. package/dist/components/{p-BLjdzUzs.js → p-x38RbGJA.js} +1 -1
  145. package/dist/esm/index.js +2 -2
  146. package/dist/esm/kritzel-active-users_42.entry.js +301 -126
  147. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  148. package/dist/esm/loader.js +1 -1
  149. package/dist/esm/{schema.constants-cuIrI5X8.js → schema.constants-CqBoZbmA.js} +77 -13
  150. package/dist/esm/stencil.js +1 -1
  151. package/dist/stencil/index.esm.js +1 -1
  152. package/dist/stencil/p-3372fb1e.entry.js +9 -0
  153. package/dist/stencil/{p-10c2b77c.entry.js → p-69298b5f.entry.js} +1 -1
  154. package/dist/stencil/p-CqBoZbmA.js +1 -0
  155. package/dist/stencil/stencil.esm.js +1 -1
  156. package/dist/types/classes/handlers/context-menu.handler.d.ts +14 -0
  157. package/dist/types/classes/managers/theme.manager.d.ts +22 -2
  158. package/dist/types/classes/objects/text.class.d.ts +1 -0
  159. package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +14 -2
  160. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +18 -1
  161. package/dist/types/components/shared/kritzel-color/kritzel-color.d.ts +3 -2
  162. package/dist/types/components/shared/kritzel-color-palette/kritzel-color-palette.d.ts +3 -2
  163. package/dist/types/components/ui/kritzel-context-menu/kritzel-context-menu.d.ts +9 -3
  164. package/dist/types/components/ui/kritzel-controls/kritzel-controls.d.ts +2 -2
  165. package/dist/types/components/ui/kritzel-settings/kritzel-settings.d.ts +2 -1
  166. package/dist/types/components/ui/kritzel-tool-config/kritzel-tool-config.d.ts +3 -2
  167. package/dist/types/components.d.ts +61 -18
  168. package/dist/types/constants/color-palette.constants.d.ts +4 -2
  169. package/dist/types/constants/version.d.ts +1 -1
  170. package/dist/types/helpers/color.helper.d.ts +4 -3
  171. package/dist/types/helpers/svg-export.helper.d.ts +3 -3
  172. package/dist/types/index.d.ts +2 -0
  173. package/dist/types/interfaces/theme.interface.d.ts +7 -3
  174. package/package.json +1 -1
  175. package/dist/components/p-B8wX0-3H.js +0 -1
  176. package/dist/components/p-BvgGpgKP.js +0 -1
  177. package/dist/components/p-C-sJ1r3g.js +0 -1
  178. package/dist/components/p-CBTqCoUx.js +0 -1
  179. package/dist/components/p-DdlK75Kx.js +0 -1
  180. package/dist/components/p-DjAiIBXv.js +0 -1
  181. package/dist/stencil/p-9ce67a14.entry.js +0 -9
  182. package/dist/stencil/p-cuIrI5X8.js +0 -1
@@ -12,8 +12,9 @@ export class KritzelContextMenu {
12
12
  actionSelected;
13
13
  close;
14
14
  processedItems = [];
15
- openSubmenuIndex = null;
16
- submenuPosition = 'right';
15
+ /** Current open submenu path (e.g. '0.2.1'). Empty if none. */
16
+ openSubmenuPath = '';
17
+ submenuPositions = {};
17
18
  submenuTimer = null;
18
19
  submenuRefs = new Map();
19
20
  menuItemWrapperRefs = new Map();
@@ -31,7 +32,8 @@ export class KritzelContextMenu {
31
32
  }
32
33
  componentDidUpdate() {
33
34
  this.adjustPositionToViewport();
34
- this.adjustSubmenuPosition();
35
+ this.adjustSubmenuPositions();
36
+ this.pruneStaleRefs();
35
37
  }
36
38
  disconnectedCallback() {
37
39
  if (this.submenuTimer) {
@@ -68,60 +70,102 @@ export class KritzelContextMenu {
68
70
  this.host.style.top = `${newTop}px`;
69
71
  }
70
72
  }
71
- adjustSubmenuPosition() {
72
- if (this.openSubmenuIndex === null)
73
+ adjustSubmenuPositions() {
74
+ if (!this.openSubmenuPath)
73
75
  return;
74
- const submenuEl = this.submenuRefs.get(this.openSubmenuIndex);
75
- if (!submenuEl)
76
- return;
77
- const submenuRect = submenuEl.getBoundingClientRect();
78
76
  const viewportHeight = window.innerHeight;
79
- // Adjust vertical position if needed (horizontal is pre-calculated)
80
- if (submenuRect.bottom > viewportHeight - VIEWPORT_PADDING) {
81
- const overflow = submenuRect.bottom - (viewportHeight - VIEWPORT_PADDING);
82
- submenuEl.style.top = `${-overflow}px`;
77
+ // Adjust every open submenu in the chain (every prefix of openSubmenuPath).
78
+ for (const path of this.getOpenSubmenuPaths()) {
79
+ const submenuEl = this.submenuRefs.get(path);
80
+ const wrapperEl = this.menuItemWrapperRefs.get(path);
81
+ if (!submenuEl || !wrapperEl)
82
+ continue;
83
+ const wrapperRect = wrapperEl.getBoundingClientRect();
84
+ const submenuHeight = submenuEl.offsetHeight; // Constant regardless of current top shift
85
+ const naturalBottom = wrapperRect.top + submenuHeight;
86
+ if (naturalBottom > viewportHeight - VIEWPORT_PADDING) {
87
+ let overflow = naturalBottom - (viewportHeight - VIEWPORT_PADDING);
88
+ // Don't shift up so far that the top goes above the viewport
89
+ if (wrapperRect.top - overflow < VIEWPORT_PADDING) {
90
+ overflow = wrapperRect.top - VIEWPORT_PADDING;
91
+ }
92
+ submenuEl.style.top = `${-overflow}px`;
93
+ }
94
+ else {
95
+ submenuEl.style.top = '0px';
96
+ }
83
97
  }
84
- else {
85
- submenuEl.style.top = '0px';
98
+ }
99
+ getOpenSubmenuPaths() {
100
+ if (!this.openSubmenuPath)
101
+ return [];
102
+ const parts = this.openSubmenuPath.split('.');
103
+ const paths = [];
104
+ for (let i = 1; i <= parts.length; i++) {
105
+ paths.push(parts.slice(0, i).join('.'));
106
+ }
107
+ return paths;
108
+ }
109
+ isSubmenuOpen(path) {
110
+ return this.openSubmenuPath === path || this.openSubmenuPath.startsWith(path + '.');
111
+ }
112
+ getParentPath(path) {
113
+ const idx = path.lastIndexOf('.');
114
+ return idx === -1 ? '' : path.substring(0, idx);
115
+ }
116
+ pruneStaleRefs() {
117
+ const openPaths = new Set(this.getOpenSubmenuPaths());
118
+ for (const key of Array.from(this.submenuRefs.keys())) {
119
+ if (!openPaths.has(key)) {
120
+ this.submenuRefs.delete(key);
121
+ }
122
+ }
123
+ // Keep root-level wrapper refs (single-segment paths) always rendered; prune deeper
124
+ // wrappers whose parent submenu is no longer open.
125
+ for (const key of Array.from(this.menuItemWrapperRefs.keys())) {
126
+ const parent = this.getParentPath(key);
127
+ if (parent !== '' && !openPaths.has(parent)) {
128
+ this.menuItemWrapperRefs.delete(key);
129
+ }
86
130
  }
87
131
  }
88
132
  handleItemClick(item, isDisabled, hasChildren) {
89
133
  if (isDisabled)
90
134
  return;
91
135
  if (hasChildren) {
92
- // Toggle submenu on click for touch devices
136
+ // Hover handles open/close; click on a parent is a no-op.
93
137
  return;
94
138
  }
95
139
  if (item.action) {
96
140
  this.actionSelected.emit(item);
97
141
  }
98
142
  }
99
- handleItemMouseEnter(index, hasChildren) {
143
+ handleItemMouseEnter(path, hasChildren) {
100
144
  if (this.submenuTimer) {
101
145
  clearTimeout(this.submenuTimer);
102
146
  this.submenuTimer = null;
103
147
  }
148
+ const parentPath = this.getParentPath(path);
104
149
  if (hasChildren) {
105
150
  this.submenuTimer = setTimeout(() => {
106
- // Pre-calculate position before opening to avoid flicker
107
- const wrapperEl = this.menuItemWrapperRefs.get(index);
151
+ // Pre-calculate horizontal position before opening to avoid flicker.
152
+ const wrapperEl = this.menuItemWrapperRefs.get(path);
153
+ let position = 'right';
108
154
  if (wrapperEl) {
109
155
  const rect = wrapperEl.getBoundingClientRect();
110
156
  const viewportWidth = window.innerWidth;
111
- // Check if opening to the right would overflow
112
157
  const wouldOverflowRight = rect.right + ESTIMATED_SUBMENU_WIDTH > viewportWidth - VIEWPORT_PADDING;
113
- this.submenuPosition = wouldOverflowRight ? 'left' : 'right';
158
+ position = wouldOverflowRight ? 'left' : 'right';
114
159
  }
115
- else {
116
- this.submenuPosition = 'right';
117
- }
118
- this.openSubmenuIndex = index;
160
+ this.submenuPositions = { ...this.submenuPositions, [path]: position };
161
+ this.openSubmenuPath = path;
119
162
  }, SUBMENU_DELAY);
120
163
  }
121
164
  else {
122
- // Close any open submenu when hovering a non-parent item
165
+ // Hovering a sibling without children: collapse to the parent chain so any
166
+ // sibling-rooted submenu closes, but the ancestor chain stays open.
123
167
  this.submenuTimer = setTimeout(() => {
124
- this.openSubmenuIndex = null;
168
+ this.openSubmenuPath = parentPath;
125
169
  }, SUBMENU_DELAY);
126
170
  }
127
171
  }
@@ -131,9 +175,11 @@ export class KritzelContextMenu {
131
175
  this.submenuTimer = null;
132
176
  }
133
177
  }
134
- handleSubmenuMouseLeave() {
178
+ handleSubmenuMouseLeave(path) {
179
+ // Close this submenu (and any deeper levels) but keep the ancestor chain open.
180
+ const parentPath = this.getParentPath(path);
135
181
  this.submenuTimer = setTimeout(() => {
136
- this.openSubmenuIndex = null;
182
+ this.openSubmenuPath = parentPath;
137
183
  }, SUBMENU_DELAY);
138
184
  }
139
185
  async updateMenuItems() {
@@ -163,28 +209,28 @@ export class KritzelContextMenu {
163
209
  }
164
210
  return defaultValue;
165
211
  }
166
- renderSubmenu(processedChildren, parentIndex) {
167
- return (h("div", { class: { 'submenu-container': true, 'position-left': this.submenuPosition === 'left' }, ref: el => el && this.submenuRefs.set(parentIndex, el), onMouseEnter: () => this.handleSubmenuMouseEnter(), onMouseLeave: () => this.handleSubmenuMouseLeave() }, processedChildren.map(({ item, isDisabled, processedChildren: nestedChildren }, index) => {
168
- const prevItem = index > 0 ? processedChildren[index - 1].item : null;
212
+ renderItems(items, parentPath) {
213
+ return items.map(({ item, isDisabled, processedChildren }, index) => {
214
+ const path = parentPath === '' ? String(index) : `${parentPath}.${index}`;
215
+ const prevItem = index > 0 ? items[index - 1].item : null;
169
216
  const showDivider = prevItem && prevItem.group !== item.group;
170
- const hasChildren = nestedChildren && nestedChildren.length > 0;
217
+ const hasChildren = !!processedChildren && processedChildren.length > 0;
218
+ const submenuOpen = hasChildren && this.isSubmenuOpen(path);
171
219
  return [
172
- showDivider && h("div", { class: "menu-divider", key: `submenu-divider-${index}` }),
173
- h("button", { key: `submenu-${item.label}-${index}`, class: { 'menu-item': true, 'disabled': isDisabled, 'has-children': hasChildren }, onClick: () => this.handleItemClick(item, isDisabled, hasChildren), disabled: isDisabled }, item.icon && h("kritzel-icon", { name: item.icon, size: 16 }), h("span", { class: "label" }, item.label), hasChildren && h("kritzel-icon", { name: "chevron-right", size: 12, class: "submenu-arrow" }))
220
+ showDivider && h("div", { class: "menu-divider", key: `divider-${path}` }),
221
+ h("div", { class: "menu-item-wrapper", key: `wrapper-${path}`, ref: el => el && this.menuItemWrapperRefs.set(path, el), onMouseEnter: () => this.handleItemMouseEnter(path, hasChildren) }, h("button", { key: `${item.label}-${path}`, class: { 'menu-item': true, 'disabled': isDisabled, 'has-children': hasChildren, 'submenu-open': submenuOpen }, onClick: () => this.handleItemClick(item, isDisabled, hasChildren), disabled: isDisabled && !hasChildren }, item.icon && h("kritzel-icon", { name: item.icon, size: 16 }), h("span", { class: "label" }, item.label), hasChildren && h("kritzel-icon", { name: "chevron-right", size: 12, class: "submenu-arrow" })), hasChildren && submenuOpen && this.renderSubmenu(processedChildren, path)),
174
222
  ];
175
- })));
223
+ });
224
+ }
225
+ renderSubmenu(processedChildren, path) {
226
+ const position = this.submenuPositions[path] === 'left' ? 'left' : 'right';
227
+ return (h("div", { class: { 'submenu-container': true, 'position-left': position === 'left' }, key: `submenu-${path}`, ref: el => el && this.submenuRefs.set(path, el), onMouseEnter: () => this.handleSubmenuMouseEnter(), onMouseLeave: () => this.handleSubmenuMouseLeave(path) }, this.renderItems(processedChildren, path)));
176
228
  }
177
229
  render() {
178
- return (h(Host, { key: '100ec82feefbc285f8e50fca3f23f853c33aa215' }, h("div", { key: '9305a0190bdcf7a9e4b5ae7473651a0261dbfd7c', class: "menu-container" }, this.processedItems.map(({ item, isDisabled, processedChildren }, index) => {
179
- const prevItem = index > 0 ? this.processedItems[index - 1].item : null;
180
- const showDivider = prevItem && prevItem.group !== item.group;
181
- const hasChildren = processedChildren && processedChildren.length > 0;
182
- const isSubmenuOpen = this.openSubmenuIndex === index;
183
- return [
184
- showDivider && h("div", { class: "menu-divider", key: `divider-${index}` }),
185
- h("div", { class: "menu-item-wrapper", ref: el => el && this.menuItemWrapperRefs.set(index, el), onMouseEnter: () => this.handleItemMouseEnter(index, hasChildren) }, h("button", { key: `${item.label}-${index}`, class: { 'menu-item': true, 'disabled': isDisabled, 'has-children': hasChildren, 'submenu-open': isSubmenuOpen }, onClick: () => this.handleItemClick(item, isDisabled, hasChildren), disabled: isDisabled && !hasChildren }, item.icon && h("kritzel-icon", { name: item.icon, size: 16 }), h("span", { class: "label" }, item.label), hasChildren && h("kritzel-icon", { name: "chevron-right", size: 12, class: "submenu-arrow" })), hasChildren && isSubmenuOpen && this.renderSubmenu(processedChildren, index))
186
- ];
187
- }))));
230
+ if (!this.processedItems || this.processedItems.length === 0) {
231
+ return null;
232
+ }
233
+ return (h(Host, null, h("div", { class: "menu-container" }, this.renderItems(this.processedItems, ''))));
188
234
  }
189
235
  static get is() { return "kritzel-context-menu"; }
190
236
  static get encapsulation() { return "shadow"; }
@@ -253,8 +299,8 @@ export class KritzelContextMenu {
253
299
  static get states() {
254
300
  return {
255
301
  "processedItems": {},
256
- "openSubmenuIndex": {},
257
- "submenuPosition": {}
302
+ "openSubmenuPath": {},
303
+ "submenuPositions": {}
258
304
  };
259
305
  }
260
306
  static get events() {
@@ -118,7 +118,7 @@
118
118
  .kritzel-control-separator {
119
119
  width: 1px;
120
120
  height: 24px;
121
- background-color: var(--kritzel-controls-border, #ebebeb);
121
+ background-color: var(--kritzel-controls-separator-color, #ebebeb);
122
122
  margin: 0 4px;
123
123
  }
124
124
 
@@ -10,7 +10,7 @@ export class KritzelControls {
10
10
  activeControl = null;
11
11
  isUtilityPanelVisible = true;
12
12
  undoState = null;
13
- theme;
13
+ theme = 'light';
14
14
  isControlsReady;
15
15
  firstConfig = null;
16
16
  isTouchDevice = KritzelDevicesHelper.isTouchDevice();
@@ -230,13 +230,13 @@ export class KritzelControls {
230
230
  // Separate tool controls from config control
231
231
  const toolControls = this.internalControls.filter(c => c.type === 'tool' || c.type === 'separator');
232
232
  const configControl = this.internalControls.find(c => c.type === 'config' && c.name === this.firstConfig?.name);
233
- return (h(Host, { key: '7f2a5fed45ac89b34a86b87552ffaa1b94f44d8b', class: {
233
+ return (h(Host, { key: 'b567aac7bca12cc5ffb0ee1eb9e6978636aa3c31', class: {
234
234
  mobile: this.isTouchDevice,
235
- } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '2dcbbe498bce0b59fbd225d103f2e322d3b7ff85', style: {
235
+ } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '88e8ae9ae7429987724df70895b02a3f59216364', style: {
236
236
  position: 'absolute',
237
237
  bottom: '56px',
238
238
  left: '12px',
239
- }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '6431079040502a78f021c612a7e953b1349f319a', class: "kritzel-controls" }, h("div", { key: '6e0d6dce107cf6b54fb904809fd525ce3b2ae4f0', class: { 'scroll-indicator-left': true, 'visible': this.canScrollLeft } }), h("div", { key: '2686304ad10deac2d022e1deba19195587659d64', class: "kritzel-tools-scroll", ref: el => (this.toolsScrollRef = el), onScroll: this.handleToolsScroll }, toolControls.map(control => {
239
+ }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '658e3d7b94e49a002d5057c1fb4fc199a371c48d', class: "kritzel-controls" }, h("div", { key: 'b54bb52a43e4a94ae1148cd4e75528bcaad681ef', class: { 'scroll-indicator-left': true, 'visible': this.canScrollLeft } }), h("div", { key: '36ce760357d3228141281a45c0ac7b0024b04795', class: "kritzel-tools-scroll", ref: el => (this.toolsScrollRef = el), onScroll: this.handleToolsScroll }, toolControls.map(control => {
240
240
  // Check if this control has sub-options (split-button)
241
241
  if (control.subOptions?.length) {
242
242
  const selectedSubOption = this.getSelectedSubOption(control);
@@ -266,10 +266,10 @@ export class KritzelControls {
266
266
  'kritzel-control': true,
267
267
  'selected': this.activeControl?.name === control?.name,
268
268
  }, key: control.name, "data-testid": `tool-${control.name}`, onClick: _event => this.handleControlClick?.(control), "aria-label": control.name.charAt(0).toUpperCase() + control.name.slice(1) }, h("kritzel-icon", { name: control.icon })));
269
- })), h("div", { key: 'f4bcbc856f6e027cdb579356faf54288b43aa080', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight && !(configControl && this.activeControl && hasConfigUI) } }), configControl && this.activeControl && (h("div", { class: {
269
+ })), h("div", { key: 'f0b1e0f74fe197f4d39e307e7dd8dd4819c4b183', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight && !(configControl && this.activeControl && hasConfigUI) } }), configControl && this.activeControl && (h("div", { class: {
270
270
  'kritzel-config-container': true,
271
271
  'visible': hasConfigUI,
272
- }, key: configControl.name }, h("div", { key: '0646c98b32047f51841f75dd5fb57813ebb153f5', class: { 'config-gradient-left': true, 'visible': this.needsScrolling } }), h("kritzel-tooltip", { key: '0816b8d7ca55add3c6a81de788be3c61a8a814f2', anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), triggerElement: this.configTriggerRef }, h("kritzel-tool-config", { key: '8f299593454e7855d83ad5d79b528940b2dcb202', tool: this.activeControl.tool, theme: this.theme, engine: this.kritzelEngine, onToolChange: event => this.handleToolChange?.(event), onDisplayValuesChange: this.handleDisplayValuesChange, style: { width: '100%', height: '100%' } })), h("div", { key: '8b2dc1dcc61df93a92cea8ba2aeb776e57965ab3', tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", "data-testid": "tool-config", ref: el => {
272
+ }, key: configControl.name }, h("div", { key: '51cc3ebf13092e710048441ff64856edd4f53dfc', class: { 'config-gradient-left': true, 'visible': this.needsScrolling } }), h("kritzel-tooltip", { key: 'dcace186ae3ece1d7e943f51b48ed5094d847284', anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), triggerElement: this.configTriggerRef }, h("kritzel-tool-config", { key: '9b16ac90f335fec3c043545fa0c5b363ab99924e', tool: this.activeControl.tool, theme: this.theme, engine: this.kritzelEngine, onToolChange: event => this.handleToolChange?.(event), onDisplayValuesChange: this.handleDisplayValuesChange, style: { width: '100%', height: '100%' } })), h("div", { key: '2425507968e27a01b66c1d7be79a40ebe77cd27d', tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", "data-testid": "tool-config", ref: el => {
273
273
  if (el)
274
274
  this.configTriggerRef = el;
275
275
  }, onKeyDown: event => {
@@ -278,7 +278,7 @@ export class KritzelControls {
278
278
  }
279
279
  }, style: {
280
280
  cursor: 'pointer',
281
- } }, this.displayValues && (h("div", { key: '1074fc05048aed92ec9d62e347822df0a57a80e4', class: "color-container" }, h("kritzel-color", { key: '222efa6883ccbc6c97d10360039a1315de7d6273', value: this.displayValues.color, theme: this.theme, size: 18, style: {
281
+ } }, this.displayValues && (h("div", { key: 'd2499df3c0a90c101957f55664452739e0f1692b', class: "color-container" }, h("kritzel-color", { key: 'b7cfcd3a8579c63f508c2786eecace1223e88974', value: this.displayValues.color, theme: this.theme, size: 18, style: {
282
282
  borderRadius: '50%',
283
283
  border: 'none',
284
284
  } })))))))));
@@ -397,14 +397,14 @@ export class KritzelControls {
397
397
  "type": "string",
398
398
  "mutable": false,
399
399
  "complexType": {
400
- "original": "ThemeMode",
401
- "resolved": "\"dark\" | \"light\"",
400
+ "original": "ThemeName",
401
+ "resolved": "\"dark\" | \"light\" | string & {}",
402
402
  "references": {
403
- "ThemeMode": {
403
+ "ThemeName": {
404
404
  "location": "import",
405
- "path": "../../../constants/color-palette.constants",
406
- "id": "src/constants/color-palette.constants.ts::ThemeMode",
407
- "referenceLocation": "ThemeMode"
405
+ "path": "../../../interfaces/theme.interface",
406
+ "id": "src/interfaces/theme.interface.ts::ThemeName",
407
+ "referenceLocation": "ThemeName"
408
408
  }
409
409
  }
410
410
  },
@@ -417,7 +417,8 @@ export class KritzelControls {
417
417
  "getter": false,
418
418
  "setter": false,
419
419
  "reflect": false,
420
- "attribute": "theme"
420
+ "attribute": "theme",
421
+ "defaultValue": "'light'"
421
422
  }
422
423
  };
423
424
  }
@@ -15,7 +15,7 @@ export class KritzelCurrentUser {
15
15
  this.dialogRef?.open();
16
16
  };
17
17
  render() {
18
- return (h(Host, { key: 'c392caf731f8352fd8e2a95918fe48a2f00dd9e5' }, h("kritzel-avatar", { key: 'b3bdce0efa0c0610aa028303386c643d53bc8300', user: this.user, size: this.avatarSize, onClick: this.handleAvatarClick }), h("kritzel-current-user-dialog", { key: '5e7af1aea468028e091ad8f461e4352cb9f9636b', ref: el => (this.dialogRef = el), user: this.user })));
18
+ return (h(Host, { key: 'a735cb9f16f4898fde0b52573affa2d270a8f1de' }, h("kritzel-avatar", { key: 'd449a515182718ab4ef3b26b2277696bbc7ab46f', user: this.user, size: this.avatarSize, onClick: this.handleAvatarClick }), h("kritzel-current-user-dialog", { key: '3542f6df43c9924218e344f70bdc398c74a8eae6', ref: el => (this.dialogRef = el), user: this.user })));
19
19
  }
20
20
  static get is() { return "kritzel-current-user"; }
21
21
  static get encapsulation() { return "shadow"; }
@@ -21,7 +21,7 @@ export class KritzelCurrentUserDialog {
21
21
  }
22
22
  render() {
23
23
  const displayName = this.getDisplayName();
24
- return (h(Host, { key: 'e1dd44cdfdbaebfe886fed0d9feba2ef232b6615' }, h("kritzel-dialog", { key: 'cd3daa7abd53c10852d63a2fe53d919414cd8904', dialogTitle: "Account", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small", contained: true }, h("div", { key: '94d0a691ede73135e6cf4ef144c13e52e410ffbe', class: "user-info" }, h("kritzel-avatar", { key: 'e57592d2f3663b593534055be5aae1b224fa8906', user: this.user, size: 80 }), displayName && h("div", { key: '237db2d0608ee49ea70e5282b61a59077f0f4595', class: "user-name" }, displayName), this.user?.email && h("div", { key: 'd821e8171530b92ce6f1781c1145b611d3c533d0', class: "user-email" }, this.user.email)))));
24
+ return (h(Host, { key: '40c1a1bed0ddf02f9835199b5f7d2363e4d1902b' }, h("kritzel-dialog", { key: 'a83c09eac66ddf51155591a32245e3f15e34943e', dialogTitle: "Account", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small", contained: true }, h("div", { key: '14f7100a881ee3c5ba6b672d509bf3a9161ccd62', class: "user-info" }, h("kritzel-avatar", { key: 'e3552a80db81db4c26f81c6cc699363afa6153ea', user: this.user, size: 80 }), displayName && h("div", { key: 'c54164be605ac2bd2fc8bac6bb4481f820119028', class: "user-name" }, displayName), this.user?.email && h("div", { key: 'e6af7c44e45443eb24be0777768de96b0e3d249e', class: "user-email" }, this.user.email)))));
25
25
  }
26
26
  static get is() { return "kritzel-current-user-dialog"; }
27
27
  static get encapsulation() { return "shadow"; }
@@ -65,7 +65,7 @@ export class KritzelExport {
65
65
  return (h("div", { class: "export-tab-content" }, h("kritzel-input", { label: "Filename", value: this.exportFilename, placeholder: "Enter filename", suffix: ".json", onValueChange: this.handleFilenameChange })));
66
66
  }
67
67
  render() {
68
- return (h(Host, { key: '5178e66f75b94697c771e2dc6fe7ce317e21cd1a' }, h("kritzel-dialog", { key: 'f80cbe3fa709ed7e046303034b7345ca1f94bc48', isOpen: this.isDialogOpen, dialogTitle: "Export", closable: true, contained: true, onDialogClose: this.closeDialog }, h("div", { key: 'e7968807c2b67ebfc800cb1694b4e34af245ffba', class: "export-content" }, h("kritzel-pill-tabs", { key: 'eac62225c4c42431296f330791a1fb2212f579f5', tabs: this.tabs, value: this.activeTab, onValueChange: this.handleTabChange }), this.activeTab === 'viewport' && this.renderViewportExport(), this.activeTab === 'workspace' && this.renderWorkspaceExport(), h("button", { key: '3489affca39a901c2ef05a0698cdf51c0a7f6d1a', class: "export-primary-button", onClick: this.handleExport }, "Export")))));
68
+ return (h(Host, { key: 'efeea781325e672e3f4c1579a50da1c928dc88b5' }, h("kritzel-dialog", { key: '60e27233f484e70fd12bcc0f8a72b89d2f72d596', isOpen: this.isDialogOpen, dialogTitle: "Export", closable: true, contained: true, onDialogClose: this.closeDialog }, h("div", { key: 'e58e1d9804fdc8cb3d4c053ead641e2301b99ea5', class: "export-content" }, h("kritzel-pill-tabs", { key: '409f4c2d64f5477dc57c72a8e32ae0a12dfb7eda', tabs: this.tabs, value: this.activeTab, onValueChange: this.handleTabChange }), this.activeTab === 'viewport' && this.renderViewportExport(), this.activeTab === 'workspace' && this.renderWorkspaceExport(), h("button", { key: '7166aee26e0dbbdf6e7348428f7a740614948e5e', class: "export-primary-button", onClick: this.handleExport }, "Export")))));
69
69
  }
70
70
  static get is() { return "kritzel-export"; }
71
71
  static get encapsulation() { return "shadow"; }
@@ -44,7 +44,7 @@ export class KritzelLoginDialog {
44
44
  this.dialogClosed.emit();
45
45
  };
46
46
  render() {
47
- return (h(Host, { key: '1a664868b840030a773f61c2a0f4388dfb014675' }, h("kritzel-dialog", { key: '54844ffa772a211515c1ef3e6834ec45f7f3d035', dialogTitle: this.dialogTitle, isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small", contained: true }, h("div", { key: 'd9b981b6904c58bc39173ae37ee5c4c0ee329005', class: "login-content" }, this.subtitle && (h("p", { key: 'd4d200060507d2b8b755796d8313acdfc7e2f587', class: "login-subtitle" }, this.subtitle)), h("div", { key: '3dc1e3c070e62d026eb16ceb48eb63c94bc2bed0', class: "login-providers" }, this.providers.map(provider => (h("button", { key: provider.name, class: {
47
+ return (h(Host, { key: '8cac83db48fef2531f1669c3f601526b1e5cdefa' }, h("kritzel-dialog", { key: '34e7208c8c34550292c2b7503759bf103cfb49a6', dialogTitle: this.dialogTitle, isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small", contained: true }, h("div", { key: 'b0a0d8e0f38adc8d9b9545a02c5fc879f64a24de', class: "login-content" }, this.subtitle && (h("p", { key: 'a51b5f0a8b402aaf979d4bf47c6f9c3ba7e14bfe', class: "login-subtitle" }, this.subtitle)), h("div", { key: 'b6d8f8748eadf1462dd4161f089130b7ded31b59', class: "login-providers" }, this.providers.map(provider => (h("button", { key: provider.name, class: {
48
48
  'provider-button': true,
49
49
  'is-loading': this.loadingProvider === provider.name,
50
50
  'is-disabled': this.loadingProvider !== null && this.loadingProvider !== provider.name,
@@ -55,7 +55,7 @@ export class KritzelMoreMenu {
55
55
  this.closeMenu();
56
56
  };
57
57
  render() {
58
- return (h(Host, { key: '33d85e3b5ad51effdf2f61c8742dbe829ef43f15', class: { mobile: this.isTouchDevice }, style: { display: this.visible ? '' : 'none' } }, h("div", { key: '917ca25a14294f44a0428431a3ec08a84db2aff0', class: { 'more-menu-wrapper': true, visible: this.visible } }, h("button", { key: '61e145de48ac7aced1fcc03dde5d5d14f4448167', class: "more-menu-button", "data-testid": "more-menu-button", onClick: this.toggleMenu, "aria-label": "More options" }, h("kritzel-icon", { key: 'ba13d2117b28658c518c9721f348329d677683f9', name: this.icon, size: this.iconSize })), h("kritzel-portal", { key: '48d757891102ec2925366e0ca0542a6e75f2621f', anchor: this.menuAnchor, offsetY: this.offsetY, onClose: this.closeMenu }, h("kritzel-menu", { key: 'f04040f7122507642652839be4f75fbb157de20d', items: this.visibleItems, onItemSelect: this.handleMenuItemSelect })))));
58
+ return (h(Host, { key: '93bee9fc14d532a74f1b077098fb0a470655d2fe', class: { mobile: this.isTouchDevice }, style: { display: this.visible ? '' : 'none' } }, h("div", { key: '1783013acb533de9580698f29a7c8ae212b583fc', class: { 'more-menu-wrapper': true, visible: this.visible } }, h("button", { key: '8dc2f098377e78db0bf6efc05daaf02496cef527', class: "more-menu-button", "data-testid": "more-menu-button", onClick: this.toggleMenu, "aria-label": "More options" }, h("kritzel-icon", { key: '876a229226b0f79f1d5ef5d0b7793f362b884923', name: this.icon, size: this.iconSize })), h("kritzel-portal", { key: '57f7a69408b00c1bb9e5a08d22e224c6e6bcdea4', anchor: this.menuAnchor, offsetY: this.offsetY, onClose: this.closeMenu }, h("kritzel-menu", { key: '73a2aacd1b7c0ec79d7fa1695fbc02b1a0bde1b5', items: this.visibleItems, onItemSelect: this.handleMenuItemSelect })))));
59
59
  }
60
60
  static get is() { return "kritzel-more-menu"; }
61
61
  static get encapsulation() { return "shadow"; }
@@ -24,6 +24,7 @@ const SETTINGS_CATEGORIES = [
24
24
  export class KritzelSettings {
25
25
  host;
26
26
  /** Keyboard shortcuts to display in the settings dialog */
27
+ availableThemes = ['light', 'dark'];
27
28
  shortcuts = [];
28
29
  /** Current settings values. Used to initialize and sync the component's internal state. */
29
30
  settings;
@@ -37,7 +38,7 @@ export class KritzelSettings {
37
38
  scaleMin = DEFAULT_SCALE_MIN;
38
39
  scaleMax = DEFAULT_SCALE_MAX;
39
40
  lockDrawingScale = DEFAULT_LOCK_DRAWING_SCALE;
40
- currentTheme = 'light';
41
+ theme = 'light';
41
42
  viewportBoundaryLeft = DEFAULT_VIEWPORT_BOUNDARY_LEFT;
42
43
  viewportBoundaryRight = DEFAULT_VIEWPORT_BOUNDARY_RIGHT;
43
44
  viewportBoundaryTop = DEFAULT_VIEWPORT_BOUNDARY_TOP;
@@ -60,8 +61,8 @@ export class KritzelSettings {
60
61
  if (typeof settings.lockDrawingScale === 'boolean') {
61
62
  this.lockDrawingScale = settings.lockDrawingScale;
62
63
  }
63
- if (settings.theme === 'light' || settings.theme === 'dark') {
64
- this.currentTheme = settings.theme;
64
+ if (typeof settings.theme === 'string') {
65
+ this.theme = settings.theme;
65
66
  }
66
67
  if (typeof settings.viewportBoundaryLeft === 'number') {
67
68
  this.viewportBoundaryLeft = settings.viewportBoundaryLeft;
@@ -84,7 +85,7 @@ export class KritzelSettings {
84
85
  scaleMin: this.scaleMin,
85
86
  scaleMax: this.scaleMax,
86
87
  lockDrawingScale: this.lockDrawingScale,
87
- theme: this.currentTheme,
88
+ theme: this.theme,
88
89
  viewportBoundaryLeft: this.viewportBoundaryLeft,
89
90
  viewportBoundaryRight: this.viewportBoundaryRight,
90
91
  viewportBoundaryTop: this.viewportBoundaryTop,
@@ -106,7 +107,7 @@ export class KritzelSettings {
106
107
  this.emitSettings();
107
108
  };
108
109
  handleThemeChange = (event) => {
109
- this.currentTheme = event.detail ? 'dark' : 'light';
110
+ this.theme = event.detail;
110
111
  this.emitSettings();
111
112
  };
112
113
  handleViewportBoundaryLeftChange = (event) => {
@@ -167,7 +168,7 @@ export class KritzelSettings {
167
168
  renderCategoryContent() {
168
169
  switch (this.selectedCategoryId) {
169
170
  case 'general':
170
- return (h("div", { class: "settings-content" }, h("h3", null, "General Settings"), h("div", { class: "settings-group" }, h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Dark Mode"), h("p", { class: "settings-description" }, "Toggle between light and dark color themes for the editor interface."), h("kritzel-slide-toggle", { checked: this.currentTheme === 'dark', label: "Dark Mode", onCheckedChange: this.handleThemeChange })), h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Lock Drawing Scale"), h("p", { class: "settings-description" }, "When enabled, drawn objects maintain a fixed visual size regardless of the current zoom level."), h("kritzel-slide-toggle", { checked: this.lockDrawingScale, label: "Lock Drawing Scale", onCheckedChange: this.handleLockDrawingScaleChange })))));
171
+ return (h("div", { class: "settings-content" }, h("h3", null, "General Settings"), h("div", { class: "settings-group" }, h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Theme"), h("p", { class: "settings-description" }, "Select a registered color theme for the editor interface."), h("kritzel-dropdown", { options: this.availableThemes.map(t => ({ value: t, label: t })), value: this.theme, onValueChanged: this.handleThemeChange })), h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Lock Drawing Scale"), h("p", { class: "settings-description" }, "When enabled, drawn objects maintain a fixed visual size regardless of the current zoom level."), h("kritzel-slide-toggle", { checked: this.lockDrawingScale, label: "Lock Drawing Scale", onCheckedChange: this.handleLockDrawingScaleChange })))));
171
172
  case 'viewport':
172
173
  return (h("div", { class: "settings-content" }, h("h3", null, "Viewport Settings"), h("div", { class: "settings-group" }, h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Minimum Zoom Level"), h("p", { class: "settings-description" }, "Sets the minimum zoom level. Lower values allow zooming out further to see more of the canvas."), h("kritzel-numeric-input", { value: this.scaleMin, min: 0.0001, max: 1, step: 0.0001, onValueChange: this.handleScaleMinChange })), h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Maximum Zoom Level"), h("p", { class: "settings-description" }, "Sets the maximum zoom level. Higher values allow zooming in closer for detailed work."), h("kritzel-numeric-input", { value: this.scaleMax, min: 1, max: 1000, step: 1, onValueChange: this.handleScaleMaxChange })), h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Viewport Boundary Left"), h("p", { class: "settings-description" }, "Left boundary in world coordinates. Set to limit how far left the viewport can pan."), h("kritzel-numeric-input", { value: this.viewportBoundaryLeft, step: 100, placeholder: "Infinite", onValueChange: this.handleViewportBoundaryLeftChange })), h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Viewport Boundary Right"), h("p", { class: "settings-description" }, "Right boundary in world coordinates. Set to limit how far right the viewport can pan."), h("kritzel-numeric-input", { value: this.viewportBoundaryRight, step: 100, placeholder: "Infinite", onValueChange: this.handleViewportBoundaryRightChange })), h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Viewport Boundary Top"), h("p", { class: "settings-description" }, "Top boundary in world coordinates. Set to limit how far up the viewport can pan."), h("kritzel-numeric-input", { value: this.viewportBoundaryTop, step: 100, placeholder: "Infinite", onValueChange: this.handleViewportBoundaryTopChange })), h("div", { class: "settings-item" }, h("label", { class: "settings-label" }, "Viewport Boundary Bottom"), h("p", { class: "settings-description" }, "Bottom boundary in world coordinates. Set to limit how far down the viewport can pan."), h("kritzel-numeric-input", { value: this.viewportBoundaryBottom, step: 100, placeholder: "Infinite", onValueChange: this.handleViewportBoundaryBottomChange })))));
173
174
  case 'shortcuts':
@@ -181,7 +182,7 @@ export class KritzelSettings {
181
182
  }
182
183
  }
183
184
  render() {
184
- return (h(Host, { key: 'e86192a8ca49f8618d58ede4d04d321ea238d7d4' }, h("kritzel-dialog", { key: '23a47a8cd9281794bfd2aec7edd6a4ef4b931550', isOpen: this.isDialogOpen, dialogTitle: "Settings", size: "large", contained: true, onDialogClose: this.closeDialog }, h("kritzel-master-detail", { key: '007c8a1c04bd0d692b55d88988b0f8874f9242a4', items: SETTINGS_CATEGORIES, selectedItemId: this.selectedCategoryId, onItemSelect: this.handleCategorySelect }, this.renderCategoryContent()))));
185
+ return (h(Host, { key: '46c6792ae9cdd932d3dc71526862c9281c0cefc1' }, h("kritzel-dialog", { key: '1cd288cdf8b26bea378665c54bfc14577597fe49', isOpen: this.isDialogOpen, dialogTitle: "Settings", size: "large", contained: true, onDialogClose: this.closeDialog }, h("kritzel-master-detail", { key: '4d07e94ebb09035807356bab4bc7eaca57c36c6c', items: SETTINGS_CATEGORIES, selectedItemId: this.selectedCategoryId, onItemSelect: this.handleCategorySelect }, this.renderCategoryContent()))));
185
186
  }
186
187
  static get is() { return "kritzel-settings"; }
187
188
  static get encapsulation() { return "shadow"; }
@@ -197,6 +198,24 @@ export class KritzelSettings {
197
198
  }
198
199
  static get properties() {
199
200
  return {
201
+ "availableThemes": {
202
+ "type": "unknown",
203
+ "mutable": false,
204
+ "complexType": {
205
+ "original": "string[]",
206
+ "resolved": "string[]",
207
+ "references": {}
208
+ },
209
+ "required": false,
210
+ "optional": false,
211
+ "docs": {
212
+ "tags": [],
213
+ "text": "Keyboard shortcuts to display in the settings dialog"
214
+ },
215
+ "getter": false,
216
+ "setter": false,
217
+ "defaultValue": "['light', 'dark']"
218
+ },
200
219
  "shortcuts": {
201
220
  "type": "unknown",
202
221
  "mutable": false,
@@ -220,7 +239,7 @@ export class KritzelSettings {
220
239
  "optional": false,
221
240
  "docs": {
222
241
  "tags": [],
223
- "text": "Keyboard shortcuts to display in the settings dialog"
242
+ "text": ""
224
243
  },
225
244
  "getter": false,
226
245
  "setter": false,
@@ -263,7 +282,7 @@ export class KritzelSettings {
263
282
  "scaleMin": {},
264
283
  "scaleMax": {},
265
284
  "lockDrawingScale": {},
266
- "currentTheme": {},
285
+ "theme": {},
267
286
  "viewportBoundaryLeft": {},
268
287
  "viewportBoundaryRight": {},
269
288
  "viewportBoundaryTop": {},
@@ -84,9 +84,9 @@ export class KritzelShareDialog {
84
84
  this.dialogClosed.emit();
85
85
  };
86
86
  render() {
87
- return (h(Host, { key: 'bd58f146337b3eca96ca34408a3d30621f01765a' }, h("kritzel-dialog", { key: '0575ac82e19d07cf909556cae2ec433e0057fd5b', dialogTitle: "Share Workspace", size: "small", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, contained: true }, h("div", { key: 'c51d207e31255f45724103bfecbe858f13a721e6', class: "share-content" }, h("div", { key: 'ca6cb7721b9ba834c133b2cb953b208475e34fb5', class: "share-section" }, h("div", { key: '2c76845c903cc1c18cc26b9111d608e732ed12a5', class: "share-row" }, h("div", { key: '7700533f54372bc81d8d795414318a6bf0e93c47', class: "share-label-group" }, h("label", { key: 'a1d80009cb09cfe35bce35ce1151bf0754b052c1', class: "share-label" }, "Link sharing"), h("p", { key: '10c1963e95e658c7fb86174f1dba7565ce40d5a6', class: "share-description" }, this.internalIsPublic
87
+ return (h(Host, { key: 'a104c14b2492d97f3ada98c9eaaa845d63074063' }, h("kritzel-dialog", { key: '1b12b27504153e54aeb0cb4e6b1030a0d43b9735', dialogTitle: "Share Workspace", size: "small", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, contained: true }, h("div", { key: '652f23e37876be356beb6f93abf5930e91d82cea', class: "share-content" }, h("div", { key: 'aaf336f2ac86fe23cac79cef920a9d67681046e2', class: "share-section" }, h("div", { key: '8075a7b3fff47c4b924d3b2d92b1377641920939', class: "share-row" }, h("div", { key: '41e98a74a5d4aede50fd75a7de62cbef9b5a5a31', class: "share-label-group" }, h("label", { key: '8a4f53e13d5a81497dd31316a49971c7245d82a2', class: "share-label" }, "Link sharing"), h("p", { key: '907a59d50e595734f03067f70830cf96defdf8d8', class: "share-description" }, this.internalIsPublic
88
88
  ? 'Anyone with the link can access this workspace.'
89
- : 'Link sharing is disabled. Only you can access this workspace.')), h("kritzel-slide-toggle", { key: 'ec62a5ece12be0cea18a16c5d41db0a992309174', checked: this.internalIsPublic, onCheckedChange: this.handleToggleChange, label: "Enable link sharing" }))), this.internalIsPublic && (h("div", { key: '5e826d4c8c37792ba3a74a0189ad313a8ab482e2', class: "share-section" }, h("div", { key: 'f8e35cda32cb34ab21f56335aa27503fd6fe98c4', class: "share-url-container" }, h("input", { key: '47feb20a1843e1d3d8f7d146d71574b187002e8d', type: "text", class: "share-url-input", value: this.getShareUrl(), readOnly: true, onClick: (e) => e.target.select() }), h("button", { key: '052f56f35d057430cbc8fd03da5bef574b173791', class: { 'copy-button': true, 'copy-success': this.copySuccess }, onClick: this.handleCopyUrl, title: this.copySuccess ? 'Copied!' : 'Copy link' }, h("kritzel-icon", { key: '4e1de478f837a352185be2a06e15796dc1fb2f5e', name: this.copySuccess ? 'check' : 'copy', size: 18 })))))))));
89
+ : 'Link sharing is disabled. Only you can access this workspace.')), h("kritzel-slide-toggle", { key: '0d75cfeeb63c33d20380ffe9a7e4c27148548ef9', checked: this.internalIsPublic, onCheckedChange: this.handleToggleChange, label: "Enable link sharing" }))), this.internalIsPublic && (h("div", { key: 'a8a10c74fd326c5097e4a5f0ee165602c3606ade', class: "share-section" }, h("div", { key: '6261a9fc6cb2be2a50856fb8a990b9da3fee84bf', class: "share-url-container" }, h("input", { key: '26ee72eebfee88d06a50c338cccc9af296c8ba4c', type: "text", class: "share-url-input", value: this.getShareUrl(), readOnly: true, onClick: (e) => e.target.select() }), h("button", { key: '4b1ec06fa27c95d9d0bb93f8cbb851a02fdd52cc', class: { 'copy-button': true, 'copy-success': this.copySuccess }, onClick: this.handleCopyUrl, title: this.copySuccess ? 'Copied!' : 'Copy link' }, h("kritzel-icon", { key: 'd9eea56b3523fcc3557d9868f3da7f28449a9447', name: this.copySuccess ? 'check' : 'copy', size: 18 })))))))));
90
90
  }
91
91
  static get is() { return "kritzel-share-dialog"; }
92
92
  static get encapsulation() { return "shadow"; }
@@ -280,14 +280,14 @@ export class KritzelToolConfig {
280
280
  "type": "string",
281
281
  "mutable": false,
282
282
  "complexType": {
283
- "original": "ThemeMode",
284
- "resolved": "\"dark\" | \"light\"",
283
+ "original": "ThemeName",
284
+ "resolved": "\"dark\" | \"light\" | string & {}",
285
285
  "references": {
286
- "ThemeMode": {
286
+ "ThemeName": {
287
287
  "location": "import",
288
- "path": "../../../constants/color-palette.constants",
289
- "id": "src/constants/color-palette.constants.ts::ThemeMode",
290
- "referenceLocation": "ThemeMode"
288
+ "path": "../../../interfaces/theme.interface",
289
+ "id": "src/interfaces/theme.interface.ts::ThemeName",
290
+ "referenceLocation": "ThemeName"
291
291
  }
292
292
  }
293
293
  },
@@ -17,7 +17,7 @@ export class KritzelUtilityPanel {
17
17
  this.redo.emit();
18
18
  }
19
19
  render() {
20
- return (h(Host, { key: 'f800ea5843cf73ae132b56396ad05d664043f789' }, h("button", { key: '8f2c35b9b774ba5662ad584ebaa3e98a21e2d4e5', class: "utility-button", "data-testid": "utility-undo", disabled: !this.undoState?.canUndo, onClick: event => this.handleUndo(event), "aria-label": "Undo" }, h("kritzel-icon", { key: 'ffba256fea2b2ba7c767601a2051c940e5865fd5', name: "undo" })), h("button", { key: '261a7759ec1e25000ed76d2cf5aaef908cea886c', class: "utility-button", "data-testid": "utility-redo", disabled: !this.undoState?.canRedo, onClick: event => this.handleRedo(event), "aria-label": "Redo" }, h("kritzel-icon", { key: '7483a8c23a1dcd77dc0bfdb3cc39c4998e48b193', name: "redo" })), h("div", { key: '7450540c285a6cf746d52b31eb2e7f88ec30c1ec', class: "utility-separator" }), h("button", { key: '62fa097474ede7de6018a76ec492a9d72bb0cb11', class: "utility-button", "data-testid": "utility-delete", onClick: () => this.delete.emit(), "aria-label": "Delete selected items" }, h("kritzel-icon", { key: 'a1cea0b8a5524fe5877ae2ce7939bcb5a66b4dcd', name: "delete" }))));
20
+ return (h(Host, { key: 'b49f6db6c0e574dc8a5a733c749ecda6f24f9d25' }, h("button", { key: 'e6306e54c8f660c3e92d032527fad1ea45ca0cf8', class: "utility-button", "data-testid": "utility-undo", disabled: !this.undoState?.canUndo, onClick: event => this.handleUndo(event), "aria-label": "Undo" }, h("kritzel-icon", { key: '5bb1293049a1e3004504289d92ccc79958786f3f', name: "undo" })), h("button", { key: '8102b0403d7f328ce4bfeb79767d5bd99d879013', class: "utility-button", "data-testid": "utility-redo", disabled: !this.undoState?.canRedo, onClick: event => this.handleRedo(event), "aria-label": "Redo" }, h("kritzel-icon", { key: '5db3047bec5d8ab695a2dc67780a5dbecbae64d2', name: "redo" })), h("div", { key: 'e894d9f2aaa2cad7aa980d3b839eca05a8d9c9df', class: "utility-separator" }), h("button", { key: 'f0a7de5ab91f82a2e5e8df75cc1903ec647abdac', class: "utility-button", "data-testid": "utility-delete", onClick: () => this.delete.emit(), "aria-label": "Delete selected items" }, h("kritzel-icon", { key: '5b146375394299bae946a95545c3c42c2bf36766', name: "delete" }))));
21
21
  }
22
22
  static get is() { return "kritzel-utility-panel"; }
23
23
  static get encapsulation() { return "shadow"; }
@@ -2,7 +2,10 @@
2
2
  * Helper function to resolve a color value for a specific theme
3
3
  */
4
4
  export function resolveColor(color, theme) {
5
- return color[theme];
5
+ if (theme in color && color[theme]) {
6
+ return color[theme];
7
+ }
8
+ return color[theme === 'dark' ? 'dark' : 'light'];
6
9
  }
7
10
  /**
8
11
  * Default color palette shared across all tool configurations.
@@ -3,4 +3,4 @@
3
3
  * This file is auto-generated by the version bump scripts.
4
4
  * Do not modify manually.
5
5
  */
6
- export const KRITZEL_VERSION = '0.3.8';
6
+ export const KRITZEL_VERSION = '0.3.10';
@@ -7,6 +7,7 @@
7
7
  * DO NOT use this file to export your components. Instead, use the recommended approaches
8
8
  * to consume components of this package as outlined in the `README.md`.
9
9
  */
10
+ export * from './classes/objects/base-object.class';
10
11
  export * from './classes/objects/text.class';
11
12
  export * from './classes/objects/path.class';
12
13
  export * from './classes/objects/image.class';
@@ -35,6 +36,7 @@ export * from './classes/managers/anchor.manager';
35
36
  export * from './classes/managers/theme.manager';
36
37
  export * from './interfaces/toolbar-control.interface';
37
38
  export * from './interfaces/menu-item.interface';
39
+ export * from './interfaces/object-change-event.interface';
38
40
  export * from './interfaces/sync-provider.interface';
39
41
  export * from './interfaces/sync-config.interface';
40
42
  export * from './interfaces/asset.interface';
@@ -37,7 +37,10 @@ export const darkTheme = {
37
37
  },
38
38
  selection: {
39
39
  borderColor: '#0A84FF',
40
+ boxBackgroundColor: 'rgba(10, 132, 255, 0.2)',
41
+ boxBorderColor: 'rgba(10, 132, 255, 0.5)',
40
42
  handleColor: '#1a1a1a',
43
+ handleStrokeColor: '#0A84FF'
41
44
  },
42
45
  checkerboard: {
43
46
  colorDark: '#4a4a4a',
@@ -75,6 +78,7 @@ export const darkTheme = {
75
78
  controlHoverBackgroundColor: 'hsl(0, 0%, 100%, 8%)',
76
79
  controlSelectedBackgroundColor: '#0A84FF',
77
80
  controlSelectedColor: '#ffffff',
81
+ separatorColor: '#3a3a3a',
78
82
  },
79
83
  currentUserDialog: {
80
84
  emailColor: '#999999',