kritzel-stencil 0.4.7 → 0.4.8

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 (47) hide show
  1. package/dist/cjs/index.cjs.js +1 -1
  2. package/dist/cjs/kritzel-active-users_47.cjs.entry.js +31 -14
  3. package/dist/cjs/{schema.constants-C8hqt--r.js → schema.constants-gDeS7USU.js} +75 -7
  4. package/dist/collection/classes/handlers/line-handle.handler.js +1 -1
  5. package/dist/collection/classes/managers/license.manager.js +56 -0
  6. package/dist/collection/classes/objects/line.class.js +1 -1
  7. package/dist/collection/classes/objects/shape.class.js +3 -3
  8. package/dist/collection/classes/objects/text.class.js +1 -1
  9. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +10 -3
  10. package/dist/collection/constants/license.constants.js +1 -1
  11. package/dist/collection/constants/version.js +1 -1
  12. package/dist/collection/helpers/svg-export.helper.js +19 -9
  13. package/dist/collection/helpers/theme.helper.js +8 -0
  14. package/dist/collection/themes/dark-theme.js +2 -0
  15. package/dist/collection/themes/light-theme.js +2 -0
  16. package/dist/components/index.js +3 -3
  17. package/dist/components/kritzel-awareness-cursors.js +1 -1
  18. package/dist/components/kritzel-color-palette.js +1 -1
  19. package/dist/components/kritzel-color.js +1 -1
  20. package/dist/components/kritzel-controls.js +1 -1
  21. package/dist/components/kritzel-editor.js +9 -9
  22. package/dist/components/kritzel-engine.js +1 -1
  23. package/dist/components/kritzel-settings.js +1 -1
  24. package/dist/components/kritzel-stroke-size.js +1 -1
  25. package/dist/components/kritzel-tool-config.js +1 -1
  26. package/dist/components/{p-Ka7OWbnk.js → p-0H4HOJjy.js} +2 -2
  27. package/dist/components/{p-1nG2hxfJ.js → p-9m1cAVwG.js} +2 -2
  28. package/dist/components/{p-DkH3hoLz.js → p-BknTMcbz.js} +5 -5
  29. package/dist/components/{p-zd7zXld8.js → p-CMZ09rB1.js} +1 -1
  30. package/dist/components/{p-BmyTdQGy.js → p-CURiiQfq.js} +12 -0
  31. package/dist/components/{p-Bm_BWQa4.js → p-Ckm1Aq0V.js} +1 -1
  32. package/dist/components/{p-BZxOukfY.js → p-Cr2z2UBL.js} +4 -4
  33. package/dist/components/{p-C2yi85nc.js → p-D8bHNNEm.js} +93 -20
  34. package/dist/components/{p-D0oplzU-.js → p-DgA865Tp.js} +1 -1
  35. package/dist/esm/index.js +2 -2
  36. package/dist/esm/kritzel-active-users_47.entry.js +31 -14
  37. package/dist/esm/{schema.constants-uC7_X6yr.js → schema.constants-Rpm1NA9u.js} +75 -7
  38. package/dist/stencil/index.esm.js +2 -2
  39. package/dist/stencil/{p-472f2e90.entry.js → p-8c1b7b08.entry.js} +31 -14
  40. package/dist/stencil/{p-uC7_X6yr.js → p-Rpm1NA9u.js} +75 -7
  41. package/dist/stencil/stencil.esm.js +1 -1
  42. package/dist/types/classes/managers/license.manager.d.ts +6 -0
  43. package/dist/types/constants/license.constants.d.ts +1 -1
  44. package/dist/types/constants/version.d.ts +1 -1
  45. package/dist/types/helpers/theme.helper.d.ts +1 -0
  46. package/dist/types/interfaces/theme.interface.d.ts +2 -0
  47. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var schema_constants = require('./schema.constants-C8hqt--r.js');
3
+ var schema_constants = require('./schema.constants-gDeS7USU.js');
4
4
  var Y = require('yjs');
5
5
  var yIndexeddb = require('y-indexeddb');
6
6
  var yWebsocket = require('y-websocket');
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var index = require('./index-Xav9JFHg.js');
4
- var schema_constants = require('./schema.constants-C8hqt--r.js');
4
+ var schema_constants = require('./schema.constants-gDeS7USU.js');
5
5
  var Y = require('yjs');
6
6
  require('y-indexeddb');
7
7
  require('y-websocket');
@@ -26284,14 +26284,20 @@ class KritzelSvgExportHelper {
26284
26284
  const scale = this.getObjectScale(text);
26285
26285
  // Convert ProseMirror content to HTML
26286
26286
  const htmlContent = this.prosemirrorToHtml(text.content, text, theme);
26287
- // Calculate dimensions accounting for scale
26287
+ // Calculate dimensions in SVG world units (world units = CSS px in a 1:1 SVG).
26288
26288
  const width = text.totalWidth / scale;
26289
26289
  const height = text.totalHeight / scale;
26290
+ // Convert the font size from pt to SVG world units (px equivalent).
26291
+ // text.fontSize is stored in CSS pt; 1 pt = 4/3 px at 96 dpi.
26292
+ // We also bake in scaleFactor (user resize) and divide by the object scale so
26293
+ // that the rendered font aligns with the world-unit dimensions above.
26294
+ // The CSS transform: scale(scaleFactor) is removed from the div because the
26295
+ // adjusted font-size already accounts for it – keeping both would double-scale
26296
+ // the content and overflow the foreignObject.
26297
+ const svgFontSizePx = (text.fontSize * (4 / 3) * (text.scaleFactor || 1) / scale).toFixed(4);
26290
26298
  return `<g transform="${transform}"${opacity}>
26291
- <foreignObject x="0" y="0" width="${width}" height="${height}">
26292
- <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: ${text.fontFamily}; font-size: ${text.fontSize}pt; color: ${fontColor}; background-color: ${bgColor || 'transparent'}; transform: scale(${text.scaleFactor}); transform-origin: top left; white-space: pre-wrap; word-wrap: break-word;">
26293
- ${htmlContent}
26294
- </div>
26299
+ <foreignObject x="0" y="0" width="${width}" height="${height}" overflow="visible">
26300
+ <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: ${text.fontFamily}; font-size: ${svgFontSizePx}px; color: ${fontColor}; background-color: ${bgColor || 'transparent'}; white-space: pre-wrap; word-wrap: break-word; width: 100%; margin: 0; padding: 0; box-sizing: border-box;">${htmlContent.trim()}</div>
26295
26301
  </foreignObject>
26296
26302
  </g>`;
26297
26303
  }
@@ -26310,9 +26316,13 @@ class KritzelSvgExportHelper {
26310
26316
  const height = text.totalHeight / scale;
26311
26317
  const lines = this.prosemirrorToPlainTextLines(text.content);
26312
26318
  const resolvedLines = lines.length > 0 ? lines : [''];
26313
- const resolvedFontSize = Math.max(1, text.fontSize * (text.scaleFactor || 1));
26314
- const lineHeight = resolvedFontSize * 1.2;
26315
- const startY = resolvedFontSize;
26319
+ // Convert the font size from pt to SVG world units (px equivalent).
26320
+ // text.fontSize is stored in CSS pt; 1 pt = 4/3 px at 96 dpi.
26321
+ // We also bake in scaleFactor (user resize) and divide by the object scale so
26322
+ // that the rendered lines align with the world-unit bounding box dimensions above.
26323
+ const resolvedFontSizePx = Math.max(1, text.fontSize * (4 / 3) * (text.scaleFactor || 1) / scale);
26324
+ const lineHeight = resolvedFontSizePx * 1.2;
26325
+ const startY = resolvedFontSizePx;
26316
26326
  const bgRect = bgColor && bgColor !== 'transparent'
26317
26327
  ? `<rect x="0" y="0" width="${width}" height="${height}" fill="${bgColor}"/>`
26318
26328
  : '';
@@ -26324,7 +26334,7 @@ class KritzelSvgExportHelper {
26324
26334
  .join('');
26325
26335
  return `<g transform="${transform}"${opacity}>
26326
26336
  ${bgRect}
26327
- <text font-family="${this.escapeHtml(text.fontFamily)}" font-size="${resolvedFontSize}" fill="${fontColor}">${tspans}</text>
26337
+ <text font-family="${this.escapeHtml(text.fontFamily)}" font-size="${resolvedFontSizePx}" fill="${fontColor}">${tspans}</text>
26328
26338
  </g>`;
26329
26339
  }
26330
26340
  /**
@@ -29983,7 +29993,9 @@ const KritzelEngine = class {
29983
29993
  this.emitObjectsInViewportChange();
29984
29994
  }
29985
29995
  }
29986
- return (index.h(index.Host, null, index.h("kritzel-loading-overlay", { visible: this.core.store.state.isLoading, text: this.core.localizationManager.translate('engine.loading') }), index.h("kritzel-sync-indicator", { visible: this.isSyncing, text: "Syncing..." }), this.core.store.state.debugInfo.showViewportInfo && (index.h("div", { class: "debug-panel" }, index.h("div", null, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), index.h("div", null, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), index.h("div", null, "TranslateX: ", this.core.store.state?.translateX), index.h("div", null, "TranslateY: ", this.core.store.state?.translateY), index.h("div", null, "ViewportWidth: ", this.core.store.state?.viewportWidth), index.h("div", null, "ViewportHeight: ", this.core.store.state?.viewportHeight), index.h("div", null, "PointerCount: ", this.core.store.state.pointers.size), index.h("div", null, "Scale: ", this.core.store.state?.scale), index.h("div", null, "ActiveTool: ", this.core.store.state?.activeTool?.name), index.h("div", null, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), index.h("div", null, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), index.h("div", null, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), index.h("div", null, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), index.h("div", null, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), index.h("div", null, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), index.h("div", null, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), index.h("div", null, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), index.h("div", null, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), index.h("div", null, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), index.h("div", null, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), index.h("div", null, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), index.h("div", null, "PointerX: ", this.core.store.state?.pointerX), index.h("div", null, "PointerY: ", this.core.store.state?.pointerY), index.h("div", null, "TotalObjects: ", this.core.store.totalObjectCount), index.h("div", null, "ObjectsInViewport: ", this.core.store.objectsInViewport.length), index.h("div", null, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), index.h("div", null, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), index.h("div", { id: "origin", class: "origin", style: {
29996
+ return (index.h(index.Host, null, index.h("kritzel-loading-overlay", { visible: this.core.store.state.isLoading, text: this.core.localizationManager.translate('engine.loading') }), index.h("kritzel-sync-indicator", { visible: this.isSyncing, text: "Syncing..." }), this.core.store.state.debugInfo.showViewportInfo && (index.h("div", { class: "debug-panel", style: {
29997
+ color: 'var(--kritzel-global-text-primary)',
29998
+ } }, index.h("div", null, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), index.h("div", null, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), index.h("div", null, "TranslateX: ", this.core.store.state?.translateX), index.h("div", null, "TranslateY: ", this.core.store.state?.translateY), index.h("div", null, "ViewportWidth: ", this.core.store.state?.viewportWidth), index.h("div", null, "ViewportHeight: ", this.core.store.state?.viewportHeight), index.h("div", null, "PointerCount: ", this.core.store.state.pointers.size), index.h("div", null, "Scale: ", this.core.store.state?.scale), index.h("div", null, "ActiveTool: ", this.core.store.state?.activeTool?.name), index.h("div", null, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), index.h("div", null, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), index.h("div", null, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), index.h("div", null, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), index.h("div", null, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), index.h("div", null, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), index.h("div", null, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), index.h("div", null, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), index.h("div", null, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), index.h("div", null, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), index.h("div", null, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), index.h("div", null, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), index.h("div", null, "PointerX: ", this.core.store.state?.pointerX), index.h("div", null, "PointerY: ", this.core.store.state?.pointerY), index.h("div", null, "TotalObjects: ", this.core.store.totalObjectCount), index.h("div", null, "ObjectsInViewport: ", this.core.store.objectsInViewport.length), index.h("div", null, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), index.h("div", null, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), index.h("div", { id: "origin", class: "origin", style: {
29987
29999
  transform: `matrix(${this.core.store.state?.scale}, 0, 0, ${this.core.store.state?.scale}, ${this.core.store.state?.translateX}, ${this.core.store.state?.translateY})`,
29988
30000
  opacity: this._sceneBootstrapOpacity.toString(),
29989
30001
  transition: this._sceneBootstrapTransitionEnabled ? 'opacity 220ms ease-out' : 'none',
@@ -30166,6 +30178,7 @@ const KritzelEngine = class {
30166
30178
  left: `${object.totalWidth}px`,
30167
30179
  top: '0',
30168
30180
  zIndex: (object.zIndex + 2).toString(),
30181
+ color: 'var(--kritzel-global-text-primary)',
30169
30182
  } }, index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "Id: ", object.id), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "userId: ", object.userId), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "width: ", object.width), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "height: ", object.height), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "translateX: ", object.translateX), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "translateY: ", object.translateY), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "scale: ", object.scale), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "rotationDegrees: ", object.rotationDegrees), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "zIndex: ", object.zIndex), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "isSelected: ", object.isSelected ? 'true' : 'false'), schema_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelCustomElement') && (index.h("div", null, index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "isInteractive: ", object.isInteractive ? 'true' : 'false'))), schema_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelImage') && (index.h("div", null, index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "assetId: ", object.assetId), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "mimeType: ", object.mimeType))))), (this.core.displaySelectionGroupUI(object) || this.core.displaySelectionLineUI(object)) &&
30170
30183
  (() => {
30171
30184
  const isSelectionGroup = schema_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup');
@@ -30366,8 +30379,12 @@ const KritzelEngine = class {
30366
30379
  strokeDasharray: data.dashArray,
30367
30380
  strokeLinecap: 'round',
30368
30381
  } }))), index.h("circle", { cx: data.centerX, cy: data.centerY, r: data.indicatorRadius, style: {
30369
- fill: isActiveSnap ? 'var(--kritzel-snap-indicator-fill, rgba(59, 130, 246, 0.3))' : 'rgba(59, 130, 246, 0.18)',
30370
- stroke: isActiveSnap ? 'var(--kritzel-snap-indicator-stroke, #007bff)' : 'rgba(59, 130, 246, 0.5)',
30382
+ fill: isActiveSnap
30383
+ ? 'var(--kritzel-snap-indicator-fill, rgba(59, 130, 246, 0.3))'
30384
+ : 'var(--kritzel-snap-indicator-fill-inactive, var(--kritzel-snap-indicator-fill, rgba(59, 130, 246, 0.18)))',
30385
+ stroke: isActiveSnap
30386
+ ? 'var(--kritzel-snap-indicator-stroke, #007bff)'
30387
+ : 'var(--kritzel-snap-indicator-stroke-inactive, var(--kritzel-snap-indicator-stroke, rgba(59, 130, 246, 0.5)))',
30371
30388
  strokeWidth: data.indicatorStrokeWidth,
30372
30389
  } }))));
30373
30390
  })(), (() => {
@@ -31840,7 +31857,7 @@ const KritzelPortal = class {
31840
31857
  * This file is auto-generated by the version bump scripts.
31841
31858
  * Do not modify manually.
31842
31859
  */
31843
- const KRITZEL_VERSION = '0.4.7';
31860
+ const KRITZEL_VERSION = '0.4.8';
31844
31861
 
31845
31862
  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-tab-panel{display:none}.settings-tab-panel.is-active{display:block}.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)}`;
31846
31863
 
@@ -14579,7 +14579,9 @@ const lightTheme = {
14579
14579
  },
14580
14580
  snap: {
14581
14581
  indicatorFill: 'rgba(59, 130, 246, 0.3)',
14582
+ indicatorFillInactive: 'rgba(59, 130, 246, 0.18)',
14582
14583
  indicatorStroke: '#007bff',
14584
+ indicatorStrokeInactive: 'rgba(59, 130, 246, 0.5)',
14583
14585
  lineStroke: 'rgba(0, 0, 0, 0.2)',
14584
14586
  },
14585
14587
  fontSize: {
@@ -14916,7 +14918,9 @@ const darkTheme = {
14916
14918
  },
14917
14919
  snap: {
14918
14920
  indicatorFill: 'rgba(10, 132, 255, 0.35)',
14921
+ indicatorFillInactive: 'rgba(10, 132, 255, 0.2)',
14919
14922
  indicatorStroke: '#0A84FF',
14923
+ indicatorStrokeInactive: 'rgba(255, 255, 255, 0.45)',
14920
14924
  lineStroke: 'rgba(255, 255, 255, 0.35)',
14921
14925
  },
14922
14926
  fontSize: {
@@ -15116,6 +15120,7 @@ const darkTheme = {
15116
15120
  };
15117
15121
 
15118
15122
  class ThemeHelper {
15123
+ static appliedVariableNames = new WeakMap();
15119
15124
  /**
15120
15125
  * Converts a camelCase string to kebab-case.
15121
15126
  * @example ThemeHelper.camelToKebab('controlHoverBackgroundColor') → 'control-hover-background-color'
@@ -15181,7 +15186,14 @@ class ThemeHelper {
15181
15186
  */
15182
15187
  static applyThemeToElement(element, theme) {
15183
15188
  const variables = ThemeHelper.flattenThemeToVariables(theme);
15189
+ const previousVariableNames = ThemeHelper.appliedVariableNames.get(element);
15190
+ for (const name of previousVariableNames ?? []) {
15191
+ if (!variables.has(name)) {
15192
+ element.style.removeProperty(name);
15193
+ }
15194
+ }
15184
15195
  ThemeHelper.applyVariablesToElement(element, variables);
15196
+ ThemeHelper.appliedVariableNames.set(element, new Set(variables.keys()));
15185
15197
  }
15186
15198
  }
15187
15199
 
@@ -15802,7 +15814,7 @@ class KritzelText extends KritzelBaseObject {
15802
15814
  }
15803
15815
  element.style.fontFamily = KritzelFontRegistry.resolveCssFontFamily(this.fontFamily);
15804
15816
  element.style.fontSize = `${this.fontSize}pt`;
15805
- element.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor);
15817
+ element.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor, this._core?.themeManager?.currentTheme);
15806
15818
  if (this.isMounted && this.elementRef === element && this.editor.dom.parentElement === element) {
15807
15819
  if (!this._core.store.state.isScaling && !this._core.store.state.isPanning) {
15808
15820
  requestAnimationFrame(() => this.adjustSizeOnInput());
@@ -18709,7 +18721,7 @@ class KritzelLine extends KritzelBaseObject {
18709
18721
  getArrowFill(end) {
18710
18722
  const config = end === 'start' ? this.arrows?.start : this.arrows?.end;
18711
18723
  const color = config?.fill ?? this.stroke;
18712
- return KritzelColorHelper.resolveThemeColor(color);
18724
+ return KritzelColorHelper.resolveThemeColor(color, this._core?.themeManager?.currentTheme);
18713
18725
  }
18714
18726
  /**
18715
18727
  * Generates SVG path data for an arrow head based on the given style.
@@ -19512,7 +19524,7 @@ class KritzelShape extends KritzelBaseObject {
19512
19524
  }
19513
19525
  element.style.fontFamily = this.fontFamily;
19514
19526
  element.style.fontSize = `${this.fontSize}pt`;
19515
- element.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor);
19527
+ element.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor, this._core?.themeManager?.currentTheme);
19516
19528
  element.style.whiteSpace = 'pre-wrap';
19517
19529
  element.style.wordWrap = 'break-word';
19518
19530
  if (this.editor.dom.parentElement === element) {
@@ -19828,13 +19840,13 @@ class KritzelShape extends KritzelBaseObject {
19828
19840
  this.fontColor = KritzelColorHelper.determineTextColor(this.fillColor);
19829
19841
  // Update the editor's text color immediately
19830
19842
  if (this.editor?.dom?.parentElement) {
19831
- this.editor.dom.parentElement.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor);
19843
+ this.editor.dom.parentElement.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor, this._core?.themeManager?.currentTheme);
19832
19844
  }
19833
19845
  }
19834
19846
  else if (changedProperties.includes('fontColor')) {
19835
19847
  // Also update editor when fontColor changes directly
19836
19848
  if (this.editor?.dom?.parentElement) {
19837
- this.editor.dom.parentElement.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor);
19849
+ this.editor.dom.parentElement.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor, this._core?.themeManager?.currentTheme);
19838
19850
  }
19839
19851
  }
19840
19852
  }
@@ -24698,7 +24710,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
24698
24710
  t: clipInfo?.t,
24699
24711
  edgeX: clipInfo?.edgeX,
24700
24712
  edgeY: clipInfo?.edgeY,
24701
- lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
24713
+ lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke, this._core.themeManager.currentTheme),
24702
24714
  lineStrokeWidth: line.strokeWidth / line.scale,
24703
24715
  arrowOffset: endpoint === 'start'
24704
24716
  ? (line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined)
@@ -30277,7 +30289,7 @@ const KRITZEL_LICENSE_TOKEN_PREFIX = 'KRTZL1.';
30277
30289
  * printed public key here. Replacing this value invalidates every license
30278
30290
  * signed with the previous key.
30279
30291
  */
30280
- const KRITZEL_LICENSE_PUBLIC_KEY = 'I-pYJlxDEjT94rwSaqhXW5Sv__fMKS-JKKnV3pg0PaM';
30292
+ const KRITZEL_LICENSE_PUBLIC_KEY = '2IwZDin-qV3kOt8SEQCqdaJsGT9S7tAGapUYJVnVGQo';
30281
30293
 
30282
30294
  /** Minimum delay (ms) between periodic license re-validations. */
30283
30295
  const MIN_REVALIDATION_DELAY_MS = 30_000;
@@ -30510,8 +30522,64 @@ class KritzelLicenseManager {
30510
30522
  if (typeof claims.exp === 'number' && claims.exp * 1000 <= Date.now()) {
30511
30523
  return { status: 'rejected' }; // Cleanly expired.
30512
30524
  }
30525
+ const allowedDomains = this.normalizeAllowedDomains(claims.allowedDomains);
30526
+ if (allowedDomains === null || allowedDomains.length === 0) {
30527
+ return { status: 'rejected' };
30528
+ }
30529
+ const currentHostname = this.getCurrentHostname();
30530
+ if (!currentHostname || !allowedDomains.includes(currentHostname)) {
30531
+ return { status: 'rejected' };
30532
+ }
30513
30533
  return { status: 'pass', payloadSegment, signatureSegment, claims };
30514
30534
  }
30535
+ /** Returns the normalized current hostname, or null when unavailable/invalid. */
30536
+ getCurrentHostname() {
30537
+ const hostname = globalThis.location?.hostname;
30538
+ if (typeof hostname !== 'string') {
30539
+ return null;
30540
+ }
30541
+ return this.normalizeDomain(hostname);
30542
+ }
30543
+ /** Normalizes and validates the allowed domains claim. */
30544
+ normalizeAllowedDomains(domains) {
30545
+ if (!Array.isArray(domains)) {
30546
+ return null;
30547
+ }
30548
+ const normalized = new Set();
30549
+ for (const entry of domains) {
30550
+ if (typeof entry !== 'string') {
30551
+ return null;
30552
+ }
30553
+ const domain = this.normalizeDomain(entry);
30554
+ if (!domain) {
30555
+ return null;
30556
+ }
30557
+ normalized.add(domain);
30558
+ }
30559
+ return Array.from(normalized);
30560
+ }
30561
+ /** Normalizes a domain and rejects malformed hostnames. */
30562
+ normalizeDomain(value) {
30563
+ const trimmed = value.trim().toLowerCase();
30564
+ if (trimmed.length === 0) {
30565
+ return null;
30566
+ }
30567
+ const withoutTrailingDot = trimmed.endsWith('.') ? trimmed.slice(0, -1) : trimmed;
30568
+ if (withoutTrailingDot.length === 0) {
30569
+ return null;
30570
+ }
30571
+ try {
30572
+ const parsed = new URL(`https://${withoutTrailingDot}`);
30573
+ const normalizedHostname = parsed.hostname.toLowerCase();
30574
+ if (normalizedHostname !== withoutTrailingDot) {
30575
+ return null;
30576
+ }
30577
+ return normalizedHostname;
30578
+ }
30579
+ catch {
30580
+ return null;
30581
+ }
30582
+ }
30515
30583
  /**
30516
30584
  * Verifies the Ed25519 signature of the token against the embedded public
30517
30585
  * key. Resolves true/false for a definitive valid/invalid signature.
@@ -463,7 +463,7 @@ export class KritzelLineHandleHandler extends KritzelBaseHandler {
463
463
  t: clipInfo?.t,
464
464
  edgeX: clipInfo?.edgeX,
465
465
  edgeY: clipInfo?.edgeY,
466
- lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
466
+ lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke, this._core.themeManager.currentTheme),
467
467
  lineStrokeWidth: line.strokeWidth / line.scale,
468
468
  arrowOffset: endpoint === 'start'
469
469
  ? (line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined)
@@ -230,8 +230,64 @@ export class KritzelLicenseManager {
230
230
  if (typeof claims.exp === 'number' && claims.exp * 1000 <= Date.now()) {
231
231
  return { status: 'rejected' }; // Cleanly expired.
232
232
  }
233
+ const allowedDomains = this.normalizeAllowedDomains(claims.allowedDomains);
234
+ if (allowedDomains === null || allowedDomains.length === 0) {
235
+ return { status: 'rejected' };
236
+ }
237
+ const currentHostname = this.getCurrentHostname();
238
+ if (!currentHostname || !allowedDomains.includes(currentHostname)) {
239
+ return { status: 'rejected' };
240
+ }
233
241
  return { status: 'pass', payloadSegment, signatureSegment, claims };
234
242
  }
243
+ /** Returns the normalized current hostname, or null when unavailable/invalid. */
244
+ getCurrentHostname() {
245
+ const hostname = globalThis.location?.hostname;
246
+ if (typeof hostname !== 'string') {
247
+ return null;
248
+ }
249
+ return this.normalizeDomain(hostname);
250
+ }
251
+ /** Normalizes and validates the allowed domains claim. */
252
+ normalizeAllowedDomains(domains) {
253
+ if (!Array.isArray(domains)) {
254
+ return null;
255
+ }
256
+ const normalized = new Set();
257
+ for (const entry of domains) {
258
+ if (typeof entry !== 'string') {
259
+ return null;
260
+ }
261
+ const domain = this.normalizeDomain(entry);
262
+ if (!domain) {
263
+ return null;
264
+ }
265
+ normalized.add(domain);
266
+ }
267
+ return Array.from(normalized);
268
+ }
269
+ /** Normalizes a domain and rejects malformed hostnames. */
270
+ normalizeDomain(value) {
271
+ const trimmed = value.trim().toLowerCase();
272
+ if (trimmed.length === 0) {
273
+ return null;
274
+ }
275
+ const withoutTrailingDot = trimmed.endsWith('.') ? trimmed.slice(0, -1) : trimmed;
276
+ if (withoutTrailingDot.length === 0) {
277
+ return null;
278
+ }
279
+ try {
280
+ const parsed = new URL(`https://${withoutTrailingDot}`);
281
+ const normalizedHostname = parsed.hostname.toLowerCase();
282
+ if (normalizedHostname !== withoutTrailingDot) {
283
+ return null;
284
+ }
285
+ return normalizedHostname;
286
+ }
287
+ catch {
288
+ return null;
289
+ }
290
+ }
235
291
  /**
236
292
  * Verifies the Ed25519 signature of the token against the embedded public
237
293
  * key. Resolves true/false for a definitive valid/invalid signature.
@@ -749,7 +749,7 @@ export class KritzelLine extends KritzelBaseObject {
749
749
  getArrowFill(end) {
750
750
  const config = end === 'start' ? this.arrows?.start : this.arrows?.end;
751
751
  const color = config?.fill ?? this.stroke;
752
- return KritzelColorHelper.resolveThemeColor(color);
752
+ return KritzelColorHelper.resolveThemeColor(color, this._core?.themeManager?.currentTheme);
753
753
  }
754
754
  /**
755
755
  * Generates SVG path data for an arrow head based on the given style.
@@ -175,7 +175,7 @@ export class KritzelShape extends KritzelBaseObject {
175
175
  }
176
176
  element.style.fontFamily = this.fontFamily;
177
177
  element.style.fontSize = `${this.fontSize}pt`;
178
- element.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor);
178
+ element.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor, this._core?.themeManager?.currentTheme);
179
179
  element.style.whiteSpace = 'pre-wrap';
180
180
  element.style.wordWrap = 'break-word';
181
181
  if (this.editor.dom.parentElement === element) {
@@ -491,13 +491,13 @@ export class KritzelShape extends KritzelBaseObject {
491
491
  this.fontColor = KritzelColorHelper.determineTextColor(this.fillColor);
492
492
  // Update the editor's text color immediately
493
493
  if (this.editor?.dom?.parentElement) {
494
- this.editor.dom.parentElement.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor);
494
+ this.editor.dom.parentElement.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor, this._core?.themeManager?.currentTheme);
495
495
  }
496
496
  }
497
497
  else if (changedProperties.includes('fontColor')) {
498
498
  // Also update editor when fontColor changes directly
499
499
  if (this.editor?.dom?.parentElement) {
500
- this.editor.dom.parentElement.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor);
500
+ this.editor.dom.parentElement.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor, this._core?.themeManager?.currentTheme);
501
501
  }
502
502
  }
503
503
  }
@@ -149,7 +149,7 @@ export class KritzelText extends KritzelBaseObject {
149
149
  }
150
150
  element.style.fontFamily = KritzelFontRegistry.resolveCssFontFamily(this.fontFamily);
151
151
  element.style.fontSize = `${this.fontSize}pt`;
152
- element.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor);
152
+ element.style.color = KritzelColorHelper.resolveThemeColor(this.fontColor, this._core?.themeManager?.currentTheme);
153
153
  if (this.isMounted && this.elementRef === element && this.editor.dom.parentElement === element) {
154
154
  if (!this._core.store.state.isScaling && !this._core.store.state.isPanning) {
155
155
  requestAnimationFrame(() => this.adjustSizeOnInput());
@@ -2416,7 +2416,9 @@ export class KritzelEngine {
2416
2416
  this.emitObjectsInViewportChange();
2417
2417
  }
2418
2418
  }
2419
- return (h(Host, null, h("kritzel-loading-overlay", { visible: this.core.store.state.isLoading, text: this.core.localizationManager.translate('engine.loading') }), h("kritzel-sync-indicator", { visible: this.isSyncing, text: "Syncing..." }), this.core.store.state.debugInfo.showViewportInfo && (h("div", { class: "debug-panel" }, h("div", null, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", null, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", null, "TranslateX: ", this.core.store.state?.translateX), h("div", null, "TranslateY: ", this.core.store.state?.translateY), h("div", null, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", null, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", null, "PointerCount: ", this.core.store.state.pointers.size), h("div", null, "Scale: ", this.core.store.state?.scale), h("div", null, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", null, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", null, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", null, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", null, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", null, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", null, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", null, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", null, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", null, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", null, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", null, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", null, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", null, "PointerX: ", this.core.store.state?.pointerX), h("div", null, "PointerY: ", this.core.store.state?.pointerY), h("div", null, "TotalObjects: ", this.core.store.totalObjectCount), h("div", null, "ObjectsInViewport: ", this.core.store.objectsInViewport.length), h("div", null, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", null, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { id: "origin", class: "origin", style: {
2419
+ return (h(Host, null, h("kritzel-loading-overlay", { visible: this.core.store.state.isLoading, text: this.core.localizationManager.translate('engine.loading') }), h("kritzel-sync-indicator", { visible: this.isSyncing, text: "Syncing..." }), this.core.store.state.debugInfo.showViewportInfo && (h("div", { class: "debug-panel", style: {
2420
+ color: 'var(--kritzel-global-text-primary)',
2421
+ } }, h("div", null, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", null, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", null, "TranslateX: ", this.core.store.state?.translateX), h("div", null, "TranslateY: ", this.core.store.state?.translateY), h("div", null, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", null, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", null, "PointerCount: ", this.core.store.state.pointers.size), h("div", null, "Scale: ", this.core.store.state?.scale), h("div", null, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", null, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", null, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", null, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", null, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", null, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", null, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", null, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", null, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", null, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", null, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", null, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", null, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", null, "PointerX: ", this.core.store.state?.pointerX), h("div", null, "PointerY: ", this.core.store.state?.pointerY), h("div", null, "TotalObjects: ", this.core.store.totalObjectCount), h("div", null, "ObjectsInViewport: ", this.core.store.objectsInViewport.length), h("div", null, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", null, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { id: "origin", class: "origin", style: {
2420
2422
  transform: `matrix(${this.core.store.state?.scale}, 0, 0, ${this.core.store.state?.scale}, ${this.core.store.state?.translateX}, ${this.core.store.state?.translateY})`,
2421
2423
  opacity: this._sceneBootstrapOpacity.toString(),
2422
2424
  transition: this._sceneBootstrapTransitionEnabled ? 'opacity 220ms ease-out' : 'none',
@@ -2599,6 +2601,7 @@ export class KritzelEngine {
2599
2601
  left: `${object.totalWidth}px`,
2600
2602
  top: '0',
2601
2603
  zIndex: (object.zIndex + 2).toString(),
2604
+ color: 'var(--kritzel-global-text-primary)',
2602
2605
  } }, h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "Id: ", object.id), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "userId: ", object.userId), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "width: ", object.width), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "height: ", object.height), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "translateX: ", object.translateX), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "translateY: ", object.translateY), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "scale: ", object.scale), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "rotationDegrees: ", object.rotationDegrees), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "zIndex: ", object.zIndex), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "isSelected: ", object.isSelected ? 'true' : 'false'), KritzelClassHelper.isInstanceOf(object, 'KritzelCustomElement') && (h("div", null, h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "isInteractive: ", object.isInteractive ? 'true' : 'false'))), KritzelClassHelper.isInstanceOf(object, 'KritzelImage') && (h("div", null, h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "assetId: ", object.assetId), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "mimeType: ", object.mimeType))))), (this.core.displaySelectionGroupUI(object) || this.core.displaySelectionLineUI(object)) &&
2603
2606
  (() => {
2604
2607
  const isSelectionGroup = KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup');
@@ -2799,8 +2802,12 @@ export class KritzelEngine {
2799
2802
  strokeDasharray: data.dashArray,
2800
2803
  strokeLinecap: 'round',
2801
2804
  } }))), h("circle", { cx: data.centerX, cy: data.centerY, r: data.indicatorRadius, style: {
2802
- fill: isActiveSnap ? 'var(--kritzel-snap-indicator-fill, rgba(59, 130, 246, 0.3))' : 'rgba(59, 130, 246, 0.18)',
2803
- stroke: isActiveSnap ? 'var(--kritzel-snap-indicator-stroke, #007bff)' : 'rgba(59, 130, 246, 0.5)',
2805
+ fill: isActiveSnap
2806
+ ? 'var(--kritzel-snap-indicator-fill, rgba(59, 130, 246, 0.3))'
2807
+ : 'var(--kritzel-snap-indicator-fill-inactive, var(--kritzel-snap-indicator-fill, rgba(59, 130, 246, 0.18)))',
2808
+ stroke: isActiveSnap
2809
+ ? 'var(--kritzel-snap-indicator-stroke, #007bff)'
2810
+ : 'var(--kritzel-snap-indicator-stroke-inactive, var(--kritzel-snap-indicator-stroke, rgba(59, 130, 246, 0.5)))',
2804
2811
  strokeWidth: data.indicatorStrokeWidth,
2805
2812
  } }))));
2806
2813
  })(), (() => {
@@ -22,4 +22,4 @@ export const KRITZEL_LICENSE_TOKEN_PREFIX = 'KRTZL1.';
22
22
  * printed public key here. Replacing this value invalidates every license
23
23
  * signed with the previous key.
24
24
  */
25
- export const KRITZEL_LICENSE_PUBLIC_KEY = 'I-pYJlxDEjT94rwSaqhXW5Sv__fMKS-JKKnV3pg0PaM';
25
+ export const KRITZEL_LICENSE_PUBLIC_KEY = '2IwZDin-qV3kOt8SEQCqdaJsGT9S7tAGapUYJVnVGQo';
@@ -3,4 +3,4 @@
3
3
  * This file is auto-generated by the version bump scripts.
4
4
  * Do not modify manually.
5
5
  */
6
- export const KRITZEL_VERSION = '0.4.7';
6
+ export const KRITZEL_VERSION = '0.4.8';
@@ -346,14 +346,20 @@ export class KritzelSvgExportHelper {
346
346
  const scale = this.getObjectScale(text);
347
347
  // Convert ProseMirror content to HTML
348
348
  const htmlContent = this.prosemirrorToHtml(text.content, text, theme);
349
- // Calculate dimensions accounting for scale
349
+ // Calculate dimensions in SVG world units (world units = CSS px in a 1:1 SVG).
350
350
  const width = text.totalWidth / scale;
351
351
  const height = text.totalHeight / scale;
352
+ // Convert the font size from pt to SVG world units (px equivalent).
353
+ // text.fontSize is stored in CSS pt; 1 pt = 4/3 px at 96 dpi.
354
+ // We also bake in scaleFactor (user resize) and divide by the object scale so
355
+ // that the rendered font aligns with the world-unit dimensions above.
356
+ // The CSS transform: scale(scaleFactor) is removed from the div because the
357
+ // adjusted font-size already accounts for it – keeping both would double-scale
358
+ // the content and overflow the foreignObject.
359
+ const svgFontSizePx = (text.fontSize * (4 / 3) * (text.scaleFactor || 1) / scale).toFixed(4);
352
360
  return `<g transform="${transform}"${opacity}>
353
- <foreignObject x="0" y="0" width="${width}" height="${height}">
354
- <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: ${text.fontFamily}; font-size: ${text.fontSize}pt; color: ${fontColor}; background-color: ${bgColor || 'transparent'}; transform: scale(${text.scaleFactor}); transform-origin: top left; white-space: pre-wrap; word-wrap: break-word;">
355
- ${htmlContent}
356
- </div>
361
+ <foreignObject x="0" y="0" width="${width}" height="${height}" overflow="visible">
362
+ <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: ${text.fontFamily}; font-size: ${svgFontSizePx}px; color: ${fontColor}; background-color: ${bgColor || 'transparent'}; white-space: pre-wrap; word-wrap: break-word; width: 100%; margin: 0; padding: 0; box-sizing: border-box;">${htmlContent.trim()}</div>
357
363
  </foreignObject>
358
364
  </g>`;
359
365
  }
@@ -372,9 +378,13 @@ export class KritzelSvgExportHelper {
372
378
  const height = text.totalHeight / scale;
373
379
  const lines = this.prosemirrorToPlainTextLines(text.content);
374
380
  const resolvedLines = lines.length > 0 ? lines : [''];
375
- const resolvedFontSize = Math.max(1, text.fontSize * (text.scaleFactor || 1));
376
- const lineHeight = resolvedFontSize * 1.2;
377
- const startY = resolvedFontSize;
381
+ // Convert the font size from pt to SVG world units (px equivalent).
382
+ // text.fontSize is stored in CSS pt; 1 pt = 4/3 px at 96 dpi.
383
+ // We also bake in scaleFactor (user resize) and divide by the object scale so
384
+ // that the rendered lines align with the world-unit bounding box dimensions above.
385
+ const resolvedFontSizePx = Math.max(1, text.fontSize * (4 / 3) * (text.scaleFactor || 1) / scale);
386
+ const lineHeight = resolvedFontSizePx * 1.2;
387
+ const startY = resolvedFontSizePx;
378
388
  const bgRect = bgColor && bgColor !== 'transparent'
379
389
  ? `<rect x="0" y="0" width="${width}" height="${height}" fill="${bgColor}"/>`
380
390
  : '';
@@ -386,7 +396,7 @@ export class KritzelSvgExportHelper {
386
396
  .join('');
387
397
  return `<g transform="${transform}"${opacity}>
388
398
  ${bgRect}
389
- <text font-family="${this.escapeHtml(text.fontFamily)}" font-size="${resolvedFontSize}" fill="${fontColor}">${tspans}</text>
399
+ <text font-family="${this.escapeHtml(text.fontFamily)}" font-size="${resolvedFontSizePx}" fill="${fontColor}">${tspans}</text>
390
400
  </g>`;
391
401
  }
392
402
  /**
@@ -1,4 +1,5 @@
1
1
  export class ThemeHelper {
2
+ static appliedVariableNames = new WeakMap();
2
3
  /**
3
4
  * Converts a camelCase string to kebab-case.
4
5
  * @example ThemeHelper.camelToKebab('controlHoverBackgroundColor') → 'control-hover-background-color'
@@ -64,6 +65,13 @@ export class ThemeHelper {
64
65
  */
65
66
  static applyThemeToElement(element, theme) {
66
67
  const variables = ThemeHelper.flattenThemeToVariables(theme);
68
+ const previousVariableNames = ThemeHelper.appliedVariableNames.get(element);
69
+ for (const name of previousVariableNames ?? []) {
70
+ if (!variables.has(name)) {
71
+ element.style.removeProperty(name);
72
+ }
73
+ }
67
74
  ThemeHelper.applyVariablesToElement(element, variables);
75
+ ThemeHelper.appliedVariableNames.set(element, new Set(variables.keys()));
68
76
  }
69
77
  }
@@ -131,7 +131,9 @@ export const darkTheme = {
131
131
  },
132
132
  snap: {
133
133
  indicatorFill: 'rgba(10, 132, 255, 0.35)',
134
+ indicatorFillInactive: 'rgba(10, 132, 255, 0.2)',
134
135
  indicatorStroke: '#0A84FF',
136
+ indicatorStrokeInactive: 'rgba(255, 255, 255, 0.45)',
135
137
  lineStroke: 'rgba(255, 255, 255, 0.35)',
136
138
  },
137
139
  fontSize: {
@@ -132,7 +132,9 @@ export const lightTheme = {
132
132
  },
133
133
  snap: {
134
134
  indicatorFill: 'rgba(59, 130, 246, 0.3)',
135
+ indicatorFillInactive: 'rgba(59, 130, 246, 0.18)',
135
136
  indicatorStroke: '#007bff',
137
+ indicatorStrokeInactive: 'rgba(59, 130, 246, 0.5)',
136
138
  lineStroke: 'rgba(0, 0, 0, 0.2)',
137
139
  },
138
140
  fontSize: {
@@ -1,13 +1,13 @@
1
1
  export { g as getAssetPath, r as render, s as setAssetPath, a as setNonce, b as setPlatformOptions } from './p-B43upypT.js';
2
- export { K as KritzelBaseObject, b as KritzelLine, a as KritzelPath } from './p-Ka7OWbnk.js';
3
- export { u as APP_STATE_MIGRATIONS, A as AssetNotFoundError, D as DE_LOCALE, E as EN_LOCALE, F as FR_LOCALE, I as IndexedDBAssetProvider, s as KritzelAlignment, o as KritzelAnchorManager, n as KritzelAssetResolver, e as KritzelBaseTool, f as KritzelBrushTool, l as KritzelCursorHelper, d as KritzelCustomElement, r as KritzelCustomElementRendererRegistry, h as KritzelEraserTool, b as KritzelGroup, a as KritzelImage, i as KritzelImageTool, q as KritzelLicenseManager, g as KritzelLineTool, p as KritzelLocalizationManager, m as KritzelSelectionTool, c as KritzelShape, k as KritzelShapeTool, K as KritzelText, j as KritzelTextTool, S as ShapeType, W as WORKSPACE_MIGRATIONS, t as runMigrations } from './p-C2yi85nc.js';
2
+ export { K as KritzelBaseObject, b as KritzelLine, a as KritzelPath } from './p-0H4HOJjy.js';
3
+ export { u as APP_STATE_MIGRATIONS, A as AssetNotFoundError, D as DE_LOCALE, E as EN_LOCALE, F as FR_LOCALE, I as IndexedDBAssetProvider, s as KritzelAlignment, o as KritzelAnchorManager, n as KritzelAssetResolver, e as KritzelBaseTool, f as KritzelBrushTool, l as KritzelCursorHelper, d as KritzelCustomElement, r as KritzelCustomElementRendererRegistry, h as KritzelEraserTool, b as KritzelGroup, a as KritzelImage, i as KritzelImageTool, q as KritzelLicenseManager, g as KritzelLineTool, p as KritzelLocalizationManager, m as KritzelSelectionTool, c as KritzelShape, k as KritzelShapeTool, K as KritzelText, j as KritzelTextTool, S as ShapeType, W as WORKSPACE_MIGRATIONS, t as runMigrations } from './p-D8bHNNEm.js';
4
4
  import * as Y from 'yjs';
5
5
  import { IndexeddbPersistence } from 'y-indexeddb';
6
6
  import { WebsocketProvider } from 'y-websocket';
7
7
  import { H as HocuspocusProvider, a as HocuspocusProviderWebsocket } from './kritzel-editor.js';
8
8
  export { d as DEFAULT_ASSET_STORAGE_CONFIG, D as DEFAULT_BRUSH_CONFIG, c as DEFAULT_LINE_TOOL_CONFIG, b as DEFAULT_TEXT_CONFIG, KritzelEditor, defineCustomElement as defineCustomElementKritzelEditor } from './kritzel-editor.js';
9
9
  export { K as KritzelWorkspace, W as WORKSPACE_EXPORT_VERSION } from './p-DAwPPDMS.js';
10
- export { K as KritzelThemeManager, d as darkTheme, l as lightTheme } from './p-BmyTdQGy.js';
10
+ export { K as KritzelThemeManager, d as darkTheme, l as lightTheme } from './p-CURiiQfq.js';
11
11
  export { K as KritzelFontRegistry } from './p-DJkKlUyZ.js';
12
12
  export { C as CURRENT_APP_STATE_SCHEMA_VERSION, a as CURRENT_WORKSPACE_SCHEMA_VERSION } from './p-CW-VyJgK.js';
13
13
  export { KritzelActiveUsers, defineCustomElement as defineCustomElementKritzelActiveUsers } from './kritzel-active-users.js';
@@ -1,4 +1,4 @@
1
- import { c as KritzelAwarenessCursors$1, d as defineCustomElement$1 } from './p-Ka7OWbnk.js';
1
+ import { c as KritzelAwarenessCursors$1, d as defineCustomElement$1 } from './p-0H4HOJjy.js';
2
2
 
3
3
  const KritzelAwarenessCursors = KritzelAwarenessCursors$1;
4
4
  const defineCustomElement = defineCustomElement$1;
@@ -1,4 +1,4 @@
1
- import { K as KritzelColorPalette$1, d as defineCustomElement$1 } from './p-1nG2hxfJ.js';
1
+ import { K as KritzelColorPalette$1, d as defineCustomElement$1 } from './p-9m1cAVwG.js';
2
2
 
3
3
  const KritzelColorPalette = KritzelColorPalette$1;
4
4
  const defineCustomElement = defineCustomElement$1;
@@ -1,4 +1,4 @@
1
- import { K as KritzelColorComponent, d as defineCustomElement$1 } from './p-zd7zXld8.js';
1
+ import { K as KritzelColorComponent, d as defineCustomElement$1 } from './p-CMZ09rB1.js';
2
2
 
3
3
  const KritzelColor = KritzelColorComponent;
4
4
  const defineCustomElement = defineCustomElement$1;