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
@@ -1,5 +1,5 @@
1
1
  import { r as registerInstance, h, H as Host, c as createEvent, g as getElement } from './index-Dhio9uis.js';
2
- import { c as KritzelPath, e as KritzelLine, R as KritzelColorHelper, T as KritzelDevicesHelper, q as KritzelSelectionTool, n as KritzelTextTool, U as KritzelMouseButton, V as KritzelHTMLHelper, X as DEFAULT_STROKE_SIZES, Y as DEFAULT_COLOR_PALETTE, S as ShapeType, D as DEFAULT_BRUSH_CONFIG, j as KritzelBrushTool, l as KritzelEraserTool, z as DEFAULT_LINE_TOOL_CONFIG, k as KritzelLineTool, o as KritzelShapeTool, y as DEFAULT_TEXT_CONFIG, m as KritzelImageTool, L as KritzelAlignment, B as DEFAULT_ASSET_STORAGE_CONFIG, Z as ObjectHelper, _ as ThemeHelper, E as darkTheme, C as lightTheme, $ as KritzelSelectionGroup, a0 as KritzelSelectionBox, a1 as KritzelIconRegistry, a2 as KritzelKeyboardHelper, a3 as KritzelBaseHandler, s as KritzelWorkspace, f as KritzelGroup, h as KritzelCustomElement, d as KritzelImage, g as KritzelShape, b as KritzelText, M as runMigrations, Q as CURRENT_WORKSPACE_SCHEMA_VERSION, O as WORKSPACE_MIGRATIONS, a4 as KritzelClassHelper, P as CURRENT_APP_STATE_SCHEMA_VERSION, N as APP_STATE_MIGRATIONS, p as KritzelCursorHelper, a5 as imageCompression, t as KritzelAnchorManager, u as KritzelThemeManager, v as KritzelLocalizationManager, w as KritzelLicenseManager, r as KritzelAssetResolver, a6 as KritzelEventHelper, W as WORKSPACE_EXPORT_VERSION } from './schema.constants-Cv5-yM39.js';
2
+ import { c as KritzelPath, e as KritzelLine, T as KritzelColorHelper, U as KritzelDevicesHelper, V as KritzelMouseButton, X as KritzelHTMLHelper, Y as DEFAULT_STROKE_SIZES, Z as DEFAULT_COLOR_PALETTE, S as ShapeType, q as KritzelSelectionTool, D as DEFAULT_BRUSH_CONFIG, j as KritzelBrushTool, l as KritzelEraserTool, B as DEFAULT_LINE_TOOL_CONFIG, k as KritzelLineTool, o as KritzelShapeTool, z as DEFAULT_TEXT_CONFIG, n as KritzelTextTool, m as KritzelImageTool, M as KritzelAlignment, C as DEFAULT_ASSET_STORAGE_CONFIG, _ as ObjectHelper, $ as resolveTextToolAvailableFonts, a0 as ThemeHelper, F as darkTheme, E as lightTheme, a1 as KritzelSelectionGroup, a2 as KritzelSelectionBox, a3 as KritzelIconRegistry, y as KritzelFontRegistry, a4 as KritzelKeyboardHelper, a5 as KritzelBaseHandler, s as KritzelWorkspace, f as KritzelGroup, h as KritzelCustomElement, d as KritzelImage, g as KritzelShape, b as KritzelText, N as runMigrations, R as CURRENT_WORKSPACE_SCHEMA_VERSION, P as WORKSPACE_MIGRATIONS, a6 as KritzelClassHelper, Q as CURRENT_APP_STATE_SCHEMA_VERSION, O as APP_STATE_MIGRATIONS, p as KritzelCursorHelper, a7 as KritzelImageFileHelper, a8 as imageCompression, t as KritzelAnchorManager, u as KritzelThemeManager, v as KritzelLocalizationManager, w as KritzelLicenseManager, r as KritzelAssetResolver, a9 as KritzelEventHelper, W as WORKSPACE_EXPORT_VERSION, i as KritzelBaseTool } from './schema.constants-rIkxAcpB.js';
3
3
  import * as Y from 'yjs';
4
4
  import 'y-indexeddb';
5
5
  import 'y-websocket';
@@ -174,23 +174,38 @@ const KritzelAwarenessCursors = class {
174
174
  objectVersion = 0;
175
175
  cleanupIntervalId;
176
176
  objectChangeRafId = null;
177
+ removeAwarenessChangeListener;
178
+ removeObjectsChangeListener;
177
179
  componentDidLoad() {
178
- this.core.store.objects?.onAwarenessChange(states => {
180
+ this.removeAwarenessChangeListener = this.core.store.objects?.onAwarenessChange(states => {
179
181
  this.handleAwarenessChange(states);
180
182
  });
181
- this.core.store.objects?.onObjectsChange(() => {
183
+ this.removeObjectsChangeListener = this.core.store.objects?.onObjectsChange(() => {
182
184
  this.handleRemoteObjectChange();
183
185
  });
184
186
  this.cleanupIntervalId = setInterval(() => {
185
187
  this.cleanupStaleCursors();
186
188
  }, CLEANUP_INTERVAL_MS);
189
+ if (this.cleanupIntervalId && typeof this.cleanupIntervalId.unref === 'function') {
190
+ this.cleanupIntervalId.unref();
191
+ }
187
192
  }
188
193
  disconnectedCallback() {
189
- if (this.cleanupIntervalId) {
190
- clearInterval(this.cleanupIntervalId);
194
+ if (this.removeAwarenessChangeListener) {
195
+ this.removeAwarenessChangeListener();
196
+ this.removeAwarenessChangeListener = undefined;
197
+ }
198
+ if (this.removeObjectsChangeListener) {
199
+ this.removeObjectsChangeListener();
200
+ this.removeObjectsChangeListener = undefined;
191
201
  }
192
- if (this.objectChangeRafId !== null) {
193
- cancelAnimationFrame(this.objectChangeRafId);
202
+ if (this.cleanupIntervalId && typeof globalThis.clearInterval === 'function') {
203
+ globalThis.clearInterval(this.cleanupIntervalId);
204
+ this.cleanupIntervalId = undefined;
205
+ }
206
+ if (this.objectChangeRafId !== null && typeof globalThis.cancelAnimationFrame === 'function') {
207
+ globalThis.cancelAnimationFrame(this.objectChangeRafId);
208
+ this.objectChangeRafId = null;
194
209
  }
195
210
  }
196
211
  handleAwarenessChange(states) {
@@ -348,7 +363,7 @@ const KritzelAwarenessCursors = class {
348
363
  }
349
364
  render() {
350
365
  const cursors = Array.from(this.remoteCursors.values());
351
- return (h(Host, { key: '5c695e3c5a012767b31bb1b756ef03e2a174f46e' }, cursors.map(remoteCursor => {
366
+ return (h(Host, { key: '9be6694250bafe011be27301d1c7a53fc7957657' }, cursors.map(remoteCursor => {
352
367
  if (!remoteCursor.cursor)
353
368
  return null;
354
369
  // When a remote user is actively drawing, derive cursor position from
@@ -971,8 +986,9 @@ const KritzelControls = class {
971
986
  this.closeTooltip();
972
987
  }
973
988
  handleSelectionChange() {
974
- if (this.activeControl?.tool instanceof KritzelSelectionTool) {
975
- this.updateDisplayValues(this.activeControl.tool);
989
+ const tool = this.activeControl?.tool;
990
+ if (tool && typeof tool !== 'function' && tool.toolType === 'selection') {
991
+ this.updateDisplayValues(tool);
976
992
  }
977
993
  }
978
994
  async onControlsChange() {
@@ -1023,7 +1039,7 @@ const KritzelControls = class {
1023
1039
  color: KritzelColorHelper.applyOpacity(color, opacity, this.theme),
1024
1040
  size,
1025
1041
  };
1026
- if (tool instanceof KritzelTextTool) {
1042
+ if (tool.toolType === 'text') {
1027
1043
  displayValues.fontFamily = tool.fontFamily;
1028
1044
  }
1029
1045
  // Check for equality implementation to prevent unnecessary re-renders
@@ -1164,13 +1180,13 @@ const KritzelControls = class {
1164
1180
  // Separate tool controls from config control
1165
1181
  const toolControls = this.internalControls.filter(c => c.type === 'tool' || c.type === 'separator');
1166
1182
  const configControl = this.internalControls.find(c => c.type === 'config' && c.name === this.firstConfig?.name);
1167
- return (h(Host, { key: '5a23a0a1baa407097b18b9bc43f41186c0c333b2', style: { display: this.visible ? '' : 'none' }, class: {
1183
+ return (h(Host, { key: 'a526bd337a6b75933ec4be89c3edded0a9c39e7c', style: { display: this.visible ? '' : 'none' }, class: {
1168
1184
  mobile: this.isTouchDevice,
1169
- } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '6d3878904bd13266be79cef0e0d1ee55d760b3da', style: {
1185
+ } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '5314f067c5a22425690e3b6f26c836962ac528a2', style: {
1170
1186
  position: 'absolute',
1171
1187
  bottom: '56px',
1172
1188
  left: '12px',
1173
- }, 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 => {
1189
+ }, 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 => {
1174
1190
  // Check if this control has sub-options (split-button)
1175
1191
  if (control.subOptions?.length) {
1176
1192
  const selectedSubOption = this.getSelectedSubOption(control);
@@ -1200,10 +1216,10 @@ const KritzelControls = class {
1200
1216
  'kritzel-control': true,
1201
1217
  'selected': this.activeControl?.name === control?.name,
1202
1218
  }, 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 })));
1203
- })), h("div", { key: '49806e8e06b8756a097eeec53203a55586214a4a', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight && !(configControl && this.activeControl && hasConfigUI) } }), configControl && this.activeControl && (h("div", { class: {
1219
+ })), h("div", { key: 'bc8d79e0512f30fe150c6d2ac7f81206fc47c1a5', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight && !(configControl && this.activeControl && hasConfigUI) } }), configControl && this.activeControl && (h("div", { class: {
1204
1220
  'kritzel-config-container': true,
1205
1221
  'visible': hasConfigUI,
1206
- }, 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 => {
1222
+ }, 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 => {
1207
1223
  if (el)
1208
1224
  this.configTriggerRef = el;
1209
1225
  }, onKeyDown: event => {
@@ -1212,7 +1228,7 @@ const KritzelControls = class {
1212
1228
  }
1213
1229
  }, style: {
1214
1230
  cursor: 'pointer',
1215
- } }, this.displayValues && (h("div", { key: '140bbf567b0bbd2a2fe878536e93d8646a58b209', class: "color-container" }, h("kritzel-color", { key: 'eeb1d94fa41bbc177d39b4158ae3c4c1d692c0ec', value: this.displayValues.color, theme: this.theme, size: 18, style: {
1231
+ } }, this.displayValues && (h("div", { key: '6aa1daed9425d069c7d0405a04e65b4c4e88291f', class: "color-container" }, h("kritzel-color", { key: '7b54461769bf4aec3e39d1fbe21c015b7d7a85bb', value: this.displayValues.color, theme: this.theme, size: 18, style: {
1216
1232
  borderRadius: '50%',
1217
1233
  border: 'none',
1218
1234
  } })))))))));
@@ -2290,6 +2306,11 @@ const KritzelEditor = class {
2290
2306
  /** The locale used to resolve terms missing from the active locale. */
2291
2307
  fallbackLocale = 'en';
2292
2308
  customSvgIcons = {};
2309
+ /**
2310
+ * Registers fonts for runtime usage (for example custom object rendering).
2311
+ * Text-tool config tooltip options are controlled via controls[].config.availableFonts.
2312
+ */
2313
+ customFonts = {};
2293
2314
  isPanningEnabled = true;
2294
2315
  isZoomingEnabled = true;
2295
2316
  isControlsVisible = true;
@@ -2342,11 +2363,14 @@ const KritzelEditor = class {
2342
2363
  isEditorVisible = false;
2343
2364
  activeNotification;
2344
2365
  isNotificationDismissing = false;
2366
+ normalizedControls = [];
2345
2367
  notificationDismissTimeout;
2346
2368
  notificationDismissAnimationTimeout;
2347
2369
  notificationDisplayDurationMs = 5000;
2348
2370
  notificationDismissAnimationDurationMs = 180;
2349
2371
  isNotificationHovered = false;
2372
+ customFontsLoadVersion = 0;
2373
+ customFontsReady = Promise.resolve();
2350
2374
  onIsEngineReady(newValue) {
2351
2375
  if (newValue && this.isControlsReady) {
2352
2376
  this.checkIsReady();
@@ -2376,6 +2400,12 @@ const KritzelEditor = class {
2376
2400
  onActiveWorkspaceChange() {
2377
2401
  this.updateCurrentIsPublic();
2378
2402
  }
2403
+ onCustomFontsChange(newValue) {
2404
+ void this.registerCustomFonts(newValue);
2405
+ }
2406
+ onControlsChange(newValue) {
2407
+ this.normalizedControls = this.normalizeControls(newValue);
2408
+ }
2379
2409
  onActiveWorkspaceIdChange(newId) {
2380
2410
  // If no ID is provided, do nothing (allows other logic to control activeWorkspace)
2381
2411
  if (!newId) {
@@ -2649,9 +2679,27 @@ const KritzelEditor = class {
2649
2679
  loginDialogRef;
2650
2680
  currentUserDialogRef;
2651
2681
  componentWillLoad() {
2682
+ this.normalizedControls = this.normalizeControls(this.controls);
2652
2683
  this.loadSettingsFromStorage();
2653
2684
  this.applyTheme();
2654
2685
  }
2686
+ isTextToolConfig(config) {
2687
+ return !!config && 'fontFamily' in config && 'size' in config && 'color' in config;
2688
+ }
2689
+ normalizeControls(controls) {
2690
+ return controls.map(control => {
2691
+ if (!this.isTextToolConfig(control.config)) {
2692
+ return control;
2693
+ }
2694
+ return {
2695
+ ...control,
2696
+ config: {
2697
+ ...control.config,
2698
+ availableFonts: resolveTextToolAvailableFonts(control.config.availableFonts),
2699
+ },
2700
+ };
2701
+ });
2702
+ }
2655
2703
  applyTheme() {
2656
2704
  const themeObj = this.resolveThemeObject();
2657
2705
  ThemeHelper.applyThemeToElement(this.host, themeObj);
@@ -2662,6 +2710,7 @@ const KritzelEditor = class {
2662
2710
  }
2663
2711
  componentDidLoad() {
2664
2712
  this.registerCustomSvgIcons();
2713
+ this.registerCustomFonts();
2665
2714
  this.listenForMobileKeyboard();
2666
2715
  this.setOsSpecificCssVariables();
2667
2716
  }
@@ -2674,6 +2723,7 @@ const KritzelEditor = class {
2674
2723
  await customElements.whenDefined('kritzel-workspace-manager');
2675
2724
  await customElements.whenDefined('kritzel-controls');
2676
2725
  await customElements.whenDefined('kritzel-engine');
2726
+ await this.waitForCustomFontsReady();
2677
2727
  if (!this.isEngineReady || !this.isControlsReady || !this.isWorkspaceManagerReady || !this.activeWorkspace) {
2678
2728
  return;
2679
2729
  }
@@ -2937,6 +2987,15 @@ const KritzelEditor = class {
2937
2987
  async registerLocales(locales) {
2938
2988
  await this.engineRef?.registerLocales(locales);
2939
2989
  }
2990
+ /**
2991
+ * Registers custom fonts for the editor runtime.
2992
+ * @param fonts - Mapping from font keys to family names or font definitions.
2993
+ * @remarks This no longer controls the text-tool config tooltip font options.
2994
+ * Configure tooltip fonts via controls[].config.availableFonts.
2995
+ */
2996
+ async registerFonts(fonts) {
2997
+ await this.registerCustomFonts(fonts);
2998
+ }
2940
2999
  /**
2941
3000
  * Resolves a term key to its translated string for the active locale.
2942
3001
  * @param key - The term key to resolve.
@@ -3014,6 +3073,25 @@ const KritzelEditor = class {
3014
3073
  KritzelIconRegistry.register(name, svg);
3015
3074
  }
3016
3075
  }
3076
+ async registerCustomFonts(fonts = this.customFonts) {
3077
+ KritzelFontRegistry.registerFonts(fonts);
3078
+ const currentVersion = ++this.customFontsLoadVersion;
3079
+ const loadPromise = KritzelFontRegistry.waitForFonts(fonts);
3080
+ this.customFontsReady = loadPromise;
3081
+ await loadPromise;
3082
+ if (currentVersion !== this.customFontsLoadVersion) {
3083
+ await this.waitForCustomFontsReady();
3084
+ }
3085
+ }
3086
+ async waitForCustomFontsReady() {
3087
+ while (true) {
3088
+ const version = this.customFontsLoadVersion;
3089
+ await this.customFontsReady;
3090
+ if (version === this.customFontsLoadVersion) {
3091
+ return;
3092
+ }
3093
+ }
3094
+ }
3017
3095
  listenForMobileKeyboard() {
3018
3096
  KritzelKeyboardHelper.onKeyboardVisibleChanged(isOpen => {
3019
3097
  this.isVirtualKeyboardOpen = isOpen;
@@ -3052,36 +3130,36 @@ const KritzelEditor = class {
3052
3130
  const isLoggedIn = this.isLoggedIn;
3053
3131
  const shouldShowCurrentUser = isLoggedIn;
3054
3132
  const shouldShowLoginButton = this.isReady && !!this.loginConfig && !isLoggedIn;
3055
- return (h(Host, { key: '3128b38a04cd636abc1f2466f1ac1aa3dcea2262' }, h("div", { key: 'b1fbb051cb1144fd84f28cfea4ad824f92189dbd', class: "editor-content", style: {
3133
+ return (h(Host, { key: '00d652c74feac7b4917772146368125da6deeb1c' }, h("div", { key: 'f8fb86b45b0cdabb5e3ad52de6d6ef31a71a09e4', class: "editor-content", style: {
3056
3134
  opacity: this.isEditorVisible ? '1' : '0',
3057
3135
  visibility: this.isEditorVisible ? 'visible' : 'hidden',
3058
3136
  transition: 'opacity 0.2s ease-in-out, visibility 0.2s ease-in-out',
3059
- } }, 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 => {
3137
+ } }, 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 => {
3060
3138
  if (el) {
3061
3139
  this.engineRef = el;
3062
3140
  }
3063
- }, 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 => {
3141
+ }, 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 => {
3064
3142
  if (el) {
3065
3143
  this.controlsRef = el;
3066
3144
  }
3067
- }, 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 => {
3145
+ }, 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 => {
3068
3146
  if (el) {
3069
3147
  this.settingsRef = el;
3070
3148
  }
3071
- }, 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 => {
3149
+ }, 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 => {
3072
3150
  if (el) {
3073
3151
  this.exportRef = el;
3074
3152
  }
3075
- }, 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 => {
3153
+ }, 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 => {
3076
3154
  if (el) {
3077
3155
  this.currentUserDialogRef = el;
3078
3156
  this.currentUserDialogRef.addEventListener('logoutRequest', this.handleCurrentUserLogout);
3079
3157
  }
3080
- }, 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 => {
3158
+ }, 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 => {
3081
3159
  if (el) {
3082
3160
  this.shareDialogRef = el;
3083
3161
  }
3084
- }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: 'ac54c699a94bb2657a6817c37d6220b75d931cd3', ref: el => {
3162
+ }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '5f6528a57884bd1494e45755609e710f69041a4d', ref: el => {
3085
3163
  if (el) {
3086
3164
  this.loginDialogRef = el;
3087
3165
  }
@@ -3100,6 +3178,12 @@ const KritzelEditor = class {
3100
3178
  "activeWorkspace": [{
3101
3179
  "onActiveWorkspaceChange": 0
3102
3180
  }],
3181
+ "customFonts": [{
3182
+ "onCustomFontsChange": 0
3183
+ }],
3184
+ "controls": [{
3185
+ "onControlsChange": 0
3186
+ }],
3103
3187
  "activeWorkspaceId": [{
3104
3188
  "onActiveWorkspaceIdChange": 0
3105
3189
  }],
@@ -21381,7 +21465,7 @@ class KritzelContextMenuHandler extends KritzelBaseHandler {
21381
21465
  * @returns void - This method does not return a value; it updates state and triggers a rerender.
21382
21466
  */
21383
21467
  handleContextMenu(event) {
21384
- if (!(this._core.store.state.activeTool instanceof KritzelSelectionTool)) {
21468
+ if (this._core.store.state.activeTool?.toolType !== 'selection') {
21385
21469
  return;
21386
21470
  }
21387
21471
  if (this._core.store.state.skipContextMenu) {
@@ -21441,7 +21525,7 @@ class KritzelContextMenuHandler extends KritzelBaseHandler {
21441
21525
  */
21442
21526
  open(options) {
21443
21527
  const { x, y, objectId } = options;
21444
- if (this._core.store.state.activeTool instanceof KritzelSelectionTool) {
21528
+ if (this._core.store.state.activeTool?.toolType === 'selection') {
21445
21529
  const selectionTool = this._core.store.state.activeTool;
21446
21530
  selectionTool?.moveHandler?.cancelPendingDrag();
21447
21531
  }
@@ -22256,6 +22340,9 @@ class KritzelObjectMap {
22256
22340
  */
22257
22341
  onAwarenessChange(callback) {
22258
22342
  this._awarenessChangeCallbacks.push(callback);
22343
+ return () => {
22344
+ this._awarenessChangeCallbacks = this._awarenessChangeCallbacks.filter(existingCallback => existingCallback !== callback);
22345
+ };
22259
22346
  }
22260
22347
  /**
22261
22348
  * Registers a callback to be invoked when remote object changes are received.
@@ -22264,6 +22351,9 @@ class KritzelObjectMap {
22264
22351
  */
22265
22352
  onObjectsChange(callback) {
22266
22353
  this._objectsChangeCallbacks.push(callback);
22354
+ return () => {
22355
+ this._objectsChangeCallbacks = this._objectsChangeCallbacks.filter(existingCallback => existingCallback !== callback);
22356
+ };
22267
22357
  }
22268
22358
  /**
22269
22359
  * Returns the Yjs UndoManager instance for managing undo/redo operations.
@@ -22460,6 +22550,9 @@ class KritzelObjectMap {
22460
22550
  callback(states);
22461
22551
  }
22462
22552
  }, delayMs);
22553
+ if (typeof this._awarenessEmitTimeout.unref === 'function') {
22554
+ this._awarenessEmitTimeout.unref();
22555
+ }
22463
22556
  }
22464
22557
  };
22465
22558
  this._awareness.on('change', this._awarenessChangeHandler);
@@ -24450,9 +24543,8 @@ class KritzelClipboardManager {
24450
24543
  const scale = this._core.store.state.scale;
24451
24544
  const offset = 25 / scale;
24452
24545
  const preparePromises = intent.files.map(async (file, index) => {
24453
- // Use the default image compression size of 1200
24454
- const compressed = await imageCompression(file, { maxWidthOrHeight: 1200 });
24455
- return KritzelImage.fromBlob(this._core, compressed, {
24546
+ const preparedFile = await this.prepareImageFile(file);
24547
+ return KritzelImage.fromBlob(this._core, preparedFile, {
24456
24548
  translateX: index * offset,
24457
24549
  translateY: index * offset,
24458
24550
  });
@@ -24464,6 +24556,12 @@ class KritzelClipboardManager {
24464
24556
  return null;
24465
24557
  }
24466
24558
  }
24559
+ async prepareImageFile(file) {
24560
+ if (KritzelImageFileHelper.isGifFile(file)) {
24561
+ return file;
24562
+ }
24563
+ return imageCompression(file, { maxWidthOrHeight: 1200 });
24564
+ }
24467
24565
  async materializePlainText(intent) {
24468
24566
  const textObject = KritzelText.create(this._core);
24469
24567
  const paragraphs = intent.text.split('\n').map(line => {
@@ -27931,7 +28029,7 @@ const KritzelEngine = class {
27931
28029
  }
27932
28030
  return existingTool;
27933
28031
  }
27934
- const registeredTool = this.core.toolRegistry.registerTool(toolName, toolClass);
28032
+ const registeredTool = this.core.toolRegistry.registerTool(toolName, this.resolveToolClass(toolClass));
27935
28033
  if (toolConfig) {
27936
28034
  this.applyToolConfig(registeredTool, toolConfig);
27937
28035
  }
@@ -29112,31 +29210,71 @@ const KritzelEngine = class {
29112
29210
  this.syncingChange.emit(isSyncing);
29113
29211
  }
29114
29212
  applyToolConfig(tool, toolConfig) {
29115
- Object.entries(toolConfig).forEach(([key, value]) => {
29213
+ const normalizedToolConfig = this.normalizeToolConfig(toolConfig);
29214
+ Object.entries(normalizedToolConfig).forEach(([key, value]) => {
29116
29215
  tool[key] = value;
29117
29216
  });
29118
29217
  // Resolve palettes map into the flat palette array the UI reads
29119
- if ('palettes' in toolConfig && 'type' in toolConfig) {
29120
- const brushConfig = toolConfig;
29218
+ if ('palettes' in normalizedToolConfig && 'type' in normalizedToolConfig) {
29219
+ const brushConfig = normalizedToolConfig;
29121
29220
  const resolvedPalette = brushConfig.palettes[brushConfig.type];
29122
29221
  if (resolvedPalette) {
29123
29222
  tool['palette'] = resolvedPalette;
29124
29223
  }
29125
29224
  }
29126
29225
  // Resolve sizes map into the flat sizes array the UI reads (brush tool uses a map keyed by type)
29127
- if ('sizes' in toolConfig && toolConfig.sizes != null && 'type' in toolConfig) {
29128
- const brushConfig = toolConfig;
29226
+ if ('sizes' in normalizedToolConfig && normalizedToolConfig.sizes != null && 'type' in normalizedToolConfig) {
29227
+ const brushConfig = normalizedToolConfig;
29129
29228
  const resolvedSizes = brushConfig.sizes?.[brushConfig.type];
29130
29229
  if (resolvedSizes) {
29131
29230
  tool['sizes'] = resolvedSizes;
29132
29231
  }
29133
29232
  }
29134
29233
  }
29234
+ normalizeToolConfig(toolConfig) {
29235
+ if (!this.isTextToolConfig(toolConfig)) {
29236
+ return toolConfig;
29237
+ }
29238
+ return {
29239
+ ...toolConfig,
29240
+ availableFonts: resolveTextToolAvailableFonts(toolConfig.availableFonts),
29241
+ };
29242
+ }
29243
+ isTextToolConfig(toolConfig) {
29244
+ return 'fontFamily' in toolConfig && 'size' in toolConfig && 'color' in toolConfig;
29245
+ }
29246
+ /** Built-in tool classes of this bundle keyed by their tool type. */
29247
+ static BUILT_IN_TOOL_CLASSES = {
29248
+ selection: KritzelSelectionTool,
29249
+ brush: KritzelBrushTool,
29250
+ eraser: KritzelEraserTool,
29251
+ line: KritzelLineTool,
29252
+ shape: KritzelShapeTool,
29253
+ text: KritzelTextTool,
29254
+ image: KritzelImageTool,
29255
+ };
29256
+ /**
29257
+ * Resolves a tool class to the internal built-in class when a foreign copy is passed.
29258
+ * Classes from another compiled copy of the library (dual-package hazard, e.g. built-ins
29259
+ * re-exported by a framework wrapper) would otherwise break `instanceof` checks.
29260
+ */
29261
+ resolveToolClass(toolClass) {
29262
+ if (toolClass.prototype instanceof KritzelBaseTool) {
29263
+ return toolClass;
29264
+ }
29265
+ try {
29266
+ const probe = new toolClass(this.core);
29267
+ return KritzelEngine.BUILT_IN_TOOL_CLASSES[probe.toolType] ?? toolClass;
29268
+ }
29269
+ catch {
29270
+ return toolClass;
29271
+ }
29272
+ }
29135
29273
  get isSelecting() {
29136
- return this.core.store.state.activeTool instanceof KritzelSelectionTool && this.core.store.state.isSelecting;
29274
+ return this.core.store.state.activeTool?.toolType === 'selection' && this.core.store.state.isSelecting;
29137
29275
  }
29138
29276
  get isSelectionActive() {
29139
- return this.core.store.state.activeTool instanceof KritzelSelectionTool && this.core.store.selectionGroup !== null;
29277
+ return this.core.store.state.activeTool?.toolType === 'selection' && this.core.store.selectionGroup !== null;
29140
29278
  }
29141
29279
  constructor(hostRef) {
29142
29280
  registerInstance(this, hostRef);
@@ -29453,7 +29591,7 @@ const KritzelEngine = class {
29453
29591
  this.core.store.onStateChange('activeTool', this._handleActiveToolChange.bind(this));
29454
29592
  }
29455
29593
  _handleActiveToolChange(activeTool) {
29456
- if (!(activeTool instanceof KritzelSelectionTool)) {
29594
+ if (activeTool?.toolType !== 'selection') {
29457
29595
  this.core.clearSelection();
29458
29596
  this.core.store.objects?.remove(o => o instanceof KritzelSelectionBox);
29459
29597
  this.core.store.setSelectionBox(null);
@@ -29462,10 +29600,10 @@ const KritzelEngine = class {
29462
29600
  this.core.store.state.isResizeHandleSelected = false;
29463
29601
  this.core.store.state.isRotationHandleSelected = false;
29464
29602
  }
29465
- if (!(activeTool instanceof KritzelTextTool)) {
29603
+ if (activeTool?.toolType !== 'text') {
29466
29604
  this.core.resetActiveText();
29467
29605
  }
29468
- if (!(activeTool instanceof KritzelShapeTool)) {
29606
+ if (activeTool?.toolType !== 'shape') {
29469
29607
  this.core.resetActiveShape();
29470
29608
  }
29471
29609
  this.core.store.state.skipContextMenu = false;
@@ -29699,7 +29837,7 @@ const KritzelEngine = class {
29699
29837
  borderWidth: object.borderWidth + 'px',
29700
29838
  borderStyle: 'solid',
29701
29839
  padding: object.padding + 'px',
29702
- overflow: 'hidden',
29840
+ overflow: 'visible',
29703
29841
  display: 'block',
29704
29842
  } })), KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup') && !this.core.displaySelectionLineUI(object) && (h("div", { ref: el => el && object.mount(el), style: {
29705
29843
  position: 'absolute',
@@ -30049,7 +30187,7 @@ const KritzelEngine = class {
30049
30187
  }, this.core.store.selectionGroup?.objects || []);
30050
30188
  }
30051
30189
  this.hideContextMenu();
30052
- }, 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') })));
30190
+ }, 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') })));
30053
30191
  }
30054
30192
  static get watchers() { return {
30055
30193
  "workspace": [{
@@ -30237,39 +30375,47 @@ const KritzelFontFamily = class {
30237
30375
  registerInstance(this, hostRef);
30238
30376
  this.fontFamilyChange = createEvent(this, "fontFamilyChange");
30239
30377
  }
30240
- fontOptions = [
30241
- { value: 'arial', label: 'Arial' },
30242
- { value: 'verdana', label: 'Verdana' },
30243
- { value: 'helvetica', label: 'Helvetica' },
30244
- { value: 'tahoma', label: 'Tahoma' },
30245
- { value: 'trebuchet ms', label: 'Trebuchet MS' },
30246
- { value: 'times new roman', label: 'Times New Roman' },
30247
- { value: 'georgia', label: 'Georgia' },
30248
- { value: 'garamond', label: 'Garamond' },
30249
- { value: 'courier new', label: 'Courier New' },
30250
- { value: 'brush script mt', label: 'Brush Script MT' },
30251
- ];
30378
+ fontOptions = [];
30252
30379
  selectedFontFamily;
30253
30380
  fontFamilyChange;
30381
+ handleFontOptionsChange() {
30382
+ this.ensureValidSelectedFont(this.resolveFontOptions());
30383
+ }
30254
30384
  componentWillLoad() {
30255
- if (this.fontOptions && this.fontOptions.length > 0) {
30256
- const isValidCurrentFont = this.fontOptions.some(opt => opt.value === this.selectedFontFamily);
30257
- if (!this.selectedFontFamily || !isValidCurrentFont) {
30258
- this.selectedFontFamily = this.fontOptions[0].value;
30259
- }
30260
- }
30385
+ this.ensureValidSelectedFont(this.resolveFontOptions());
30261
30386
  }
30262
30387
  handleDropdownValueChange = (event) => {
30263
30388
  this.fontFamilyChange.emit(event.detail);
30264
30389
  };
30390
+ ensureValidSelectedFont(options) {
30391
+ if (options.length === 0) {
30392
+ return;
30393
+ }
30394
+ const normalizedSelected = this.selectedFontFamily?.trim().toLocaleLowerCase();
30395
+ const isValidCurrentFont = options.some(opt => opt.value.trim().toLocaleLowerCase() === normalizedSelected);
30396
+ if (!normalizedSelected || !isValidCurrentFont) {
30397
+ this.selectedFontFamily = options[0].value;
30398
+ }
30399
+ }
30400
+ resolveFontOptions() {
30401
+ return this.fontOptions;
30402
+ }
30265
30403
  render() {
30266
- const dropdownOptions = this.fontOptions.map(option => ({
30404
+ const fontOptions = this.resolveFontOptions();
30405
+ const selectedOption = fontOptions.find(option => option.value === this.selectedFontFamily);
30406
+ const selectedFontFamily = selectedOption?.cssFontFamily ?? this.selectedFontFamily;
30407
+ const dropdownOptions = fontOptions.map(option => ({
30267
30408
  value: option.value,
30268
30409
  label: option.label,
30269
- style: { fontFamily: option.value },
30410
+ style: { fontFamily: option.cssFontFamily ?? option.value },
30270
30411
  }));
30271
- return (h(Host, { key: '0123dbdff9b7051d2ebc9dcf8d1f9c7c4c161dc4' }, h("kritzel-dropdown", { key: '5d4e289161502ecc60cef3d0c6d546a64817a12a', options: dropdownOptions, value: this.selectedFontFamily, onValueChanged: this.handleDropdownValueChange, selectStyles: { fontFamily: this.selectedFontFamily } })));
30412
+ return (h(Host, { key: '059350f34370f7a291f47a42721ca5d9bb82df49' }, h("kritzel-dropdown", { key: 'fbd8cf1ae463c7e9b76d0ac754a41de54397058e', options: dropdownOptions, value: this.selectedFontFamily, onValueChanged: this.handleDropdownValueChange, selectStyles: { fontFamily: selectedFontFamily } })));
30272
30413
  }
30414
+ static get watchers() { return {
30415
+ "fontOptions": [{
30416
+ "handleFontOptionsChange": 0
30417
+ }]
30418
+ }; }
30273
30419
  };
30274
30420
  KritzelFontFamily.style = kritzelFontFamilyCss();
30275
30421
 
@@ -31467,7 +31613,7 @@ const KritzelPortal = class {
31467
31613
  * This file is auto-generated by the version bump scripts.
31468
31614
  * Do not modify manually.
31469
31615
  */
31470
- const KRITZEL_VERSION = '0.4.0';
31616
+ const KRITZEL_VERSION = '0.4.1';
31471
31617
 
31472
31618
  const kritzelSettingsCss = () => `:host{display:contents}kritzel-dialog{--kritzel-dialog-body-padding:0;--kritzel-dialog-width-large:800px;--kritzel-dialog-height-large:500px}.footer-button{padding:8px 16px;border-radius:6px;cursor:pointer;font-size:14px}.cancel-button{border:1px solid #ebebeb;background:#fff;color:inherit}.cancel-button:hover{background:#f5f5f5}.settings-content{padding:0}.settings-content h3{margin:0 0 16px 0;font-size:18px;font-weight:600;color:var(--kritzel-settings-content-heading-color, #333333)}.settings-content p{margin:0;font-size:14px;color:var(--kritzel-settings-content-text-color, #666666);line-height:1.5}.settings-group{display:flex;flex-direction:column;gap:24px}.settings-item{display:flex;flex-direction:column;gap:8px}.settings-row{display:flex;align-items:center;justify-content:space-between;gap:16px}.settings-label{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.settings-description{font-size:12px;color:var(--kritzel-settings-description-color, #888888);margin:0;line-height:1.4}.shortcuts-list{display:flex;flex-direction:column;gap:24px}.shortcuts-category{display:flex;flex-direction:column;gap:8px}.shortcuts-category-title{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.shortcuts-group{display:flex;flex-direction:column;gap:4px}.shortcut-item{display:flex;justify-content:space-between;align-items:center;padding:6px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-item-bg, rgba(0, 0, 0, 0.02))}.shortcut-label{font-size:14px;color:var(--kritzel-settings-content-text-color, #666666)}.shortcut-key{font-family:monospace;font-size:12px;padding:2px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-key-bg, #f0f0f0);color:var(--kritzel-settings-shortcut-key-color, #333333);border:1px solid var(--kritzel-settings-shortcut-key-border, #ddd)}`;
31473
31619
 
@@ -35182,8 +35328,43 @@ const KritzelToolConfig = class {
35182
35328
  sizes = [];
35183
35329
  currentOpacity = 1;
35184
35330
  updateTrigger = 0;
35331
+ getFontOptionsFromTool() {
35332
+ const configuredFonts = resolveTextToolAvailableFonts(this.tool.availableFonts).map(font => {
35333
+ const registeredFont = typeof font === 'string'
35334
+ ? {
35335
+ family: font,
35336
+ label: font,
35337
+ cssFontFamily: font,
35338
+ }
35339
+ : {
35340
+ family: font.family,
35341
+ label: font.label ?? font.family,
35342
+ cssFontFamily: font.cssFontFamily ?? font.family,
35343
+ };
35344
+ return {
35345
+ value: registeredFont.family,
35346
+ label: registeredFont.label,
35347
+ cssFontFamily: registeredFont.cssFontFamily,
35348
+ };
35349
+ });
35350
+ const mergedFonts = [...configuredFonts];
35351
+ const seenFonts = new Set(mergedFonts.map(font => font.value.trim().toLocaleLowerCase()));
35352
+ for (const font of KritzelFontRegistry.list()) {
35353
+ const normalizedValue = font.family.trim().toLocaleLowerCase();
35354
+ if (seenFonts.has(normalizedValue)) {
35355
+ continue;
35356
+ }
35357
+ seenFonts.add(normalizedValue);
35358
+ mergedFonts.push({
35359
+ value: font.family,
35360
+ label: font.label,
35361
+ cssFontFamily: font.cssFontFamily,
35362
+ });
35363
+ }
35364
+ return mergedFonts;
35365
+ }
35185
35366
  handleSelectionChange() {
35186
- if (this.tool instanceof KritzelSelectionTool) {
35367
+ if (this.tool?.toolType === 'selection') {
35187
35368
  this.config = KritzelToolConfigHelper.getToolConfig(this.tool);
35188
35369
  if (this.config) {
35189
35370
  this.updatePalette();
@@ -35220,7 +35401,7 @@ const KritzelToolConfig = class {
35220
35401
  color: KritzelColorHelper.applyOpacity(color, opacity, this.theme),
35221
35402
  size,
35222
35403
  };
35223
- if (this.tool instanceof KritzelTextTool) {
35404
+ if (this.tool.toolType === 'text') {
35224
35405
  displayValues.fontFamily = this.tool.fontFamily;
35225
35406
  }
35226
35407
  this.displayValuesChange.emit(displayValues);
@@ -35313,7 +35494,7 @@ const KritzelToolConfig = class {
35313
35494
  case 'shape-fill':
35314
35495
  return (h("kritzel-shape-fill", { key: control.type, value: this.getShapeFillValue(), onValueChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
35315
35496
  case 'font-family':
35316
- return (h("kritzel-font-family", { key: control.type, selectedFontFamily: value, onFontFamilyChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
35497
+ return (h("kritzel-font-family", { key: control.type, fontOptions: this.getFontOptionsFromTool(), selectedFontFamily: value, onFontFamilyChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
35317
35498
  default:
35318
35499
  return null;
35319
35500
  }