kritzel-stencil 0.3.18 → 0.3.20

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 (159) hide show
  1. package/dist/cjs/index-Xav9JFHg.js +2 -2
  2. package/dist/cjs/index.cjs.js +1 -1
  3. package/dist/cjs/{kritzel-active-users_44.cjs.entry.js → kritzel-active-users_45.cjs.entry.js} +739 -310
  4. package/dist/cjs/loader.cjs.js +1 -1
  5. package/dist/cjs/{schema.constants-DrHO_CYF.js → schema.constants-IBG5VW_J.js} +116 -59
  6. package/dist/cjs/stencil.cjs.js +1 -1
  7. package/dist/collection/classes/core/core.class.js +22 -210
  8. package/dist/collection/classes/handlers/key.handler.js +1 -22
  9. package/dist/collection/classes/managers/browser-permission.manager.js +64 -0
  10. package/dist/collection/classes/managers/clipboard.manager.js +401 -0
  11. package/dist/collection/classes/objects/image.class.js +42 -0
  12. package/dist/collection/classes/tools/image-tool.class.js +1 -59
  13. package/dist/collection/collection-manifest.json +1 -0
  14. package/dist/collection/components/core/kritzel-editor/kritzel-editor.css +53 -0
  15. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +133 -53
  16. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +101 -43
  17. package/dist/collection/components/core/kritzel-watermark/kritzel-watermark.js +1 -1
  18. package/dist/collection/components/shared/kritzel-avatar/kritzel-avatar.js +3 -3
  19. package/dist/collection/components/shared/kritzel-button/kritzel-button.js +2 -2
  20. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  21. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
  22. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  23. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  24. package/dist/collection/components/shared/kritzel-input/kritzel-input.js +1 -1
  25. package/dist/collection/components/shared/kritzel-master-detail/kritzel-master-detail.js +3 -3
  26. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  27. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  28. package/dist/collection/components/shared/kritzel-numeric-input/kritzel-numeric-input.js +1 -1
  29. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +1 -1
  30. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  31. package/dist/collection/components/shared/kritzel-slide-toggle/kritzel-slide-toggle.js +1 -1
  32. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  33. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
  34. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +2 -2
  35. package/dist/collection/components/ui/kritzel-back-to-content/kritzel-back-to-content.js +1 -1
  36. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +6 -6
  37. package/dist/collection/components/ui/kritzel-current-user/kritzel-current-user.js +1 -1
  38. package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js +1 -1
  39. package/dist/collection/components/ui/kritzel-export/kritzel-export.js +1 -1
  40. package/dist/collection/components/ui/kritzel-login-dialog/kritzel-login-dialog.js +1 -1
  41. package/dist/collection/components/ui/kritzel-more-menu/kritzel-more-menu.js +1 -1
  42. package/dist/collection/components/ui/kritzel-notification-card/kritzel-notification-card.css +101 -0
  43. package/dist/collection/components/ui/kritzel-notification-card/kritzel-notification-card.js +173 -0
  44. package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +1 -1
  45. package/dist/collection/components/ui/kritzel-share-dialog/kritzel-share-dialog.js +2 -2
  46. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  47. package/dist/collection/components/ui/kritzel-zoom-panel/kritzel-zoom-panel.js +1 -1
  48. package/dist/collection/configs/default-engine-config.js +0 -1
  49. package/dist/collection/constants/version.js +1 -1
  50. package/dist/collection/interfaces/notification.interface.js +1 -0
  51. package/dist/collection/themes/dark-theme.js +36 -0
  52. package/dist/collection/themes/light-theme.js +36 -0
  53. package/dist/components/index.d.ts +2 -0
  54. package/dist/components/index.js +1 -1
  55. package/dist/components/kritzel-active-users.js +1 -1
  56. package/dist/components/kritzel-avatar.js +1 -1
  57. package/dist/components/kritzel-awareness-cursors.js +1 -1
  58. package/dist/components/kritzel-back-to-content.js +1 -1
  59. package/dist/components/kritzel-button.js +1 -1
  60. package/dist/components/kritzel-color-palette.js +1 -1
  61. package/dist/components/kritzel-color.js +1 -1
  62. package/dist/components/kritzel-controls.js +1 -1
  63. package/dist/components/kritzel-current-user-dialog.js +1 -1
  64. package/dist/components/kritzel-current-user.js +1 -1
  65. package/dist/components/kritzel-editor.js +1 -1
  66. package/dist/components/kritzel-engine.js +1 -1
  67. package/dist/components/kritzel-export.js +1 -1
  68. package/dist/components/kritzel-font-size.js +1 -1
  69. package/dist/components/kritzel-font.js +1 -1
  70. package/dist/components/kritzel-input.js +1 -1
  71. package/dist/components/kritzel-login-dialog.js +1 -1
  72. package/dist/components/kritzel-master-detail.js +1 -1
  73. package/dist/components/kritzel-menu-item.js +1 -1
  74. package/dist/components/kritzel-menu.js +1 -1
  75. package/dist/components/kritzel-more-menu.js +1 -1
  76. package/dist/components/kritzel-notification-card.d.ts +11 -0
  77. package/dist/components/kritzel-notification-card.js +1 -0
  78. package/dist/components/kritzel-numeric-input.js +1 -1
  79. package/dist/components/kritzel-opacity-slider.js +1 -1
  80. package/dist/components/kritzel-portal.js +1 -1
  81. package/dist/components/kritzel-settings.js +1 -1
  82. package/dist/components/kritzel-share-dialog.js +1 -1
  83. package/dist/components/kritzel-slide-toggle.js +1 -1
  84. package/dist/components/kritzel-split-button.js +1 -1
  85. package/dist/components/kritzel-stroke-size.js +1 -1
  86. package/dist/components/kritzel-tool-config.js +1 -1
  87. package/dist/components/kritzel-tooltip.js +1 -1
  88. package/dist/components/kritzel-utility-panel.js +1 -1
  89. package/dist/components/kritzel-watermark.js +1 -1
  90. package/dist/components/kritzel-workspace-manager.js +1 -1
  91. package/dist/components/kritzel-zoom-panel.js +1 -1
  92. package/dist/components/{p-BvRrA4hN.js → p-6Ca7Eolf.js} +1 -1
  93. package/dist/components/{p-BpnIvNvq.js → p-BO1CnEOT.js} +1 -1
  94. package/dist/components/{p-CyqRcqsO.js → p-BPoDE87Q.js} +1 -1
  95. package/dist/components/p-BX-nQAyH.js +1 -0
  96. package/dist/components/{p-DNdXJp8F.js → p-Bg0xIZiR.js} +1 -1
  97. package/dist/components/{p-DmWSRsjK.js → p-BhODnvMO.js} +1 -1
  98. package/dist/components/{p-mz3pUWW4.js → p-Bm0aKWPw.js} +1 -1
  99. package/dist/components/{p-3HxnBrCM.js → p-BrAeYciD.js} +1 -1
  100. package/dist/components/{p-f8aW1ye7.js → p-BwuemYET.js} +1 -1
  101. package/dist/components/{p-U4oawa1x.js → p-C-RiEw9X.js} +1 -1
  102. package/dist/components/{p-DdH1cKED.js → p-C1jg5IQi.js} +1 -1
  103. package/dist/components/{p-SDZNC8GF.js → p-C4-Som48.js} +1 -1
  104. package/dist/components/{p-C9HGoDHE.js → p-C5TZjIE1.js} +1 -1
  105. package/dist/components/{p-7NsK0uHu.js → p-CC0J_kEP.js} +1 -1
  106. package/dist/components/{p-CIcLzcfA.js → p-CJEIUs0x.js} +1 -1
  107. package/dist/components/{p-Bzv9Px8v.js → p-CO0eEzos.js} +1 -1
  108. package/dist/components/{p-DDkmsPpV.js → p-COdwK_mi.js} +1 -1
  109. package/dist/components/{p-CPtDfadX.js → p-COj8EVzL.js} +1 -1
  110. package/dist/components/{p-CEnEDaix.js → p-CVV-HzFR.js} +1 -1
  111. package/dist/components/{p-6RjeGuvH.js → p-Cb5YwXbC.js} +1 -1
  112. package/dist/components/{p-DX5K8xnh.js → p-CbMLbDou.js} +1 -1
  113. package/dist/components/{p-DZdgXCAx.js → p-CdkNxxut.js} +1 -1
  114. package/dist/components/{p-BKJSh8qQ.js → p-CpR9lc-o.js} +1 -1
  115. package/dist/components/p-CpzicliK.js +1 -0
  116. package/dist/components/{p-IpoC5EEY.js → p-D0RYktBV.js} +1 -1
  117. package/dist/components/{p-DI4vQRE3.js → p-D0v9WP5w.js} +1 -1
  118. package/dist/components/{p-DgmtCdnL.js → p-D28ZylHn.js} +1 -1
  119. package/dist/components/{p-F5_X4dZG.js → p-DEpnJBA3.js} +1 -1
  120. package/dist/components/{p-BCNyR5Sw.js → p-DRsD38Nl.js} +1 -1
  121. package/dist/components/p-DWhWFDcC.js +9 -0
  122. package/dist/components/{p-Dz-Ti24X.js → p-DeqALrzV.js} +1 -1
  123. package/dist/components/{p-BKvHg9cv.js → p-Ndib1Jb3.js} +1 -1
  124. package/dist/components/{p-vAeiXe6c.js → p-NwN6r0N0.js} +1 -1
  125. package/dist/components/{p-NuLP1xHe.js → p-W1ZwImdd.js} +1 -1
  126. package/dist/components/{p-Jn6TNdfe.js → p-YFR180v-.js} +1 -1
  127. package/dist/components/p-pagW8YF7.js +1 -0
  128. package/dist/components/{p-v7dxxrL5.js → p-vf28uSsc.js} +1 -1
  129. package/dist/esm/index-Dhio9uis.js +2 -2
  130. package/dist/esm/index.js +2 -2
  131. package/dist/esm/{kritzel-active-users_44.entry.js → kritzel-active-users_45.entry.js} +739 -311
  132. package/dist/esm/loader.js +1 -1
  133. package/dist/esm/{schema.constants-DchTXG3V.js → schema.constants-D35cwCYd.js} +116 -60
  134. package/dist/esm/stencil.js +1 -1
  135. package/dist/stencil/index.esm.js +1 -1
  136. package/dist/stencil/p-D35cwCYd.js +1 -0
  137. package/dist/stencil/p-e8e69a16.entry.js +9 -0
  138. package/dist/stencil/stencil.esm.js +1 -1
  139. package/dist/types/classes/core/core.class.d.ts +16 -19
  140. package/dist/types/classes/handlers/key.handler.d.ts +1 -1
  141. package/dist/types/classes/managers/browser-permission.manager.d.ts +41 -0
  142. package/dist/types/classes/managers/clipboard.manager.d.ts +86 -0
  143. package/dist/types/classes/objects/image.class.d.ts +10 -0
  144. package/dist/types/classes/tools/image-tool.class.d.ts +0 -14
  145. package/dist/types/components/core/kritzel-editor/kritzel-editor.d.ts +20 -1
  146. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +8 -4
  147. package/dist/types/components/ui/kritzel-notification-card/kritzel-notification-card.d.ts +22 -0
  148. package/dist/types/components.d.ts +83 -9
  149. package/dist/types/constants/version.d.ts +1 -1
  150. package/dist/types/interfaces/engine-state.interface.d.ts +0 -3
  151. package/dist/types/interfaces/notification.interface.d.ts +10 -0
  152. package/dist/types/interfaces/theme.interface.d.ts +43 -0
  153. package/package.json +8 -2
  154. package/readme.md +7 -111
  155. package/dist/components/p-Bc55X65h.js +0 -1
  156. package/dist/components/p-C_fKgKHu.js +0 -9
  157. package/dist/components/p-Cu9KYyoq.js +0 -1
  158. package/dist/stencil/p-86348986.entry.js +0 -9
  159. package/dist/stencil/p-DchTXG3V.js +0 -1
@@ -23,6 +23,7 @@ import { lightTheme } from "../../../themes/light-theme";
23
23
  import { darkTheme } from "../../../themes/dark-theme";
24
24
  import { KritzelSelectionGroup } from "../../../classes/objects/selection-group.class";
25
25
  import { KritzelSelectionBox } from "../../../classes/objects/selection-box.class";
26
+ import { ObjectHelper } from "../../../helpers/object.helper";
26
27
  export class KritzelEditor {
27
28
  host;
28
29
  scaleMax = ABSOLUTE_SCALE_MAX;
@@ -102,13 +103,11 @@ export class KritzelEditor {
102
103
  {
103
104
  label: 'menu.paste',
104
105
  icon: 'paste',
105
- disabled: async () => (await this.engineRef.getCopiedObjects()).length === 0,
106
106
  action: menu => this.engineRef.paste(menu.x, menu.y),
107
107
  },
108
108
  {
109
109
  label: 'menu.selectAll',
110
110
  icon: 'select-all',
111
- disabled: async () => (await this.engineRef.getObjectsInViewport()).length === 0,
112
111
  action: () => this.selectAllObjectsInViewport(),
113
112
  },
114
113
  ];
@@ -119,7 +118,6 @@ export class KritzelEditor {
119
118
  label: 'menu.paste',
120
119
  icon: 'paste',
121
120
  group: 'clipboard',
122
- disabled: async () => (await this.engineRef.getCopiedObjects()).length === 0,
123
121
  action: (menu, _) => this.engineRef.paste(menu.x, menu.y),
124
122
  },
125
123
  {
@@ -137,7 +135,6 @@ export class KritzelEditor {
137
135
  label: 'menu.align',
138
136
  icon: 'align',
139
137
  group: 'other',
140
- disabled: async () => (await this.engineRef.getSelectedObjects()).length < 2,
141
138
  children: [
142
139
  { label: 'menu.alignLeft', icon: 'align-start-vertical', action: () => this.engineRef.alignObjects(KritzelAlignment.StartHorizontal) },
143
140
  { label: 'menu.alignCenterHorizontal', icon: 'align-center-horizontal', action: () => this.engineRef.alignObjects(KritzelAlignment.CenterHorizontal) },
@@ -155,16 +152,11 @@ export class KritzelEditor {
155
152
  {
156
153
  label: 'menu.group',
157
154
  icon: 'group',
158
- disabled: async () => (await this.engineRef.getSelectedObjects()).length < 2,
159
155
  action: () => this.engineRef.group(),
160
156
  },
161
157
  {
162
158
  label: 'menu.ungroup',
163
159
  icon: 'ungroup',
164
- disabled: async () => {
165
- const selectedObjects = await this.engineRef.getSelectedObjects();
166
- return !selectedObjects.some(obj => obj.__class__ === 'KritzelGroup');
167
- },
168
160
  action: () => this.engineRef.ungroup(),
169
161
  },
170
162
  ],
@@ -241,6 +233,13 @@ export class KritzelEditor {
241
233
  shortcuts = [];
242
234
  currentIsPublic = false;
243
235
  isEditorVisible = false;
236
+ activeNotification;
237
+ isNotificationDismissing = false;
238
+ notificationDismissTimeout;
239
+ notificationDismissAnimationTimeout;
240
+ notificationDisplayDurationMs = 5000;
241
+ notificationDismissAnimationDurationMs = 180;
242
+ isNotificationHovered = false;
244
243
  onIsEngineReady(newValue) {
245
244
  if (newValue && this.isControlsReady) {
246
245
  this.checkIsReady();
@@ -503,9 +502,6 @@ export class KritzelEditor {
503
502
  async findObjects(predicate) {
504
503
  return this.engineRef.findObjects(predicate);
505
504
  }
506
- async getCopiedObjects() {
507
- return this.engineRef.getCopiedObjects();
508
- }
509
505
  async getObjectsInViewport() {
510
506
  return this.engineRef.getObjectsInViewport();
511
507
  }
@@ -521,6 +517,22 @@ export class KritzelEditor {
521
517
  async getDisplayableShortcuts() {
522
518
  return this.engineRef.getDisplayableShortcuts();
523
519
  }
520
+ /**
521
+ * Triggers a UI notification programmatically.
522
+ * @param notification - Notification payload. `id` and `timestamp` are optional.
523
+ */
524
+ async triggerNotification(notification) {
525
+ const normalizedNotification = {
526
+ ...notification,
527
+ id: notification.id ?? ObjectHelper.generateUUID(),
528
+ timestamp: notification.timestamp ?? new Date(),
529
+ };
530
+ this.clearNotificationDismissAnimationTimer();
531
+ this.isNotificationDismissing = false;
532
+ this.isNotificationHovered = false;
533
+ this.activeNotification = normalizedNotification;
534
+ this.scheduleNotificationDismiss();
535
+ }
524
536
  engineRef;
525
537
  controlsRef;
526
538
  settingsRef;
@@ -545,6 +557,10 @@ export class KritzelEditor {
545
557
  this.listenForMobileKeyboard();
546
558
  this.setOsSpecificCssVariables();
547
559
  }
560
+ disconnectedCallback() {
561
+ this.clearNotificationDismissTimer();
562
+ this.clearNotificationDismissAnimationTimer();
563
+ }
548
564
  async checkIsReady() {
549
565
  await customElements.whenDefined('kritzel-editor');
550
566
  await customElements.whenDefined('kritzel-workspace-manager');
@@ -644,6 +660,57 @@ export class KritzelEditor {
644
660
  event.stopPropagation();
645
661
  this.awarenessChange.emit(event.detail);
646
662
  }
663
+ handleNotificationsChange(event) {
664
+ event.stopPropagation();
665
+ this.clearNotificationDismissAnimationTimer();
666
+ this.isNotificationDismissing = false;
667
+ this.isNotificationHovered = false;
668
+ this.activeNotification = event.detail;
669
+ this.scheduleNotificationDismiss();
670
+ }
671
+ handleNotificationHoverChange(event) {
672
+ event.stopPropagation();
673
+ this.isNotificationHovered = event.detail;
674
+ if (this.isNotificationHovered) {
675
+ this.clearNotificationDismissTimer();
676
+ return;
677
+ }
678
+ if (this.activeNotification && !this.isNotificationDismissing) {
679
+ this.scheduleNotificationDismiss();
680
+ }
681
+ }
682
+ scheduleNotificationDismiss() {
683
+ this.clearNotificationDismissTimer();
684
+ this.notificationDismissTimeout = setTimeout(() => {
685
+ this.dismissNotification();
686
+ }, this.notificationDisplayDurationMs);
687
+ }
688
+ clearNotificationDismissTimer() {
689
+ if (this.notificationDismissTimeout) {
690
+ clearTimeout(this.notificationDismissTimeout);
691
+ this.notificationDismissTimeout = undefined;
692
+ }
693
+ }
694
+ clearNotificationDismissAnimationTimer() {
695
+ if (this.notificationDismissAnimationTimeout) {
696
+ clearTimeout(this.notificationDismissAnimationTimeout);
697
+ this.notificationDismissAnimationTimeout = undefined;
698
+ }
699
+ }
700
+ dismissNotification = () => {
701
+ this.clearNotificationDismissTimer();
702
+ this.isNotificationHovered = false;
703
+ if (!this.activeNotification || this.isNotificationDismissing) {
704
+ return;
705
+ }
706
+ this.isNotificationDismissing = true;
707
+ this.clearNotificationDismissAnimationTimer();
708
+ this.notificationDismissAnimationTimeout = setTimeout(() => {
709
+ this.activeNotification = undefined;
710
+ this.isNotificationDismissing = false;
711
+ this.notificationDismissAnimationTimeout = undefined;
712
+ }, this.notificationDismissAnimationDurationMs);
713
+ };
647
714
  handleSettingsChange(event) {
648
715
  this.scaleMin = event.detail.scaleMin;
649
716
  this.scaleMax = event.detail.scaleMax;
@@ -882,31 +949,31 @@ export class KritzelEditor {
882
949
  const isLoggedIn = this.isLoggedIn;
883
950
  const shouldShowCurrentUser = isLoggedIn;
884
951
  const shouldShowLoginButton = this.isReady && !!this.loginConfig && !isLoggedIn;
885
- return (h(Host, { key: '72238560a0f0275c506f59220277fdff7ab92c13', style: {
952
+ return (h(Host, { key: '07fe246b722ca64cfbdd8cc3474e6288c39d1128', style: {
886
953
  opacity: this.isEditorVisible ? '1' : '0',
887
954
  visibility: this.isEditorVisible ? 'visible' : 'hidden',
888
955
  transition: 'opacity 0.2s ease-in-out, visibility 0.2s ease-in-out',
889
- } }, h("div", { key: '14fd50ad857199f3b6be708fc4263aa2e69067de', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: '8788631c804770c67110c7e7906fe2034438ef9b', 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: '1840374d7353af2b050822dcd9c54be46e326278', visible: this.isBackToContentButtonVisible, text: this.resolvedTerms['backToContent.label'] ?? 'Back to content', onBackToContent: () => this.backToContent() })), h("kritzel-engine", { key: 'cdde0b65c811ee28fb4266afb96005c1fed24323', ref: el => {
956
+ } }, h("div", { key: '2d0db1e78161edb061779b5875114e901f8e79f8', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: '8fee1d57b0b868b673c41015453fd39a19f8cb43', 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: 'f2a0305bf73bd14d7522974b7956a289a0a44258', visible: this.isBackToContentButtonVisible, text: this.resolvedTerms['backToContent.label'] ?? 'Back to content', onBackToContent: () => this.backToContent() })), this.activeNotification && (h("div", { key: '918fe80193999ec072042500cd1eaf3670f5ed3c', class: "top-center-notification-layer", role: "presentation" }, h("div", { key: '0a5609b2b32f9f04bac03618803c14d9a891742c', class: { 'top-center-notification-container': true, 'is-dismissing': this.isNotificationDismissing }, role: "presentation" }, h("kritzel-notification-card", { key: 'eb1ff7aeaee3a237baf3ba2d96a10ba03a8bf7fb', notification: this.activeNotification, locale: this.locale, onDismiss: this.dismissNotification, onHoverChange: event => this.handleNotificationHoverChange(event) })))), h("kritzel-engine", { key: '8783a87101f308029482ced926a8d2163aae1428', ref: el => {
890
957
  if (el) {
891
958
  this.engineRef = el;
892
959
  }
893
- }, 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) }), h("kritzel-controls", { key: '9b91d0cc3b3afba8894bd852bac911178c0ca82d', visible: this.isControlsVisible, class: { 'keyboard-open': this.isVirtualKeyboardOpen }, ref: el => {
960
+ }, 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: '73a94f6baef4e0e26be4540ecd44d24588f5761b', visible: this.isControlsVisible, class: { 'keyboard-open': this.isVirtualKeyboardOpen }, ref: el => {
894
961
  if (el) {
895
962
  this.controlsRef = el;
896
963
  }
897
- }, controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.theme, terms: this.resolvedTerms, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: '01f63c615eec68696532926b6d1efe2443e2d46a', class: "bottom-left-buttons" }, h("kritzel-zoom-panel", { key: '73a1fc5b0892b88d6c0a7c5debb42b7bb3f03a84', visible: this.isZoomPanelVisible, disabled: !this.isZoomingEnabled, zoomPercent: this.currentZoomPercent, terms: this.resolvedTerms, onZoomIn: () => this.zoomIn(), onZoomOut: () => this.zoomOut() })), h("div", { key: '94776978ba9dd68b2c5f62b141c074852585a1cd', class: "top-right-buttons" }, h("kritzel-settings", { key: '665a74511f8e511602d4d3437927e03af2c3c01c', ref: el => {
964
+ }, controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState ?? undefined, theme: this.theme, terms: this.resolvedTerms, onIsControlsReady: () => (this.isControlsReady = true) }), h("div", { key: 'b0653098e5e15b81f453a7f3c2b27f137eb38f2e', class: "bottom-left-buttons" }, h("kritzel-zoom-panel", { key: '33384aeb6bd32d17fb428bd9ed48e5d588ac60c0', visible: this.isZoomPanelVisible, disabled: !this.isZoomingEnabled, zoomPercent: this.currentZoomPercent, terms: this.resolvedTerms, onZoomIn: () => this.zoomIn(), onZoomOut: () => this.zoomOut() })), h("div", { key: '37bed04c062b63a99c930362fb8ce30b58ed8872', class: "top-right-buttons" }, h("kritzel-settings", { key: '4e339573e3d8d48bc9803b9dd40a549bd056d147', ref: el => {
898
965
  if (el) {
899
966
  this.settingsRef = el;
900
967
  }
901
- }, 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: '6b52127d4634d92c3e7410a084f37970bd8c84e7', ref: el => {
968
+ }, 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: '2a726ea5b56cd36ecf92bbab17d69d04baf73009', ref: el => {
902
969
  if (el) {
903
970
  this.exportRef = el;
904
971
  }
905
- }, 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: '002a01d361137f867efc2a0c91a6c0e9619d4359', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: '6a9e0ad77997cbb10981d58afb176a7f7f4938aa', user: this.user, terms: this.resolvedTerms }), shouldShowLoginButton && (h("kritzel-button", { key: '5ba13725105d0af68c51fec455569ac7bbeaecde', onButtonClick: () => this.loginDialogRef?.open() }, this.resolvedTerms['login.dialogTitle'] ?? 'Sign in')), h("kritzel-more-menu", { key: 'f392a518fef108b395eb6d6681af9e06827e3780', items: this.moreMenuItems, visible: this.isMoreMenuVisible, terms: this.resolvedTerms }), h("kritzel-share-dialog", { key: '845a3153f5fd9b419f7d0b81be85f5e1ea0051cc', ref: el => {
972
+ }, 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: '26d9bbf922b1d4158a0875ba42e7b598011973d5', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: '8179cc3e940c9fd8d97978af2bf52543f661a0fd', user: this.user, terms: this.resolvedTerms }), shouldShowLoginButton && (h("kritzel-button", { key: 'd4aa5bc108043eb103754d14f1b5df42ab73f8c4', onButtonClick: () => this.loginDialogRef?.open() }, this.resolvedTerms['login.dialogTitle'] ?? 'Sign in')), h("kritzel-more-menu", { key: '17ac617bde379d7484dbeed39ac7168349b634a1', items: this.moreMenuItems, visible: this.isMoreMenuVisible, terms: this.resolvedTerms }), h("kritzel-share-dialog", { key: '881729b36d34ef3ac3d87fc51ad828fc20fa36cd', ref: el => {
906
973
  if (el) {
907
974
  this.shareDialogRef = el;
908
975
  }
909
- }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '5a7b149d8e2c2acbda3d7818d8576d8ad249ef23', ref: el => {
976
+ }, isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, terms: this.resolvedTerms, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '5e34b647ac4c3e18981042d765b4d443422c5db3', ref: el => {
910
977
  if (el) {
911
978
  this.loginDialogRef = el;
912
979
  }
@@ -1186,7 +1253,7 @@ export class KritzelEditor {
1186
1253
  },
1187
1254
  "getter": false,
1188
1255
  "setter": false,
1189
- "defaultValue": "[\n {\n label: 'menu.paste',\n icon: 'paste',\n disabled: async () => (await this.engineRef.getCopiedObjects()).length === 0,\n action: menu => this.engineRef.paste(menu.x, menu.y),\n },\n {\n label: 'menu.selectAll',\n icon: 'select-all',\n disabled: async () => (await this.engineRef.getObjectsInViewport()).length === 0,\n action: () => this.selectAllObjectsInViewport(),\n },\n ]"
1256
+ "defaultValue": "[\n {\n label: 'menu.paste',\n icon: 'paste',\n action: menu => this.engineRef.paste(menu.x, menu.y),\n },\n {\n label: 'menu.selectAll',\n icon: 'select-all',\n action: () => this.selectAllObjectsInViewport(),\n },\n ]"
1190
1257
  },
1191
1258
  "objectContextMenuItems": {
1192
1259
  "type": "unknown",
@@ -1211,7 +1278,7 @@ export class KritzelEditor {
1211
1278
  },
1212
1279
  "getter": false,
1213
1280
  "setter": false,
1214
- "defaultValue": "[\n { label: 'menu.copy', icon: 'copy', group: 'clipboard', action: () => this.engineRef.copy() },\n { label: 'menu.cut', icon: 'cut', group: 'clipboard', action: () => this.engineRef.cut() },\n {\n label: 'menu.paste',\n icon: 'paste',\n group: 'clipboard',\n disabled: async () => (await this.engineRef.getCopiedObjects()).length === 0,\n action: (menu, _) => this.engineRef.paste(menu.x, menu.y),\n },\n {\n label: 'menu.order',\n icon: 'ordering',\n group: 'other',\n children: [\n { label: 'menu.bringToFront', icon: 'bring-to-front', action: () => this.engineRef.bringToFront() },\n { label: 'menu.sendToBack', icon: 'send-to-back', action: () => this.engineRef.sendToBack() },\n { label: 'menu.moveUp', icon: 'arrow-up-from-dot', action: () => this.engineRef.bringForward() },\n { label: 'menu.moveDown', icon: 'arrow-down-from-dot', action: () => this.engineRef.sendBackward() },\n ],\n },\n {\n label: 'menu.align',\n icon: 'align',\n group: 'other',\n disabled: async () => (await this.engineRef.getSelectedObjects()).length < 2,\n children: [\n { label: 'menu.alignLeft', icon: 'align-start-vertical', action: () => this.engineRef.alignObjects(KritzelAlignment.StartHorizontal) },\n { label: 'menu.alignCenterHorizontal', icon: 'align-center-horizontal', action: () => this.engineRef.alignObjects(KritzelAlignment.CenterHorizontal) },\n { label: 'menu.alignRight', icon: 'align-end-vertical', action: () => this.engineRef.alignObjects(KritzelAlignment.EndHorizontal) },\n { label: 'menu.alignTop', icon: 'align-start-horizontal', action: () => this.engineRef.alignObjects(KritzelAlignment.StartVertical) },\n { label: 'menu.alignCenterVertical', icon: 'align-center-vertical', action: () => this.engineRef.alignObjects(KritzelAlignment.CenterVertical) },\n { label: 'menu.alignBottom', icon: 'align-end-horizontal', action: () => this.engineRef.alignObjects(KritzelAlignment.EndVertical) },\n ],\n },\n {\n label: 'menu.group',\n icon: 'group',\n group: 'other',\n children: [\n {\n label: 'menu.group',\n icon: 'group',\n disabled: async () => (await this.engineRef.getSelectedObjects()).length < 2,\n action: () => this.engineRef.group(),\n },\n {\n label: 'menu.ungroup',\n icon: 'ungroup',\n disabled: async () => {\n const selectedObjects = await this.engineRef.getSelectedObjects();\n return !selectedObjects.some(obj => obj.__class__ === 'KritzelGroup');\n },\n action: () => this.engineRef.ungroup(),\n },\n ],\n },\n {\n label: 'menu.export',\n icon: 'download',\n group: 'export',\n children: [\n { label: 'menu.exportAsSvg', icon: 'download', action: () => this.engineRef.exportSelectedObjectsAsSvg() },\n { label: 'menu.exportAsPng', icon: 'download', action: () => this.engineRef.exportSelectedObjectsAsPng() },\n ],\n },\n { label: 'menu.delete', icon: 'delete', group: 'edit', action: () => this.engineRef.delete() },\n ]"
1281
+ "defaultValue": "[\n { label: 'menu.copy', icon: 'copy', group: 'clipboard', action: () => this.engineRef.copy() },\n { label: 'menu.cut', icon: 'cut', group: 'clipboard', action: () => this.engineRef.cut() },\n {\n label: 'menu.paste',\n icon: 'paste',\n group: 'clipboard',\n action: (menu, _) => this.engineRef.paste(menu.x, menu.y),\n },\n {\n label: 'menu.order',\n icon: 'ordering',\n group: 'other',\n children: [\n { label: 'menu.bringToFront', icon: 'bring-to-front', action: () => this.engineRef.bringToFront() },\n { label: 'menu.sendToBack', icon: 'send-to-back', action: () => this.engineRef.sendToBack() },\n { label: 'menu.moveUp', icon: 'arrow-up-from-dot', action: () => this.engineRef.bringForward() },\n { label: 'menu.moveDown', icon: 'arrow-down-from-dot', action: () => this.engineRef.sendBackward() },\n ],\n },\n {\n label: 'menu.align',\n icon: 'align',\n group: 'other',\n children: [\n { label: 'menu.alignLeft', icon: 'align-start-vertical', action: () => this.engineRef.alignObjects(KritzelAlignment.StartHorizontal) },\n { label: 'menu.alignCenterHorizontal', icon: 'align-center-horizontal', action: () => this.engineRef.alignObjects(KritzelAlignment.CenterHorizontal) },\n { label: 'menu.alignRight', icon: 'align-end-vertical', action: () => this.engineRef.alignObjects(KritzelAlignment.EndHorizontal) },\n { label: 'menu.alignTop', icon: 'align-start-horizontal', action: () => this.engineRef.alignObjects(KritzelAlignment.StartVertical) },\n { label: 'menu.alignCenterVertical', icon: 'align-center-vertical', action: () => this.engineRef.alignObjects(KritzelAlignment.CenterVertical) },\n { label: 'menu.alignBottom', icon: 'align-end-horizontal', action: () => this.engineRef.alignObjects(KritzelAlignment.EndVertical) },\n ],\n },\n {\n label: 'menu.group',\n icon: 'group',\n group: 'other',\n children: [\n {\n label: 'menu.group',\n icon: 'group',\n action: () => this.engineRef.group(),\n },\n {\n label: 'menu.ungroup',\n icon: 'ungroup',\n action: () => this.engineRef.ungroup(),\n },\n ],\n },\n {\n label: 'menu.export',\n icon: 'download',\n group: 'export',\n children: [\n { label: 'menu.exportAsSvg', icon: 'download', action: () => this.engineRef.exportSelectedObjectsAsSvg() },\n { label: 'menu.exportAsPng', icon: 'download', action: () => this.engineRef.exportSelectedObjectsAsPng() },\n ],\n },\n { label: 'menu.delete', icon: 'delete', group: 'edit', action: () => this.engineRef.delete() },\n ]"
1215
1282
  },
1216
1283
  "themes": {
1217
1284
  "type": "unknown",
@@ -1715,7 +1782,9 @@ export class KritzelEditor {
1715
1782
  "currentZoomPercent": {},
1716
1783
  "shortcuts": {},
1717
1784
  "currentIsPublic": {},
1718
- "isEditorVisible": {}
1785
+ "isEditorVisible": {},
1786
+ "activeNotification": {},
1787
+ "isNotificationDismissing": {}
1719
1788
  };
1720
1789
  }
1721
1790
  static get events() {
@@ -3475,37 +3544,6 @@ export class KritzelEditor {
3475
3544
  "tags": []
3476
3545
  }
3477
3546
  },
3478
- "getCopiedObjects": {
3479
- "complexType": {
3480
- "signature": "() => Promise<KritzelBaseObject[]>",
3481
- "parameters": [],
3482
- "references": {
3483
- "Promise": {
3484
- "location": "global",
3485
- "id": "global::Promise"
3486
- },
3487
- "KritzelBaseObject": {
3488
- "location": "import",
3489
- "path": "../../../classes/objects/base-object.class",
3490
- "id": "src/classes/objects/base-object.class.ts::KritzelBaseObject",
3491
- "referenceLocation": "KritzelBaseObject"
3492
- },
3493
- "HTMLElement": {
3494
- "location": "global",
3495
- "id": "global::HTMLElement"
3496
- },
3497
- "SVGElement": {
3498
- "location": "global",
3499
- "id": "global::SVGElement"
3500
- }
3501
- },
3502
- "return": "Promise<KritzelBaseObject<HTMLElement | SVGElement>[]>"
3503
- },
3504
- "docs": {
3505
- "text": "",
3506
- "tags": []
3507
- }
3508
- },
3509
3547
  "getObjectsInViewport": {
3510
3548
  "complexType": {
3511
3549
  "signature": "() => Promise<KritzelBaseObject[]>",
@@ -3619,6 +3657,48 @@ export class KritzelEditor {
3619
3657
  "tags": []
3620
3658
  }
3621
3659
  },
3660
+ "triggerNotification": {
3661
+ "complexType": {
3662
+ "signature": "(notification: Omit<KritzelNotification, \"id\"> & Partial<Pick<KritzelNotification, \"id\" | \"timestamp\">>) => Promise<void>",
3663
+ "parameters": [{
3664
+ "name": "notification",
3665
+ "type": "Omit<KritzelNotification, \"id\"> & Partial<Pick<KritzelNotification, \"id\" | \"timestamp\">>",
3666
+ "docs": "- Notification payload. `id` and `timestamp` are optional."
3667
+ }],
3668
+ "references": {
3669
+ "Promise": {
3670
+ "location": "global",
3671
+ "id": "global::Promise"
3672
+ },
3673
+ "Omit": {
3674
+ "location": "global",
3675
+ "id": "global::Omit"
3676
+ },
3677
+ "KritzelNotification": {
3678
+ "location": "import",
3679
+ "path": "../../../interfaces/notification.interface",
3680
+ "id": "src/interfaces/notification.interface.ts::KritzelNotification",
3681
+ "referenceLocation": "KritzelNotification"
3682
+ },
3683
+ "Partial": {
3684
+ "location": "global",
3685
+ "id": "global::Partial"
3686
+ },
3687
+ "Pick": {
3688
+ "location": "global",
3689
+ "id": "global::Pick"
3690
+ }
3691
+ },
3692
+ "return": "Promise<void>"
3693
+ },
3694
+ "docs": {
3695
+ "text": "Triggers a UI notification programmatically.",
3696
+ "tags": [{
3697
+ "name": "param",
3698
+ "text": "notification - Notification payload. `id` and `timestamp` are optional."
3699
+ }]
3700
+ }
3701
+ },
3622
3702
  "openLoginDialog": {
3623
3703
  "complexType": {
3624
3704
  "signature": "() => Promise<void>",
@@ -245,6 +245,8 @@ export class KritzelEngine {
245
245
  viewportChange;
246
246
  /** Emitted when the awareness state changes (remote user cursors, presence). */
247
247
  awarenessChange;
248
+ /** Emitted when notifications change. */
249
+ notificationsChange;
248
250
  forceUpdate = 0;
249
251
  /** Manually triggers the `objectsSelectionChange` event. */
250
252
  async triggerSelectionChange() {
@@ -359,19 +361,56 @@ export class KritzelEngine {
359
361
  this.viewport.handleResize();
360
362
  }
361
363
  handleKeyDown(ev) {
362
- if (this.core.store.isDisabled || !this.shouldHandleKeyboardEvent(ev)) {
364
+ if (this.core.store.isDisabled || !this.shouldHandleGlobalShortcutEvent(ev)) {
363
365
  return;
364
366
  }
365
367
  this.keyHandler.handleKeyDown(ev);
366
368
  }
367
369
  handleKeyUp(ev) {
368
- if (this.core.store.isDisabled || !this.shouldHandleKeyboardEvent(ev)) {
370
+ if (this.core.store.isDisabled || !this.shouldHandleGlobalShortcutEvent(ev)) {
369
371
  return;
370
372
  }
371
373
  this.keyHandler.handleKeyUp(ev);
372
374
  }
375
+ handleCopy(ev) {
376
+ if (this.core.store.isDisabled || !this.shouldHandleGlobalShortcutEvent(ev)) {
377
+ return;
378
+ }
379
+ if (!this.core.store.selectionGroup || this.core.store.activeText || this.core.store.activeShape) {
380
+ return;
381
+ }
382
+ if (ev.cancelable) {
383
+ ev.preventDefault();
384
+ }
385
+ this.core.clipboardManager.copy(ev);
386
+ this.core.rerender();
387
+ }
388
+ handleCut(ev) {
389
+ if (this.core.store.isDisabled || !this.shouldHandleGlobalShortcutEvent(ev)) {
390
+ return;
391
+ }
392
+ if (!this.core.store.selectionGroup || this.core.store.activeText || this.core.store.activeShape) {
393
+ return;
394
+ }
395
+ if (ev.cancelable) {
396
+ ev.preventDefault();
397
+ }
398
+ this.core.clipboardManager.cut(ev);
399
+ }
400
+ handlePaste(ev) {
401
+ if (this.core.store.isDisabled || !this.shouldHandleGlobalShortcutEvent(ev)) {
402
+ return;
403
+ }
404
+ if (this.core.store.activeText || this.core.store.activeShape) {
405
+ return;
406
+ }
407
+ if (ev.cancelable) {
408
+ ev.preventDefault();
409
+ }
410
+ void this.core.clipboardManager.paste(undefined, undefined, ev);
411
+ }
373
412
  /**
374
- * Determines if the engine should handle a keyboard event.
413
+ * Determines if the engine should handle a keyboard/clipboard shortcut event.
375
414
  * Returns true only if:
376
415
  * - The event originates from within the engine component, OR
377
416
  * - The event target is not an interactive element that needs keyboard input
@@ -379,7 +418,7 @@ export class KritzelEngine {
379
418
  * This prevents conflicts with inputs, textareas, dialogs, or other
380
419
  * interactive elements that need their own keyboard handling.
381
420
  */
382
- shouldHandleKeyboardEvent(ev) {
421
+ shouldHandleGlobalShortcutEvent(ev) {
383
422
  const path = ev.composedPath();
384
423
  // Check if event path includes our host - works reliably with Shadow DOM
385
424
  if (path.includes(this.host)) {
@@ -475,11 +514,11 @@ export class KritzelEngine {
475
514
  }
476
515
  /** Copies the currently selected objects to the internal clipboard. */
477
516
  async copy() {
478
- this.core.copy();
517
+ this.core.clipboardManager.copy();
479
518
  }
480
519
  /** Cuts the currently selected objects to the internal clipboard (deletes them from the canvas). */
481
520
  async cut() {
482
- this.core.cut();
521
+ this.core.clipboardManager.cut();
483
522
  }
484
523
  /**
485
524
  * Pastes previously copied objects at the specified world coordinates.
@@ -487,7 +526,16 @@ export class KritzelEngine {
487
526
  * @param y - Y position in world coordinates.
488
527
  */
489
528
  async paste(x, y) {
490
- this.core.paste(x, y);
529
+ const hasPermission = await this.core.permissionManager.requestPermission('clipboard-read');
530
+ if (hasPermission !== 'granted') {
531
+ this.notificationsChange.emit({
532
+ type: 'error',
533
+ message: 'Clipboard read permission is required to paste content. Please grant permission and try again.',
534
+ id: ObjectHelper.generateUUID(),
535
+ });
536
+ return;
537
+ }
538
+ this.core.clipboardManager.paste(x, y);
491
539
  }
492
540
  /**
493
541
  * Moves an object one layer forward in the z-order.
@@ -978,10 +1026,6 @@ export class KritzelEngine {
978
1026
  async worldToScreen(x, y) {
979
1027
  return this.viewport.worldToScreen(x, y);
980
1028
  }
981
- /** Returns the objects currently stored in the internal clipboard from a prior `copy()` call. */
982
- async getCopiedObjects() {
983
- return this.core.store.state.copiedObjects || [];
984
- }
985
1029
  /** Returns all objects currently visible within the viewport bounds. */
986
1030
  async getObjectsInViewport() {
987
1031
  return this.core.getObjectsInViewport();
@@ -1901,7 +1945,6 @@ export class KritzelEngine {
1901
1945
  this.core.resetActiveShape();
1902
1946
  }
1903
1947
  this.core.store.state.skipContextMenu = false;
1904
- this.core.store.state.copiedObjects = undefined;
1905
1948
  if (activeTool) {
1906
1949
  this.activeToolChange.emit(activeTool);
1907
1950
  }
@@ -3317,6 +3360,28 @@ export class KritzelEngine {
3317
3360
  }
3318
3361
  }
3319
3362
  }
3363
+ }, {
3364
+ "method": "notificationsChange",
3365
+ "name": "notificationsChange",
3366
+ "bubbles": true,
3367
+ "cancelable": true,
3368
+ "composed": true,
3369
+ "docs": {
3370
+ "tags": [],
3371
+ "text": "Emitted when notifications change."
3372
+ },
3373
+ "complexType": {
3374
+ "original": "KritzelNotification",
3375
+ "resolved": "KritzelNotification",
3376
+ "references": {
3377
+ "KritzelNotification": {
3378
+ "location": "import",
3379
+ "path": "../../../interfaces/notification.interface",
3380
+ "id": "src/interfaces/notification.interface.ts::KritzelNotification",
3381
+ "referenceLocation": "KritzelNotification"
3382
+ }
3383
+ }
3384
+ }
3320
3385
  }];
3321
3386
  }
3322
3387
  static get methods() {
@@ -3564,6 +3629,12 @@ export class KritzelEngine {
3564
3629
  "Promise": {
3565
3630
  "location": "global",
3566
3631
  "id": "global::Promise"
3632
+ },
3633
+ "KritzelNotification": {
3634
+ "location": "import",
3635
+ "path": "../../../interfaces/notification.interface",
3636
+ "id": "src/interfaces/notification.interface.ts::KritzelNotification",
3637
+ "referenceLocation": "KritzelNotification"
3567
3638
  }
3568
3639
  },
3569
3640
  "return": "Promise<void>"
@@ -4750,37 +4821,6 @@ export class KritzelEngine {
4750
4821
  }]
4751
4822
  }
4752
4823
  },
4753
- "getCopiedObjects": {
4754
- "complexType": {
4755
- "signature": "() => Promise<KritzelBaseObject[]>",
4756
- "parameters": [],
4757
- "references": {
4758
- "Promise": {
4759
- "location": "global",
4760
- "id": "global::Promise"
4761
- },
4762
- "KritzelBaseObject": {
4763
- "location": "import",
4764
- "path": "../../../classes/objects/base-object.class",
4765
- "id": "src/classes/objects/base-object.class.ts::KritzelBaseObject",
4766
- "referenceLocation": "KritzelBaseObject"
4767
- },
4768
- "HTMLElement": {
4769
- "location": "global",
4770
- "id": "global::HTMLElement"
4771
- },
4772
- "SVGElement": {
4773
- "location": "global",
4774
- "id": "global::SVGElement"
4775
- }
4776
- },
4777
- "return": "Promise<KritzelBaseObject<HTMLElement | SVGElement>[]>"
4778
- },
4779
- "docs": {
4780
- "text": "Returns the objects currently stored in the internal clipboard from a prior `copy()` call.",
4781
- "tags": []
4782
- }
4783
- },
4784
4824
  "getObjectsInViewport": {
4785
4825
  "complexType": {
4786
4826
  "signature": "() => Promise<KritzelBaseObject[]>",
@@ -5764,6 +5804,24 @@ export class KritzelEngine {
5764
5804
  "target": "window",
5765
5805
  "capture": false,
5766
5806
  "passive": false
5807
+ }, {
5808
+ "name": "copy",
5809
+ "method": "handleCopy",
5810
+ "target": "window",
5811
+ "capture": false,
5812
+ "passive": false
5813
+ }, {
5814
+ "name": "cut",
5815
+ "method": "handleCut",
5816
+ "target": "window",
5817
+ "capture": false,
5818
+ "passive": false
5819
+ }, {
5820
+ "name": "paste",
5821
+ "method": "handlePaste",
5822
+ "target": "window",
5823
+ "capture": false,
5824
+ "passive": false
5767
5825
  }, {
5768
5826
  "name": "dblclick",
5769
5827
  "method": "preventDoubleTapZoomOnTouchDevices",
@@ -19,7 +19,7 @@ export class KritzelWatermark {
19
19
  label;
20
20
  render() {
21
21
  const label = this.label ?? this.core.localizationManager.translate('watermark.poweredBy');
22
- return (h(Host, { key: '7ec2a304ac711a4d2a54edef317eef9f4aad1d47' }, h("a", { key: '365d588727bbc360fd47bc9a3fbd66155770d32f', class: "watermark-link", href: KRITZEL_WEBSITE_URL, target: "_blank", rel: "noopener noreferrer", part: "watermark", onPointerDown: (ev) => ev.stopPropagation(), onPointerUp: (ev) => ev.stopPropagation() }, label)));
22
+ return (h(Host, { key: '975534180d82064e38fa0289a2955e97a4ca0a08' }, h("a", { key: 'c59723d1f5bf3089256c0b610d0574bfc385ef85', class: "watermark-link", href: KRITZEL_WEBSITE_URL, target: "_blank", rel: "noopener noreferrer", part: "watermark", onPointerDown: (ev) => ev.stopPropagation(), onPointerUp: (ev) => ev.stopPropagation() }, label)));
23
23
  }
24
24
  static get is() { return "kritzel-watermark"; }
25
25
  static get encapsulation() { return "shadow"; }
@@ -81,16 +81,16 @@ export class KritzelAvatar {
81
81
  height: `${this.size}px`,
82
82
  fontSize: `${Math.round(this.size * 0.4)}px`,
83
83
  };
84
- return (h(Host, { key: '0d372a5443f41835c2e8e5b33b58bcb6c1292e89', style: containerStyles, class: {
84
+ return (h(Host, { key: '4bdbe2c1d5d3a1708189166498ae4e36c19c46ff', style: containerStyles, class: {
85
85
  'has-image': !!showImage,
86
86
  'has-initials': !!showInitials,
87
87
  'has-default': !!showDefaultIcon,
88
- }, role: "img", "aria-label": this.getDisplayName() || 'User avatar' }, showImage && (h("img", { key: 'deb5f12115dd28b4b4ab2157cb2bbc9e48bb2a3f', src: imageUrl, alt: "", class: "avatar-image", ref: (el) => {
88
+ }, role: "img", "aria-label": this.getDisplayName() || 'User avatar' }, showImage && (h("img", { key: '55c3a13c9d514ea700c6b1a9339cd0dff1828b2b', src: imageUrl, alt: "", class: "avatar-image", ref: (el) => {
89
89
  if (el) {
90
90
  el.referrerPolicy = 'no-referrer';
91
91
  el.crossOrigin = 'anonymous';
92
92
  }
93
- }, onError: this.handleImageError })), showInitials && (h("span", { key: '6fc12f2d32923f33df5a8ec743d51434f67e6934', class: "avatar-initials", style: { backgroundColor: this.getBackgroundColor() } }, initials)), showDefaultIcon && (h("span", { key: 'd803b385f7eaa0659d7452231bfd46b1634f204d', class: "avatar-default" }, h("svg", { key: 'a3275ab33f58440abcd15c296d9c027dfab6495c', viewBox: "0 0 24 24", fill: "currentColor" }, h("path", { key: '31ac7e63fe6b1778c3d0cbd71b5c85791b79d084', d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" }))))));
93
+ }, onError: this.handleImageError })), showInitials && (h("span", { key: 'b60cbe2b94ea1d2cd2a09d10c928290fc1469a4a', class: "avatar-initials", style: { backgroundColor: this.getBackgroundColor() } }, initials)), showDefaultIcon && (h("span", { key: 'e8a0f49952b5b7e5e4a1fe3d67e68a56081d5e4c', class: "avatar-default" }, h("svg", { key: '1fdc256fd53aa5c866bb0e81fdd40393c8a7d69d', viewBox: "0 0 24 24", fill: "currentColor" }, h("path", { key: '8f3a5a6775154e46a2f331534fe75671b9ad081a', d: "M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" }))))));
94
94
  }
95
95
  static get is() { return "kritzel-avatar"; }
96
96
  static get encapsulation() { return "shadow"; }
@@ -15,11 +15,11 @@ export class KritzelButton {
15
15
  this.buttonClick.emit();
16
16
  };
17
17
  render() {
18
- return (h(Host, { key: 'aa366907b97e648ac73182a43d099baf457e0ba3' }, h("button", { key: 'e80875396a791606461b1a20e0f7e1c896f54073', type: this.type, class: {
18
+ return (h(Host, { key: '0de2b5dcf75de9f5b7de0c6b876bd18733bf21b9' }, h("button", { key: '5ab9d80167c6b90add7779d4b1c3ebb80fafaed5', type: this.type, class: {
19
19
  'kritzel-button': true,
20
20
  [this.variant]: true,
21
21
  'disabled': this.disabled,
22
- }, disabled: this.disabled, onClick: this.handleClick }, h("slot", { key: '8f4e57a73837adfbf91f678c54558fe4c737aa91' }))));
22
+ }, disabled: this.disabled, onClick: this.handleClick }, h("slot", { key: 'dc105f7d1f87aa55c314d942a1cc6522b272526a' }))));
23
23
  }
24
24
  static get is() { return "kritzel-button"; }
25
25
  static get encapsulation() { return "shadow"; }
@@ -38,13 +38,13 @@ export class KritzelColorComponent {
38
38
  render() {
39
39
  const resolvedColor = this.resolveColor();
40
40
  const isColorVeryLight = this.isLightColor(resolvedColor);
41
- return (h(Host, { key: '21ce0ecb616266ec4953468ae9bbe0b72259c2da' }, h("div", { key: '02c8e8684b3b0819755efd1f160bdee662428ddd', class: "checkerboard-bg", style: {
41
+ return (h(Host, { key: '0d56c7ad01eddd523ebcf11a32e68367c96090bd' }, h("div", { key: '0bef4cb73804b98e6eea6b3cf006c19c8786bdb9', class: "checkerboard-bg", style: {
42
42
  width: `${this.size}px`,
43
43
  height: `${this.size}px`,
44
44
  borderRadius: '50%',
45
45
  display: 'inline-block',
46
46
  position: 'relative',
47
- } }, h("div", { key: '0063e9a701994869adbd3022b5ace542e85dfd95', class: {
47
+ } }, h("div", { key: '5be9714c3ddd56ae77c2870333ff97a59b58bd45', class: {
48
48
  'color-circle': true,
49
49
  'white': isColorVeryLight,
50
50
  }, style: {