kritzel-stencil 0.4.0 → 0.4.1

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 (81) hide show
  1. package/dist/cjs/index.cjs.js +2 -1
  2. package/dist/cjs/kritzel-active-users_47.cjs.entry.js +249 -68
  3. package/dist/cjs/loader.cjs.js +1 -1
  4. package/dist/cjs/{schema.constants-fuAsi56H.js → schema.constants-IAt9YXDZ.js} +273 -32
  5. package/dist/cjs/stencil.cjs.js +1 -1
  6. package/dist/collection/classes/handlers/context-menu.handler.js +2 -3
  7. package/dist/collection/classes/managers/clipboard.manager.js +9 -3
  8. package/dist/collection/classes/objects/base-object.class.js +0 -1
  9. package/dist/collection/classes/objects/text.class.js +2 -1
  10. package/dist/collection/classes/registries/font-registry.class.js +161 -0
  11. package/dist/collection/classes/structures/object-map.structure.js +9 -0
  12. package/dist/collection/classes/tools/image-tool.class.js +19 -3
  13. package/dist/collection/classes/tools/text-tool.class.js +2 -0
  14. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js +22 -7
  15. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +138 -9
  16. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +58 -13
  17. package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.js +31 -22
  18. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +10 -11
  19. package/dist/collection/components/ui/kritzel-tool-config/kritzel-tool-config.js +40 -5
  20. package/dist/collection/configs/default-text-tool.config.js +2 -0
  21. package/dist/collection/constants/text-tool-fonts.constants.js +18 -0
  22. package/dist/collection/constants/version.js +1 -1
  23. package/dist/collection/helpers/image-file.helper.js +6 -0
  24. package/dist/collection/index.js +2 -0
  25. package/dist/collection/interfaces/font.interface.js +1 -0
  26. package/dist/components/index.js +1 -1
  27. package/dist/components/kritzel-awareness-cursors.js +1 -1
  28. package/dist/components/kritzel-controls.js +1 -1
  29. package/dist/components/kritzel-cursor-trail.js +1 -1
  30. package/dist/components/kritzel-editor.js +1 -1
  31. package/dist/components/kritzel-engine.js +1 -1
  32. package/dist/components/kritzel-font-family.js +1 -1
  33. package/dist/components/kritzel-settings.js +1 -1
  34. package/dist/components/kritzel-tool-config.js +1 -1
  35. package/dist/components/p-BH4oJ7yX.js +9 -0
  36. package/dist/components/p-C38P-Vd0.js +1 -0
  37. package/dist/components/p-Cm33td2O.js +1 -0
  38. package/dist/components/{p-zhjb97WN.js → p-Co165PAZ.js} +1 -1
  39. package/dist/components/p-DJkKlUyZ.js +1 -0
  40. package/dist/components/p-JYj3Facr.js +1 -0
  41. package/dist/components/p-Ka7OWbnk.js +1 -0
  42. package/dist/components/p-djJSnjAX.js +1 -0
  43. package/dist/esm/index.js +2 -2
  44. package/dist/esm/kritzel-active-users_47.entry.js +249 -68
  45. package/dist/esm/loader.js +1 -1
  46. package/dist/esm/{schema.constants-Cv5-yM39.js → schema.constants-rIkxAcpB.js} +271 -33
  47. package/dist/esm/stencil.js +1 -1
  48. package/dist/stencil/index.esm.js +1 -1
  49. package/dist/stencil/p-966ca4b5.entry.js +9 -0
  50. package/dist/stencil/p-rIkxAcpB.js +1 -0
  51. package/dist/stencil/stencil.esm.js +1 -1
  52. package/dist/types/classes/managers/clipboard.manager.d.ts +1 -0
  53. package/dist/types/classes/objects/base-object.class.d.ts +0 -1
  54. package/dist/types/classes/registries/font-registry.class.d.ts +19 -0
  55. package/dist/types/classes/structures/object-map.structure.d.ts +2 -2
  56. package/dist/types/classes/tools/image-tool.class.d.ts +9 -2
  57. package/dist/types/classes/tools/text-tool.class.d.ts +3 -0
  58. package/dist/types/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.d.ts +2 -0
  59. package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +22 -0
  60. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +10 -0
  61. package/dist/types/components/shared/kritzel-font-family/kritzel-font-family.d.ts +4 -0
  62. package/dist/types/components/ui/kritzel-tool-config/kritzel-tool-config.d.ts +1 -0
  63. package/dist/types/components.d.ts +20 -2
  64. package/dist/types/constants/text-tool-fonts.constants.d.ts +3 -0
  65. package/dist/types/constants/version.d.ts +1 -1
  66. package/dist/types/helpers/image-file.helper.d.ts +3 -0
  67. package/dist/types/index.d.ts +2 -0
  68. package/dist/types/interfaces/font.interface.d.ts +15 -0
  69. package/dist/types/interfaces/toolbar-control.interface.d.ts +2 -0
  70. package/package.json +1 -1
  71. package/dist/components/p-B4b6TDxp.js +0 -1
  72. package/dist/components/p-C2ocjA6D.js +0 -1
  73. package/dist/components/p-CwbZCXKh.js +0 -1
  74. package/dist/components/p-D2q_wTyG.js +0 -1
  75. package/dist/components/p-D8NvNMcU.js +0 -1
  76. package/dist/components/p-D8W6LE-c.js +0 -1
  77. package/dist/components/p-G9G9y5VE.js +0 -9
  78. package/dist/components/p-JhOYwUOj.js +0 -1
  79. package/dist/components/p-UEf2EN23.js +0 -1
  80. package/dist/stencil/p-2c489187.entry.js +0 -9
  81. package/dist/stencil/p-Cv5-yM39.js +0 -1
@@ -24,6 +24,8 @@ import { darkTheme } from "../../../themes/dark-theme";
24
24
  import { KritzelSelectionGroup } from "../../../classes/objects/selection-group.class";
25
25
  import { KritzelSelectionBox } from "../../../classes/objects/selection-box.class";
26
26
  import { ObjectHelper } from "../../../helpers/object.helper";
27
+ import { KritzelFontRegistry } from "../../../classes/registries/font-registry.class";
28
+ import { resolveTextToolAvailableFonts } from "../../../constants/text-tool-fonts.constants";
27
29
  export class KritzelEditor {
28
30
  host;
29
31
  scaleMax = ABSOLUTE_SCALE_MAX;
@@ -184,6 +186,11 @@ export class KritzelEditor {
184
186
  /** The locale used to resolve terms missing from the active locale. */
185
187
  fallbackLocale = 'en';
186
188
  customSvgIcons = {};
189
+ /**
190
+ * Registers fonts for runtime usage (for example custom object rendering).
191
+ * Text-tool config tooltip options are controlled via controls[].config.availableFonts.
192
+ */
193
+ customFonts = {};
187
194
  isPanningEnabled = true;
188
195
  isZoomingEnabled = true;
189
196
  isControlsVisible = true;
@@ -236,11 +243,14 @@ export class KritzelEditor {
236
243
  isEditorVisible = false;
237
244
  activeNotification;
238
245
  isNotificationDismissing = false;
246
+ normalizedControls = [];
239
247
  notificationDismissTimeout;
240
248
  notificationDismissAnimationTimeout;
241
249
  notificationDisplayDurationMs = 5000;
242
250
  notificationDismissAnimationDurationMs = 180;
243
251
  isNotificationHovered = false;
252
+ customFontsLoadVersion = 0;
253
+ customFontsReady = Promise.resolve();
244
254
  onIsEngineReady(newValue) {
245
255
  if (newValue && this.isControlsReady) {
246
256
  this.checkIsReady();
@@ -270,6 +280,12 @@ export class KritzelEditor {
270
280
  onActiveWorkspaceChange() {
271
281
  this.updateCurrentIsPublic();
272
282
  }
283
+ onCustomFontsChange(newValue) {
284
+ void this.registerCustomFonts(newValue);
285
+ }
286
+ onControlsChange(newValue) {
287
+ this.normalizedControls = this.normalizeControls(newValue);
288
+ }
273
289
  onActiveWorkspaceIdChange(newId) {
274
290
  // If no ID is provided, do nothing (allows other logic to control activeWorkspace)
275
291
  if (!newId) {
@@ -543,9 +559,27 @@ export class KritzelEditor {
543
559
  loginDialogRef;
544
560
  currentUserDialogRef;
545
561
  componentWillLoad() {
562
+ this.normalizedControls = this.normalizeControls(this.controls);
546
563
  this.loadSettingsFromStorage();
547
564
  this.applyTheme();
548
565
  }
566
+ isTextToolConfig(config) {
567
+ return !!config && 'fontFamily' in config && 'size' in config && 'color' in config;
568
+ }
569
+ normalizeControls(controls) {
570
+ return controls.map(control => {
571
+ if (!this.isTextToolConfig(control.config)) {
572
+ return control;
573
+ }
574
+ return {
575
+ ...control,
576
+ config: {
577
+ ...control.config,
578
+ availableFonts: resolveTextToolAvailableFonts(control.config.availableFonts),
579
+ },
580
+ };
581
+ });
582
+ }
549
583
  applyTheme() {
550
584
  const themeObj = this.resolveThemeObject();
551
585
  ThemeHelper.applyThemeToElement(this.host, themeObj);
@@ -556,6 +590,7 @@ export class KritzelEditor {
556
590
  }
557
591
  componentDidLoad() {
558
592
  this.registerCustomSvgIcons();
593
+ this.registerCustomFonts();
559
594
  this.listenForMobileKeyboard();
560
595
  this.setOsSpecificCssVariables();
561
596
  }
@@ -568,6 +603,7 @@ export class KritzelEditor {
568
603
  await customElements.whenDefined('kritzel-workspace-manager');
569
604
  await customElements.whenDefined('kritzel-controls');
570
605
  await customElements.whenDefined('kritzel-engine');
606
+ await this.waitForCustomFontsReady();
571
607
  if (!this.isEngineReady || !this.isControlsReady || !this.isWorkspaceManagerReady || !this.activeWorkspace) {
572
608
  return;
573
609
  }
@@ -831,6 +867,15 @@ export class KritzelEditor {
831
867
  async registerLocales(locales) {
832
868
  await this.engineRef?.registerLocales(locales);
833
869
  }
870
+ /**
871
+ * Registers custom fonts for the editor runtime.
872
+ * @param fonts - Mapping from font keys to family names or font definitions.
873
+ * @remarks This no longer controls the text-tool config tooltip font options.
874
+ * Configure tooltip fonts via controls[].config.availableFonts.
875
+ */
876
+ async registerFonts(fonts) {
877
+ await this.registerCustomFonts(fonts);
878
+ }
834
879
  /**
835
880
  * Resolves a term key to its translated string for the active locale.
836
881
  * @param key - The term key to resolve.
@@ -908,6 +953,25 @@ export class KritzelEditor {
908
953
  KritzelIconRegistry.register(name, svg);
909
954
  }
910
955
  }
956
+ async registerCustomFonts(fonts = this.customFonts) {
957
+ KritzelFontRegistry.registerFonts(fonts);
958
+ const currentVersion = ++this.customFontsLoadVersion;
959
+ const loadPromise = KritzelFontRegistry.waitForFonts(fonts);
960
+ this.customFontsReady = loadPromise;
961
+ await loadPromise;
962
+ if (currentVersion !== this.customFontsLoadVersion) {
963
+ await this.waitForCustomFontsReady();
964
+ }
965
+ }
966
+ async waitForCustomFontsReady() {
967
+ while (true) {
968
+ const version = this.customFontsLoadVersion;
969
+ await this.customFontsReady;
970
+ if (version === this.customFontsLoadVersion) {
971
+ return;
972
+ }
973
+ }
974
+ }
911
975
  listenForMobileKeyboard() {
912
976
  KritzelKeyboardHelper.onKeyboardVisibleChanged(isOpen => {
913
977
  this.isVirtualKeyboardOpen = isOpen;
@@ -946,36 +1010,36 @@ export class KritzelEditor {
946
1010
  const isLoggedIn = this.isLoggedIn;
947
1011
  const shouldShowCurrentUser = isLoggedIn;
948
1012
  const shouldShowLoginButton = this.isReady && !!this.loginConfig && !isLoggedIn;
949
- return (h(Host, { key: '3128b38a04cd636abc1f2466f1ac1aa3dcea2262' }, h("div", { key: 'b1fbb051cb1144fd84f28cfea4ad824f92189dbd', class: "editor-content", style: {
1013
+ return (h(Host, { key: '00d652c74feac7b4917772146368125da6deeb1c' }, h("div", { key: 'f8fb86b45b0cdabb5e3ad52de6d6ef31a71a09e4', class: "editor-content", style: {
950
1014
  opacity: this.isEditorVisible ? '1' : '0',
951
1015
  visibility: this.isEditorVisible ? 'visible' : 'hidden',
952
1016
  transition: 'opacity 0.2s ease-in-out, visibility 0.2s ease-in-out',
953
- } }, h("div", { key: '2b896fa8bd00a7c68b74df7a9846e8c3e0f6efc4', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: '13244251aa8999d78d275eefe5583d592f3506fc', visible: this.isWorkspaceManagerVisible, workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, terms: this.resolvedTerms, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-back-to-content", { key: '7d3015918e4af7ee435aee8da37ddfa206cdfd66', visible: this.isBackToContentButtonVisible, text: this.resolvedTerms['backToContent.label'] ?? 'Back to content', onBackToContent: () => this.backToContent() })), this.activeNotification && (h("div", { key: '49f13f23698db9b804d139e0f35696be06569346', class: "top-center-notification-layer", role: "presentation" }, h("div", { key: '765aa6995398aca3c7738758bedb7d9a7c2f099a', class: { 'top-center-notification-container': true, 'is-dismissing': this.isNotificationDismissing }, role: "presentation" }, h("kritzel-notification-card", { key: 'fe0dfa30aa498a608f9167b01e0a2309dfa67162', notification: this.activeNotification, locale: this.locale, onDismiss: this.dismissNotification, onHoverChange: event => this.handleNotificationHoverChange(event) })))), h("kritzel-engine", { key: '5aeb7979faa8779653ff6a2930d288fd601cb0e9', ref: el => {
1017
+ } }, h("div", { key: '2378c776d8eecf2614567bed11c6e93fb338e302', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: 'ce41192540169694482d6f8cca60701bc4427438', visible: this.isWorkspaceManagerVisible, workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, terms: this.resolvedTerms, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-back-to-content", { key: 'ec269d3e9f589362d8301370778f0c5e4252eead', visible: this.isBackToContentButtonVisible, text: this.resolvedTerms['backToContent.label'] ?? 'Back to content', onBackToContent: () => this.backToContent() })), this.activeNotification && (h("div", { key: '847ed8149d556c934090424ff54b17b6721ccc76', class: "top-center-notification-layer", role: "presentation" }, h("div", { key: '61d8b8e91cda100ffa6620db26ba6dfb2232946a', class: { 'top-center-notification-container': true, 'is-dismissing': this.isNotificationDismissing }, role: "presentation" }, h("kritzel-notification-card", { key: 'b7e132773225ace38e447756110dd39e4661f5fc', notification: this.activeNotification, locale: this.locale, onDismiss: this.dismissNotification, onHoverChange: event => this.handleNotificationHoverChange(event) })))), h("kritzel-engine", { key: '3e4a6d0dbc015ec675fb21a59990c87e9eaa805f', ref: el => {
954
1018
  if (el) {
955
1019
  this.engineRef = el;
956
1020
  }
957
- }, workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, assetStorageConfig: this.assetStorageConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, isObjectDistanceFadingActive: this.isObjectDistanceFadingActive, scaleMin: this.scaleMin, cursorTarget: this.cursorTarget, isLoading: this.isLoading, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, isPanningEnabled: this.isPanningEnabled, isZoomingEnabled: this.isZoomingEnabled, theme: this.theme, themes: this.themes, licenseKey: this.licenseKey, locale: this.locale, locales: this.locales, fallbackLocale: this.fallbackLocale, debugInfo: this.debugInfo, globalContextMenuItems: this.globalContextMenuItems, objectContextMenuItems: this.objectContextMenuItems, onIsEngineReady: event => this.onEngineReady(event), onWorkspacesChange: event => this.handleWorkspacesChange(event), onActiveWorkspaceChange: event => this.handleActiveWorkspaceChange(event), onObjectsChange: event => this.handleObjectsChange(event), onObjectsAdded: event => this.handleObjectsAdded(event), onObjectsRemoved: event => this.handleObjectsRemoved(event), onObjectsUpdated: event => this.handleObjectsUpdated(event), onUndoStateChange: event => this.handleUndoStateChange(event), onObjectsInViewportChange: event => this.handleObjectsInViewportChange(event), onViewportChange: event => this.handleViewportChange(event), onAwarenessChange: event => this.handleAwarenessChange(event), onNotificationsChange: event => this.handleNotificationsChange(event) }), h("kritzel-controls", { key: 'eda4dc93fe663c8472638190216bea5bf64d7153', visible: this.isControlsVisible, class: { 'keyboard-open': this.isVirtualKeyboardOpen }, ref: el => {
1021
+ }, workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, assetStorageConfig: this.assetStorageConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, isObjectDistanceFadingActive: this.isObjectDistanceFadingActive, scaleMin: this.scaleMin, cursorTarget: this.cursorTarget, isLoading: this.isLoading, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, isPanningEnabled: this.isPanningEnabled, isZoomingEnabled: this.isZoomingEnabled, theme: this.theme, themes: this.themes, licenseKey: this.licenseKey, locale: this.locale, locales: this.locales, fallbackLocale: this.fallbackLocale, debugInfo: this.debugInfo, globalContextMenuItems: this.globalContextMenuItems, objectContextMenuItems: this.objectContextMenuItems, onIsEngineReady: event => this.onEngineReady(event), onWorkspacesChange: event => this.handleWorkspacesChange(event), onActiveWorkspaceChange: event => this.handleActiveWorkspaceChange(event), onObjectsChange: event => this.handleObjectsChange(event), onObjectsAdded: event => this.handleObjectsAdded(event), onObjectsRemoved: event => this.handleObjectsRemoved(event), onObjectsUpdated: event => this.handleObjectsUpdated(event), onUndoStateChange: event => this.handleUndoStateChange(event), onObjectsInViewportChange: event => this.handleObjectsInViewportChange(event), onViewportChange: event => this.handleViewportChange(event), onAwarenessChange: event => this.handleAwarenessChange(event), onNotificationsChange: event => this.handleNotificationsChange(event) }), h("kritzel-controls", { key: 'f84607a0a4d0762c1249400f85b7b8454d672c1e', visible: this.isControlsVisible, class: { 'keyboard-open': this.isVirtualKeyboardOpen }, ref: el => {
958
1022
  if (el) {
959
1023
  this.controlsRef = el;
960
1024
  }
961
- }, controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.theme, terms: this.resolvedTerms, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: 'ffdb548da8aac3498b1b3e9bfdc9a41f8c448675', class: "bottom-left-buttons" }, h("kritzel-zoom-panel", { key: 'af6f6a86212195fd3ee13bc33c16574e0a5236a6', visible: this.isZoomPanelVisible, disabled: !this.isZoomingEnabled, zoomPercent: this.currentZoomPercent, terms: this.resolvedTerms, onZoomIn: () => this.zoomIn(), onZoomOut: () => this.zoomOut() })), h("div", { key: '0870c0e47838fc99b4041718a431535827e199cf', class: "top-right-buttons" }, h("kritzel-settings", { key: 'b95beb43dbe1a9c99454fc4ff74f0630aca7ad83', ref: el => {
1025
+ }, controls: this.normalizedControls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.theme, terms: this.resolvedTerms, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: 'e2691e7c730195e197511cb1e4d0fe707677b5ff', class: "bottom-left-buttons" }, h("kritzel-zoom-panel", { key: 'd0ac6624eab49af78d4761c3ad91842834717aad', visible: this.isZoomPanelVisible, disabled: !this.isZoomingEnabled, zoomPercent: this.currentZoomPercent, terms: this.resolvedTerms, onZoomIn: () => this.zoomIn(), onZoomOut: () => this.zoomOut() })), h("div", { key: 'ad3d386ab7e8ad9fdfbb12754db005bc85fa28ce', class: "top-right-buttons" }, h("kritzel-settings", { key: '566024ac6f3d8f7f20238cc1f83882b39dbf698e', ref: el => {
962
1026
  if (el) {
963
1027
  this.settingsRef = el;
964
1028
  }
965
- }, shortcuts: this.shortcuts, availableThemes: this.themes && this.themes.length > 0 ? this.themes.map(t => t.name) : ['light', 'dark'], availableLocales: this.availableLocaleOptions, settings: this.currentSettingsConfig, terms: this.resolvedTerms, onSettingsChange: event => this.handleSettingsChange(event) }), h("kritzel-export", { key: 'ef6fea2715ff99226fde937185a693efc2a7dd75', ref: el => {
1029
+ }, shortcuts: this.shortcuts, availableThemes: this.themes && this.themes.length > 0 ? this.themes.map(t => t.name) : ['light', 'dark'], availableLocales: this.availableLocaleOptions, settings: this.currentSettingsConfig, terms: this.resolvedTerms, onSettingsChange: event => this.handleSettingsChange(event) }), h("kritzel-export", { key: '4a23ea0c6e070d424a90e9fc87de04e11cff7ade', ref: el => {
966
1030
  if (el) {
967
1031
  this.exportRef = el;
968
1032
  }
969
- }, workspaceName: this.activeWorkspace?.name || 'workspace', terms: this.resolvedTerms, onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), h("kritzel-active-users", { key: '872b7fa12d49410d1c370baed3386a34f89d3e2a', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: 'da19825b4fba6a516803f8330cdfdfac6b6c9db1', user: this.user, terms: this.resolvedTerms, onClick: () => this.currentUserDialogRef?.open() }), shouldShowCurrentUser && (h("kritzel-current-user-dialog", { key: '9336ce611286d3ecf9318317ab4bedb8c3f79f05', ref: el => {
1033
+ }, workspaceName: this.activeWorkspace?.name || 'workspace', terms: this.resolvedTerms, onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), h("kritzel-active-users", { key: 'feef074b52e552fa2ef7e8258002571aca4c5abd', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: '844c722545363eaf7eec69037a835eae943afeb9', user: this.user, terms: this.resolvedTerms, onClick: () => this.currentUserDialogRef?.open() }), shouldShowCurrentUser && (h("kritzel-current-user-dialog", { key: '8363c1d3230d111cf1ace0ccf805669e592bb533', ref: el => {
970
1034
  if (el) {
971
1035
  this.currentUserDialogRef = el;
972
1036
  this.currentUserDialogRef.addEventListener('logoutRequest', this.handleCurrentUserLogout);
973
1037
  }
974
- }, user: this.user, terms: this.resolvedTerms })), shouldShowLoginButton && (h("kritzel-button", { key: 'a9e38ad3e13c785f6a4223ebde95f60451c6515f', onButtonClick: () => this.loginDialogRef?.open() }, this.resolvedTerms['login.dialogTitle'] ?? 'Sign in')), h("kritzel-more-menu", { key: 'b8f63a63ae4e75a70d0069410170af902f2a362b', items: this.moreMenuItems, visible: this.isMoreMenuVisible, terms: this.resolvedTerms }), h("kritzel-share-dialog", { key: '7a923fd51e8d2bd1e2833e0941622efadd55f881', ref: el => {
1038
+ }, user: this.user, terms: this.resolvedTerms })), shouldShowLoginButton && (h("kritzel-button", { key: '68ddf8c3aabc7634b4c1d88f3d2d584fa1f1b150', onButtonClick: () => this.loginDialogRef?.open() }, this.resolvedTerms['login.dialogTitle'] ?? 'Sign in')), h("kritzel-more-menu", { key: '39b938f5792f7a36bbf21ed88a1a5c7717a785a6', items: this.moreMenuItems, visible: this.isMoreMenuVisible, terms: this.resolvedTerms }), h("kritzel-share-dialog", { key: '61ed918499760d874d5d2ec140d3cf59ce94520b', ref: el => {
975
1039
  if (el) {
976
1040
  this.shareDialogRef = el;
977
1041
  }
978
- }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: 'ac54c699a94bb2657a6817c37d6220b75d931cd3', ref: el => {
1042
+ }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '5f6528a57884bd1494e45755609e710f69041a4d', ref: el => {
979
1043
  if (el) {
980
1044
  this.loginDialogRef = el;
981
1045
  }
@@ -1453,6 +1517,31 @@ export class KritzelEditor {
1453
1517
  "setter": false,
1454
1518
  "defaultValue": "{}"
1455
1519
  },
1520
+ "customFonts": {
1521
+ "type": "unknown",
1522
+ "mutable": false,
1523
+ "complexType": {
1524
+ "original": "KritzelFontMap",
1525
+ "resolved": "{ [x: string]: KritzelFontRegistration; }",
1526
+ "references": {
1527
+ "KritzelFontMap": {
1528
+ "location": "import",
1529
+ "path": "../../../interfaces/font.interface",
1530
+ "id": "src/interfaces/font.interface.ts::KritzelFontMap",
1531
+ "referenceLocation": "KritzelFontMap"
1532
+ }
1533
+ }
1534
+ },
1535
+ "required": false,
1536
+ "optional": false,
1537
+ "docs": {
1538
+ "tags": [],
1539
+ "text": "Registers fonts for runtime usage (for example custom object rendering).\nText-tool config tooltip options are controlled via controls[].config.availableFonts."
1540
+ },
1541
+ "getter": false,
1542
+ "setter": false,
1543
+ "defaultValue": "{}"
1544
+ },
1456
1545
  "isPanningEnabled": {
1457
1546
  "type": "boolean",
1458
1547
  "mutable": true,
@@ -1786,7 +1875,8 @@ export class KritzelEditor {
1786
1875
  "currentIsPublic": {},
1787
1876
  "isEditorVisible": {},
1788
1877
  "activeNotification": {},
1789
- "isNotificationDismissing": {}
1878
+ "isNotificationDismissing": {},
1879
+ "normalizedControls": {}
1790
1880
  };
1791
1881
  }
1792
1882
  static get events() {
@@ -3845,6 +3935,39 @@ export class KritzelEditor {
3845
3935
  }]
3846
3936
  }
3847
3937
  },
3938
+ "registerFonts": {
3939
+ "complexType": {
3940
+ "signature": "(fonts: KritzelFontMap) => Promise<void>",
3941
+ "parameters": [{
3942
+ "name": "fonts",
3943
+ "type": "{ [x: string]: KritzelFontRegistration; }",
3944
+ "docs": "- Mapping from font keys to family names or font definitions."
3945
+ }],
3946
+ "references": {
3947
+ "Promise": {
3948
+ "location": "global",
3949
+ "id": "global::Promise"
3950
+ },
3951
+ "KritzelFontMap": {
3952
+ "location": "import",
3953
+ "path": "../../../interfaces/font.interface",
3954
+ "id": "src/interfaces/font.interface.ts::KritzelFontMap",
3955
+ "referenceLocation": "KritzelFontMap"
3956
+ }
3957
+ },
3958
+ "return": "Promise<void>"
3959
+ },
3960
+ "docs": {
3961
+ "text": "Registers custom fonts for the editor runtime.",
3962
+ "tags": [{
3963
+ "name": "param",
3964
+ "text": "fonts - Mapping from font keys to family names or font definitions."
3965
+ }, {
3966
+ "name": "remarks",
3967
+ "text": "This no longer controls the text-tool config tooltip font options.\nConfigure tooltip fonts via controls[].config.availableFonts."
3968
+ }]
3969
+ }
3970
+ },
3848
3971
  "t": {
3849
3972
  "complexType": {
3850
3973
  "signature": "(key: KritzelTermKey, vars?: KritzelTermVars) => Promise<string>",
@@ -3904,6 +4027,12 @@ export class KritzelEditor {
3904
4027
  }, {
3905
4028
  "propName": "activeWorkspace",
3906
4029
  "methodName": "onActiveWorkspaceChange"
4030
+ }, {
4031
+ "propName": "customFonts",
4032
+ "methodName": "onCustomFontsChange"
4033
+ }, {
4034
+ "propName": "controls",
4035
+ "methodName": "onControlsChange"
3907
4036
  }, {
3908
4037
  "propName": "activeWorkspaceId",
3909
4038
  "methodName": "onActiveWorkspaceIdChange"
@@ -3,8 +3,12 @@ import { KritzelViewport } from "../../../classes/core/viewport.class";
3
3
  import { KritzelSelectionTool } from "../../../classes/tools/selection-tool.class";
4
4
  import { KritzelTextTool } from "../../../classes/tools/text-tool.class";
5
5
  import { KritzelShapeTool } from "../../../classes/tools/shape-tool.class";
6
+ import { KritzelBrushTool } from "../../../classes/tools/brush-tool.class";
7
+ import { KritzelLineTool } from "../../../classes/tools/line-tool.class";
8
+ import { KritzelImageTool } from "../../../classes/tools/image-tool.class";
6
9
  import { KritzelSelectionBox } from "../../../classes/objects/selection-box.class";
7
10
  import { KritzelKeyHandler } from "../../../classes/handlers/key.handler";
11
+ import { KritzelBaseTool } from "../../../classes/tools/base-tool.class";
8
12
  import { KritzelEraserTool } from "../../../classes/tools/eraser-tool.class";
9
13
  import { KritzelKeyboardHelper } from "../../../helpers/keyboard.helper";
10
14
  import { KritzelContextMenuHandler } from "../../../classes/handlers/context-menu.handler";
@@ -21,6 +25,7 @@ import { KritzelSvgExportHelper } from "../../../helpers/svg-export.helper";
21
25
  import { toPng, toSvg } from "html-to-image";
22
26
  import { ObjectHelper } from "../../../helpers/object.helper";
23
27
  import { KritzelWheelHelper } from "../../../helpers/wheel.helper";
28
+ import { resolveTextToolAvailableFonts } from "../../../constants/text-tool-fonts.constants";
24
29
  export class KritzelEngine {
25
30
  host;
26
31
  /** The workspace to load and display. Changing this prop switches the active workspace. */
@@ -539,7 +544,7 @@ export class KritzelEngine {
539
544
  }
540
545
  return existingTool;
541
546
  }
542
- const registeredTool = this.core.toolRegistry.registerTool(toolName, toolClass);
547
+ const registeredTool = this.core.toolRegistry.registerTool(toolName, this.resolveToolClass(toolClass));
543
548
  if (toolConfig) {
544
549
  this.applyToolConfig(registeredTool, toolConfig);
545
550
  }
@@ -1720,31 +1725,71 @@ export class KritzelEngine {
1720
1725
  this.syncingChange.emit(isSyncing);
1721
1726
  }
1722
1727
  applyToolConfig(tool, toolConfig) {
1723
- Object.entries(toolConfig).forEach(([key, value]) => {
1728
+ const normalizedToolConfig = this.normalizeToolConfig(toolConfig);
1729
+ Object.entries(normalizedToolConfig).forEach(([key, value]) => {
1724
1730
  tool[key] = value;
1725
1731
  });
1726
1732
  // Resolve palettes map into the flat palette array the UI reads
1727
- if ('palettes' in toolConfig && 'type' in toolConfig) {
1728
- const brushConfig = toolConfig;
1733
+ if ('palettes' in normalizedToolConfig && 'type' in normalizedToolConfig) {
1734
+ const brushConfig = normalizedToolConfig;
1729
1735
  const resolvedPalette = brushConfig.palettes[brushConfig.type];
1730
1736
  if (resolvedPalette) {
1731
1737
  tool['palette'] = resolvedPalette;
1732
1738
  }
1733
1739
  }
1734
1740
  // Resolve sizes map into the flat sizes array the UI reads (brush tool uses a map keyed by type)
1735
- if ('sizes' in toolConfig && toolConfig.sizes != null && 'type' in toolConfig) {
1736
- const brushConfig = toolConfig;
1741
+ if ('sizes' in normalizedToolConfig && normalizedToolConfig.sizes != null && 'type' in normalizedToolConfig) {
1742
+ const brushConfig = normalizedToolConfig;
1737
1743
  const resolvedSizes = brushConfig.sizes?.[brushConfig.type];
1738
1744
  if (resolvedSizes) {
1739
1745
  tool['sizes'] = resolvedSizes;
1740
1746
  }
1741
1747
  }
1742
1748
  }
1749
+ normalizeToolConfig(toolConfig) {
1750
+ if (!this.isTextToolConfig(toolConfig)) {
1751
+ return toolConfig;
1752
+ }
1753
+ return {
1754
+ ...toolConfig,
1755
+ availableFonts: resolveTextToolAvailableFonts(toolConfig.availableFonts),
1756
+ };
1757
+ }
1758
+ isTextToolConfig(toolConfig) {
1759
+ return 'fontFamily' in toolConfig && 'size' in toolConfig && 'color' in toolConfig;
1760
+ }
1761
+ /** Built-in tool classes of this bundle keyed by their tool type. */
1762
+ static BUILT_IN_TOOL_CLASSES = {
1763
+ selection: KritzelSelectionTool,
1764
+ brush: KritzelBrushTool,
1765
+ eraser: KritzelEraserTool,
1766
+ line: KritzelLineTool,
1767
+ shape: KritzelShapeTool,
1768
+ text: KritzelTextTool,
1769
+ image: KritzelImageTool,
1770
+ };
1771
+ /**
1772
+ * Resolves a tool class to the internal built-in class when a foreign copy is passed.
1773
+ * Classes from another compiled copy of the library (dual-package hazard, e.g. built-ins
1774
+ * re-exported by a framework wrapper) would otherwise break `instanceof` checks.
1775
+ */
1776
+ resolveToolClass(toolClass) {
1777
+ if (toolClass.prototype instanceof KritzelBaseTool) {
1778
+ return toolClass;
1779
+ }
1780
+ try {
1781
+ const probe = new toolClass(this.core);
1782
+ return KritzelEngine.BUILT_IN_TOOL_CLASSES[probe.toolType] ?? toolClass;
1783
+ }
1784
+ catch {
1785
+ return toolClass;
1786
+ }
1787
+ }
1743
1788
  get isSelecting() {
1744
- return this.core.store.state.activeTool instanceof KritzelSelectionTool && this.core.store.state.isSelecting;
1789
+ return this.core.store.state.activeTool?.toolType === 'selection' && this.core.store.state.isSelecting;
1745
1790
  }
1746
1791
  get isSelectionActive() {
1747
- return this.core.store.state.activeTool instanceof KritzelSelectionTool && this.core.store.selectionGroup !== null;
1792
+ return this.core.store.state.activeTool?.toolType === 'selection' && this.core.store.selectionGroup !== null;
1748
1793
  }
1749
1794
  constructor() {
1750
1795
  this.core = new KritzelCore(this);
@@ -2043,7 +2088,7 @@ export class KritzelEngine {
2043
2088
  this.core.store.onStateChange('activeTool', this._handleActiveToolChange.bind(this));
2044
2089
  }
2045
2090
  _handleActiveToolChange(activeTool) {
2046
- if (!(activeTool instanceof KritzelSelectionTool)) {
2091
+ if (activeTool?.toolType !== 'selection') {
2047
2092
  this.core.clearSelection();
2048
2093
  this.core.store.objects?.remove(o => o instanceof KritzelSelectionBox);
2049
2094
  this.core.store.setSelectionBox(null);
@@ -2052,10 +2097,10 @@ export class KritzelEngine {
2052
2097
  this.core.store.state.isResizeHandleSelected = false;
2053
2098
  this.core.store.state.isRotationHandleSelected = false;
2054
2099
  }
2055
- if (!(activeTool instanceof KritzelTextTool)) {
2100
+ if (activeTool?.toolType !== 'text') {
2056
2101
  this.core.resetActiveText();
2057
2102
  }
2058
- if (!(activeTool instanceof KritzelShapeTool)) {
2103
+ if (activeTool?.toolType !== 'shape') {
2059
2104
  this.core.resetActiveShape();
2060
2105
  }
2061
2106
  this.core.store.state.skipContextMenu = false;
@@ -2289,7 +2334,7 @@ export class KritzelEngine {
2289
2334
  borderWidth: object.borderWidth + 'px',
2290
2335
  borderStyle: 'solid',
2291
2336
  padding: object.padding + 'px',
2292
- overflow: 'hidden',
2337
+ overflow: 'visible',
2293
2338
  display: 'block',
2294
2339
  } })), KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup') && !this.core.displaySelectionLineUI(object) && (h("div", { ref: el => el && object.mount(el), style: {
2295
2340
  position: 'absolute',
@@ -2639,7 +2684,7 @@ export class KritzelEngine {
2639
2684
  }, this.core.store.selectionGroup?.objects || []);
2640
2685
  }
2641
2686
  this.hideContextMenu();
2642
- }, onClose: () => this.hideContextMenu() })), this.core.store.objects?.hasAwareness && h("kritzel-awareness-cursors", { core: this.core }), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { core: this.core }), !this.core.licenseManager.isLicensed && h("kritzel-watermark", { core: this.core, label: this.core.localizationManager.translate('watermark.poweredBy') })));
2687
+ }, onClose: () => this.hideContextMenu() })), this.core.store.objects?.hasAwareness && h("kritzel-awareness-cursors", { core: this.core }), this.core.store.state?.activeTool?.toolType === 'eraser' && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { core: this.core }), !this.core.licenseManager.isLicensed && h("kritzel-watermark", { core: this.core, label: this.core.localizationManager.translate('watermark.poweredBy') })));
2643
2688
  }
2644
2689
  static get is() { return "kritzel-engine"; }
2645
2690
  static get encapsulation() { return "shadow"; }
@@ -1,37 +1,40 @@
1
1
  import { Host, h } from "@stencil/core";
2
2
  export class KritzelFontFamily {
3
- fontOptions = [
4
- { value: 'arial', label: 'Arial' },
5
- { value: 'verdana', label: 'Verdana' },
6
- { value: 'helvetica', label: 'Helvetica' },
7
- { value: 'tahoma', label: 'Tahoma' },
8
- { value: 'trebuchet ms', label: 'Trebuchet MS' },
9
- { value: 'times new roman', label: 'Times New Roman' },
10
- { value: 'georgia', label: 'Georgia' },
11
- { value: 'garamond', label: 'Garamond' },
12
- { value: 'courier new', label: 'Courier New' },
13
- { value: 'brush script mt', label: 'Brush Script MT' },
14
- ];
3
+ fontOptions = [];
15
4
  selectedFontFamily;
16
5
  fontFamilyChange;
6
+ handleFontOptionsChange() {
7
+ this.ensureValidSelectedFont(this.resolveFontOptions());
8
+ }
17
9
  componentWillLoad() {
18
- if (this.fontOptions && this.fontOptions.length > 0) {
19
- const isValidCurrentFont = this.fontOptions.some(opt => opt.value === this.selectedFontFamily);
20
- if (!this.selectedFontFamily || !isValidCurrentFont) {
21
- this.selectedFontFamily = this.fontOptions[0].value;
22
- }
23
- }
10
+ this.ensureValidSelectedFont(this.resolveFontOptions());
24
11
  }
25
12
  handleDropdownValueChange = (event) => {
26
13
  this.fontFamilyChange.emit(event.detail);
27
14
  };
15
+ ensureValidSelectedFont(options) {
16
+ if (options.length === 0) {
17
+ return;
18
+ }
19
+ const normalizedSelected = this.selectedFontFamily?.trim().toLocaleLowerCase();
20
+ const isValidCurrentFont = options.some(opt => opt.value.trim().toLocaleLowerCase() === normalizedSelected);
21
+ if (!normalizedSelected || !isValidCurrentFont) {
22
+ this.selectedFontFamily = options[0].value;
23
+ }
24
+ }
25
+ resolveFontOptions() {
26
+ return this.fontOptions;
27
+ }
28
28
  render() {
29
- const dropdownOptions = this.fontOptions.map(option => ({
29
+ const fontOptions = this.resolveFontOptions();
30
+ const selectedOption = fontOptions.find(option => option.value === this.selectedFontFamily);
31
+ const selectedFontFamily = selectedOption?.cssFontFamily ?? this.selectedFontFamily;
32
+ const dropdownOptions = fontOptions.map(option => ({
30
33
  value: option.value,
31
34
  label: option.label,
32
- style: { fontFamily: option.value },
35
+ style: { fontFamily: option.cssFontFamily ?? option.value },
33
36
  }));
34
- return (h(Host, { key: '0123dbdff9b7051d2ebc9dcf8d1f9c7c4c161dc4' }, h("kritzel-dropdown", { key: '5d4e289161502ecc60cef3d0c6d546a64817a12a', options: dropdownOptions, value: this.selectedFontFamily, onValueChanged: this.handleDropdownValueChange, selectStyles: { fontFamily: this.selectedFontFamily } })));
37
+ return (h(Host, { key: '059350f34370f7a291f47a42721ca5d9bb82df49' }, h("kritzel-dropdown", { key: 'fbd8cf1ae463c7e9b76d0ac754a41de54397058e', options: dropdownOptions, value: this.selectedFontFamily, onValueChanged: this.handleDropdownValueChange, selectStyles: { fontFamily: selectedFontFamily } })));
35
38
  }
36
39
  static get is() { return "kritzel-font-family"; }
37
40
  static get encapsulation() { return "shadow"; }
@@ -69,7 +72,7 @@ export class KritzelFontFamily {
69
72
  },
70
73
  "getter": false,
71
74
  "setter": false,
72
- "defaultValue": "[\n { value: 'arial', label: 'Arial' },\n { value: 'verdana', label: 'Verdana' },\n { value: 'helvetica', label: 'Helvetica' },\n { value: 'tahoma', label: 'Tahoma' },\n { value: 'trebuchet ms', label: 'Trebuchet MS' },\n { value: 'times new roman', label: 'Times New Roman' },\n { value: 'georgia', label: 'Georgia' },\n { value: 'garamond', label: 'Garamond' },\n { value: 'courier new', label: 'Courier New' },\n { value: 'brush script mt', label: 'Brush Script MT' },\n ]"
75
+ "defaultValue": "[]"
73
76
  },
74
77
  "selectedFontFamily": {
75
78
  "type": "string",
@@ -110,4 +113,10 @@ export class KritzelFontFamily {
110
113
  }
111
114
  }];
112
115
  }
116
+ static get watchers() {
117
+ return [{
118
+ "propName": "fontOptions",
119
+ "methodName": "handleFontOptionsChange"
120
+ }];
121
+ }
113
122
  }
@@ -1,6 +1,4 @@
1
1
  import { h, Host } from "@stencil/core";
2
- import { KritzelTextTool } from "../../../classes/tools/text-tool.class";
3
- import { KritzelSelectionTool } from "../../../classes/tools/selection-tool.class";
4
2
  import { KritzelDevicesHelper } from "../../../helpers/devices.helper";
5
3
  import { KritzelToolConfigHelper } from "../../../helpers/tool-config.helper";
6
4
  import { KritzelColorHelper } from "../../../helpers/color.helper";
@@ -40,8 +38,9 @@ export class KritzelControls {
40
38
  this.closeTooltip();
41
39
  }
42
40
  handleSelectionChange() {
43
- if (this.activeControl?.tool instanceof KritzelSelectionTool) {
44
- this.updateDisplayValues(this.activeControl.tool);
41
+ const tool = this.activeControl?.tool;
42
+ if (tool && typeof tool !== 'function' && tool.toolType === 'selection') {
43
+ this.updateDisplayValues(tool);
45
44
  }
46
45
  }
47
46
  async onControlsChange() {
@@ -92,7 +91,7 @@ export class KritzelControls {
92
91
  color: KritzelColorHelper.applyOpacity(color, opacity, this.theme),
93
92
  size,
94
93
  };
95
- if (tool instanceof KritzelTextTool) {
94
+ if (tool.toolType === 'text') {
96
95
  displayValues.fontFamily = tool.fontFamily;
97
96
  }
98
97
  // Check for equality implementation to prevent unnecessary re-renders
@@ -233,13 +232,13 @@ export class KritzelControls {
233
232
  // Separate tool controls from config control
234
233
  const toolControls = this.internalControls.filter(c => c.type === 'tool' || c.type === 'separator');
235
234
  const configControl = this.internalControls.find(c => c.type === 'config' && c.name === this.firstConfig?.name);
236
- return (h(Host, { key: '5a23a0a1baa407097b18b9bc43f41186c0c333b2', style: { display: this.visible ? '' : 'none' }, class: {
235
+ return (h(Host, { key: 'a526bd337a6b75933ec4be89c3edded0a9c39e7c', style: { display: this.visible ? '' : 'none' }, class: {
237
236
  mobile: this.isTouchDevice,
238
- } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '6d3878904bd13266be79cef0e0d1ee55d760b3da', style: {
237
+ } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '5314f067c5a22425690e3b6f26c836962ac528a2', style: {
239
238
  position: 'absolute',
240
239
  bottom: '56px',
241
240
  left: '12px',
242
- }, undoState: this.undoState, terms: this.terms, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '5e066964eba04ad9b0d1abf36f61b93464254f48', class: "kritzel-controls" }, h("div", { key: '76e49fd459e0a312837ec0b5e552355395b8814e', class: { 'scroll-indicator-left': true, 'visible': this.canScrollLeft } }), h("div", { key: 'd4cffe56418dae5c569a47fe2ffc3f810f221455', class: "kritzel-tools-scroll", ref: el => (this.toolsScrollRef = el), onScroll: this.handleToolsScroll }, toolControls.map(control => {
241
+ }, undoState: this.undoState, terms: this.terms, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '7cf0cd164d723b5901f54a6671395f63dd8bad29', class: "kritzel-controls" }, h("div", { key: 'b7169122686a0a67c37d96751291fc43f4c45e4a', class: { 'scroll-indicator-left': true, 'visible': this.canScrollLeft } }), h("div", { key: '6e3c4f30900e4722abdfaf3cfdc68cf1a07b9292', class: "kritzel-tools-scroll", ref: el => (this.toolsScrollRef = el), onScroll: this.handleToolsScroll }, toolControls.map(control => {
243
242
  // Check if this control has sub-options (split-button)
244
243
  if (control.subOptions?.length) {
245
244
  const selectedSubOption = this.getSelectedSubOption(control);
@@ -269,10 +268,10 @@ export class KritzelControls {
269
268
  'kritzel-control': true,
270
269
  'selected': this.activeControl?.name === control?.name,
271
270
  }, 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 })));
272
- })), h("div", { key: '49806e8e06b8756a097eeec53203a55586214a4a', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight && !(configControl && this.activeControl && hasConfigUI) } }), configControl && this.activeControl && (h("div", { class: {
271
+ })), h("div", { key: 'bc8d79e0512f30fe150c6d2ac7f81206fc47c1a5', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight && !(configControl && this.activeControl && hasConfigUI) } }), configControl && this.activeControl && (h("div", { class: {
273
272
  'kritzel-config-container': true,
274
273
  'visible': hasConfigUI,
275
- }, key: configControl.name }, h("div", { key: 'da0ec9cf776dee0872bbb7f0369a6a5a56ebb955', class: { 'config-gradient-left': true, 'visible': this.needsScrolling } }), h("kritzel-tooltip", { key: 'c848a63392d4ed9a30f467512421fbea3096d876', anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), triggerElement: this.configTriggerRef }, h("kritzel-tool-config", { key: 'c7ed50fbdfbdab1847fb8c5c6e73d0e743b45706', tool: this.activeControl.tool, theme: this.theme, engine: this.kritzelEngine, terms: this.terms, onToolChange: event => this.handleToolChange?.(event), onDisplayValuesChange: this.handleDisplayValuesChange, style: { width: '100%', height: '100%' } })), h("div", { key: 'de6c9801d3e9e89674b60b1c4e270b8520014aae', tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", "data-testid": "tool-config", ref: el => {
274
+ }, key: configControl.name }, h("div", { key: '671431a9c388080fb917d4e8c596d17e92eaba08', class: { 'config-gradient-left': true, 'visible': this.needsScrolling } }), h("kritzel-tooltip", { key: '394e8e2c07b191b3fb93645d68d7eae67761167a', anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), triggerElement: this.configTriggerRef }, h("kritzel-tool-config", { key: 'b8479fba2afdb51fe0ef965e18b1ae45ba8a7da6', tool: this.activeControl.tool, theme: this.theme, engine: this.kritzelEngine, terms: this.terms, onToolChange: event => this.handleToolChange?.(event), onDisplayValuesChange: this.handleDisplayValuesChange, style: { width: '100%', height: '100%' } })), h("div", { key: '8fac81d682dbdec6b5247154fd20d553dfe6f1a7', tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", "data-testid": "tool-config", ref: el => {
276
275
  if (el)
277
276
  this.configTriggerRef = el;
278
277
  }, onKeyDown: event => {
@@ -281,7 +280,7 @@ export class KritzelControls {
281
280
  }
282
281
  }, style: {
283
282
  cursor: 'pointer',
284
- } }, this.displayValues && (h("div", { key: '140bbf567b0bbd2a2fe878536e93d8646a58b209', class: "color-container" }, h("kritzel-color", { key: 'eeb1d94fa41bbc177d39b4158ae3c4c1d692c0ec', value: this.displayValues.color, theme: this.theme, size: 18, style: {
283
+ } }, this.displayValues && (h("div", { key: '6aa1daed9425d069c7d0405a04e65b4c4e88291f', class: "color-container" }, h("kritzel-color", { key: '7b54461769bf4aec3e39d1fbe21c015b7d7a85bb', value: this.displayValues.color, theme: this.theme, size: 18, style: {
285
284
  borderRadius: '50%',
286
285
  border: 'none',
287
286
  } })))))))));