kritzel-stencil 0.1.73 → 0.1.75

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 (188) hide show
  1. package/dist/cjs/index-Dc7LOVhs.js +2 -2
  2. package/dist/cjs/index.cjs.js +131 -86
  3. package/dist/cjs/{kritzel-active-users_41.cjs.entry.js → kritzel-active-users_42.cjs.entry.js} +593 -177
  4. package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/stencil.cjs.js +1 -1
  7. package/dist/cjs/{workspace.migrations-DcwqsqPC.js → workspace.migrations-DkmVO6dE.js} +164 -49
  8. package/dist/collection/classes/core/core.class.js +9 -1
  9. package/dist/collection/classes/core/store.class.js +20 -6
  10. package/dist/collection/classes/core/viewport.class.js +9 -3
  11. package/dist/collection/classes/handlers/selection.handler.js +15 -2
  12. package/dist/collection/classes/managers/anchor.manager.js +101 -44
  13. package/dist/collection/classes/objects/base-object.class.js +2 -0
  14. package/dist/collection/classes/objects/custom-element.class.js +1 -0
  15. package/dist/collection/classes/objects/group.class.js +1 -0
  16. package/dist/collection/classes/objects/image.class.js +1 -0
  17. package/dist/collection/classes/objects/line.class.js +1 -0
  18. package/dist/collection/classes/objects/path.class.js +1 -0
  19. package/dist/collection/classes/objects/selection-box.class.js +1 -0
  20. package/dist/collection/classes/objects/selection-group.class.js +13 -1
  21. package/dist/collection/classes/objects/shape.class.js +1 -0
  22. package/dist/collection/classes/objects/text.class.js +1 -0
  23. package/dist/collection/classes/providers/broadcast-sync-provider.class.js +5 -0
  24. package/dist/collection/classes/providers/hocuspocus-sync-provider.class.js +120 -85
  25. package/dist/collection/classes/providers/indexeddb-sync-provider.class.js +5 -0
  26. package/dist/collection/classes/providers/websocket-sync-provider.class.js +5 -0
  27. package/dist/collection/classes/structures/app-state-map.structure.js +15 -4
  28. package/dist/collection/classes/structures/object-map.structure.js +85 -7
  29. package/dist/collection/classes/tools/brush-tool.class.js +4 -0
  30. package/dist/collection/classes/tools/line-tool.class.js +4 -0
  31. package/dist/collection/classes/tools/shape-tool.class.js +2 -0
  32. package/dist/collection/collection-manifest.json +3 -2
  33. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.css +110 -0
  34. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js +347 -0
  35. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
  36. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +3 -3
  37. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +150 -109
  38. package/dist/collection/components/shared/kritzel-avatar/kritzel-avatar.js +3 -3
  39. package/dist/collection/components/shared/kritzel-brush-style/kritzel-brush-style.js +1 -1
  40. package/dist/collection/components/shared/kritzel-button/kritzel-button.js +2 -2
  41. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  42. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
  43. package/dist/collection/components/shared/kritzel-dropdown/kritzel-dropdown.js +1 -1
  44. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  45. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  46. package/dist/collection/components/shared/kritzel-input/kritzel-input.js +1 -1
  47. package/dist/collection/components/shared/kritzel-master-detail/kritzel-master-detail.js +3 -3
  48. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  49. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  50. package/dist/collection/components/shared/kritzel-numeric-input/kritzel-numeric-input.js +1 -1
  51. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +1 -1
  52. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  53. package/dist/collection/components/shared/kritzel-slide-toggle/kritzel-slide-toggle.js +1 -1
  54. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  55. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
  56. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +2 -2
  57. package/dist/collection/components/ui/kritzel-back-to-content/kritzel-back-to-content.js +1 -1
  58. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +1 -1
  59. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +5 -5
  60. package/dist/collection/components/ui/kritzel-current-user/kritzel-current-user.js +1 -1
  61. package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js +1 -1
  62. package/dist/collection/components/ui/kritzel-export/kritzel-export.js +1 -1
  63. package/dist/collection/components/ui/kritzel-login-dialog/kritzel-login-dialog.js +1 -1
  64. package/dist/collection/components/ui/kritzel-more-menu/kritzel-more-menu.js +1 -1
  65. package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +1 -1
  66. package/dist/collection/components/ui/kritzel-share-dialog/kritzel-share-dialog.js +2 -2
  67. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  68. package/dist/collection/constants/schema.constants.js +1 -1
  69. package/dist/collection/constants/version.js +1 -1
  70. package/dist/collection/interfaces/remote-cursor.interface.js +1 -0
  71. package/dist/collection/migrations/workspace.migrations.js +10 -1
  72. package/dist/components/index.d.ts +2 -0
  73. package/dist/components/index.js +1 -1
  74. package/dist/components/kritzel-active-users.js +1 -1
  75. package/dist/components/kritzel-avatar.js +1 -1
  76. package/dist/components/kritzel-awareness-cursors.d.ts +11 -0
  77. package/dist/components/kritzel-awareness-cursors.js +1 -0
  78. package/dist/components/kritzel-back-to-content.js +1 -1
  79. package/dist/components/kritzel-brush-style.js +1 -1
  80. package/dist/components/kritzel-button.js +1 -1
  81. package/dist/components/kritzel-color-palette.js +1 -1
  82. package/dist/components/kritzel-color.js +1 -1
  83. package/dist/components/kritzel-context-menu.js +1 -1
  84. package/dist/components/kritzel-controls.js +1 -1
  85. package/dist/components/kritzel-current-user-dialog.js +1 -1
  86. package/dist/components/kritzel-current-user.js +1 -1
  87. package/dist/components/kritzel-cursor-trail.js +1 -1
  88. package/dist/components/kritzel-dropdown.js +1 -1
  89. package/dist/components/kritzel-editor.js +1 -1
  90. package/dist/components/kritzel-engine.js +1 -1
  91. package/dist/components/kritzel-export.js +1 -1
  92. package/dist/components/kritzel-font-family.js +1 -1
  93. package/dist/components/kritzel-font-size.js +1 -1
  94. package/dist/components/kritzel-font.js +1 -1
  95. package/dist/components/kritzel-input.js +1 -1
  96. package/dist/components/kritzel-login-dialog.js +1 -1
  97. package/dist/components/kritzel-master-detail.js +1 -1
  98. package/dist/components/kritzel-menu-item.js +1 -1
  99. package/dist/components/kritzel-menu.js +1 -1
  100. package/dist/components/kritzel-more-menu.js +1 -1
  101. package/dist/components/kritzel-numeric-input.js +1 -1
  102. package/dist/components/kritzel-opacity-slider.js +1 -1
  103. package/dist/components/kritzel-portal.js +1 -1
  104. package/dist/components/kritzel-settings.js +1 -1
  105. package/dist/components/kritzel-share-dialog.js +1 -1
  106. package/dist/components/kritzel-slide-toggle.js +1 -1
  107. package/dist/components/kritzel-split-button.js +1 -1
  108. package/dist/components/kritzel-stroke-size.js +1 -1
  109. package/dist/components/kritzel-tool-config.js +1 -1
  110. package/dist/components/kritzel-tooltip.js +1 -1
  111. package/dist/components/kritzel-utility-panel.js +1 -1
  112. package/dist/components/kritzel-workspace-manager.js +1 -1
  113. package/dist/components/{p-Dp8idtVD.js → p-0kShCfeb.js} +1 -1
  114. package/dist/components/{p-B47JuZiD.js → p-2OYw6GJ7.js} +1 -1
  115. package/dist/components/p-7o2FWtFx.js +1 -0
  116. package/dist/components/{p-C5KuV1pK.js → p-BA0ayKqO.js} +1 -1
  117. package/dist/components/{p-NbNVTRk6.js → p-BEJQ2kP7.js} +1 -1
  118. package/dist/components/p-BSipRoFx.js +1 -0
  119. package/dist/components/{p-CDadAOMw.js → p-BeFUNGEI.js} +1 -1
  120. package/dist/components/{p-35nrk8s0.js → p-BiByyU2C.js} +1 -1
  121. package/dist/components/{p-CCAWSyDD.js → p-BiouZo1q.js} +1 -1
  122. package/dist/components/{p-CSExtYKI.js → p-ByR0VXeU.js} +1 -1
  123. package/dist/components/{p-1MGcXTLv.js → p-C1uR_ZNW.js} +1 -1
  124. package/dist/components/{p-x8PzaMuD.js → p-C69Stayh.js} +1 -1
  125. package/dist/components/{p-Ch0UlFwq.js → p-C7SBI_0T.js} +1 -1
  126. package/dist/components/{p-DEzfXrGX.js → p-CAIGuV2J.js} +1 -1
  127. package/dist/components/p-CJ2eHeoV.js +1 -0
  128. package/dist/components/p-CW-VyJgK.js +1 -0
  129. package/dist/components/{p-DW4ADV9w.js → p-CZhyKp-f.js} +1 -1
  130. package/dist/components/p-CsR4owzk.js +1 -0
  131. package/dist/components/{p-BG1IxseV.js → p-CsoDfhD5.js} +1 -1
  132. package/dist/components/{p-BgDhcDNw.js → p-D0MQFmqi.js} +1 -1
  133. package/dist/components/{p-DpFu5yAt.js → p-D1O7DxL4.js} +1 -1
  134. package/dist/components/{p-B5ouV8EQ.js → p-DRbG92F9.js} +1 -1
  135. package/dist/components/{p-C3eaM9TB.js → p-DS0xx1eT.js} +1 -1
  136. package/dist/components/{p-jx8VOz7S.js → p-DSzQ6H2j.js} +1 -1
  137. package/dist/components/{p-DsIlDGDO.js → p-DXjuuVq9.js} +1 -1
  138. package/dist/components/p-DXpYcAnT.js +1 -0
  139. package/dist/components/{p-DiFVw6IQ.js → p-Da46jw3N.js} +1 -1
  140. package/dist/components/{p-C6kZf91d.js → p-Dj_Qjga5.js} +1 -1
  141. package/dist/components/{p-Do0Q5-iC.js → p-DvIEvoZu.js} +1 -1
  142. package/dist/components/{p-CnVzLD5e.js → p-GYI7sDxr.js} +1 -1
  143. package/dist/components/{p-CcBM_ClD.js → p-HLbqRJGs.js} +1 -1
  144. package/dist/components/{p-VHyNcODZ.js → p-KQzWumjB.js} +1 -1
  145. package/dist/components/{p-VAkeZOZL.js → p-TyR-YTXm.js} +1 -1
  146. package/dist/components/p-WmxufeOo.js +9 -0
  147. package/dist/components/{p-CHtn5xr6.js → p-b4gyXoju.js} +1 -1
  148. package/dist/components/p-iRL0wQHQ.js +1 -0
  149. package/dist/components/{p-CqLaHE27.js → p-kj9wbLY8.js} +1 -1
  150. package/dist/components/{p-DaHq4iG1.js → p-xM-_OeRO.js} +1 -1
  151. package/dist/esm/index-MV-81ybv.js +2 -2
  152. package/dist/esm/index.js +132 -87
  153. package/dist/esm/{kritzel-active-users_41.entry.js → kritzel-active-users_42.entry.js} +593 -178
  154. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  155. package/dist/esm/loader.js +1 -1
  156. package/dist/esm/stencil.js +1 -1
  157. package/dist/esm/{workspace.migrations-BGixvB76.js → workspace.migrations-D48_Bqvh.js} +164 -49
  158. package/dist/stencil/index.esm.js +1 -1
  159. package/dist/stencil/p-0dbd9a2f.entry.js +9 -0
  160. package/dist/stencil/p-D48_Bqvh.js +1 -0
  161. package/dist/stencil/{p-016ad76a.entry.js → p-fc21e29c.entry.js} +1 -1
  162. package/dist/stencil/stencil.esm.js +1 -1
  163. package/dist/types/classes/core/store.class.d.ts +10 -2
  164. package/dist/types/classes/managers/anchor.manager.d.ts +4 -0
  165. package/dist/types/classes/objects/base-object.class.d.ts +1 -0
  166. package/dist/types/classes/objects/selection-group.class.d.ts +5 -0
  167. package/dist/types/classes/providers/broadcast-sync-provider.class.d.ts +2 -0
  168. package/dist/types/classes/providers/hocuspocus-sync-provider.class.d.ts +37 -1
  169. package/dist/types/classes/providers/indexeddb-sync-provider.class.d.ts +2 -0
  170. package/dist/types/classes/providers/websocket-sync-provider.class.d.ts +2 -0
  171. package/dist/types/classes/structures/object-map.structure.d.ts +38 -0
  172. package/dist/types/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.d.ts +26 -0
  173. package/dist/types/components.d.ts +39 -4
  174. package/dist/types/constants/schema.constants.d.ts +1 -1
  175. package/dist/types/constants/version.d.ts +1 -1
  176. package/dist/types/interfaces/object.interface.d.ts +1 -0
  177. package/dist/types/interfaces/remote-cursor.interface.d.ts +17 -0
  178. package/dist/types/interfaces/sync-provider.interface.d.ts +16 -0
  179. package/dist/types/interfaces/theme.interface.d.ts +7 -0
  180. package/package.json +1 -1
  181. package/dist/components/p-BvToKcu1.js +0 -1
  182. package/dist/components/p-By3NOY-k.js +0 -9
  183. package/dist/components/p-CNro30tB.js +0 -1
  184. package/dist/components/p-Duv3EM3w.js +0 -1
  185. package/dist/components/p-KFsLHwYm.js +0 -1
  186. package/dist/components/p-hCORwbZh.js +0 -1
  187. package/dist/stencil/p-67c79d75.entry.js +0 -9
  188. package/dist/stencil/p-BGixvB76.js +0 -1
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var index = require('./index-Dc7LOVhs.js');
4
- var workspace_migrations = require('./workspace.migrations-DcwqsqPC.js');
4
+ var workspace_migrations = require('./workspace.migrations-DkmVO6dE.js');
5
5
  var Y = require('yjs');
6
6
  require('y-websocket');
7
7
  require('y-indexeddb');
@@ -154,16 +154,16 @@ const KritzelAvatar = class {
154
154
  height: `${this.size}px`,
155
155
  fontSize: `${Math.round(this.size * 0.4)}px`,
156
156
  };
157
- return (index.h(index.Host, { key: '9f2756b21f5cf12c770ecae3917b889e651180fd', style: containerStyles, class: {
157
+ return (index.h(index.Host, { key: '67c0c1f944b9c835d1685d2b743114a1b5a1f061', style: containerStyles, class: {
158
158
  'has-image': !!showImage,
159
159
  'has-initials': !!showInitials,
160
160
  'has-default': !!showDefaultIcon,
161
- }, role: "img", "aria-label": this.getDisplayName() || 'User avatar' }, showImage && (index.h("img", { key: '4d73aed6c3cb4832a661163e780b4950beb33ff3', src: imageUrl, alt: "", class: "avatar-image", ref: (el) => {
161
+ }, role: "img", "aria-label": this.getDisplayName() || 'User avatar' }, showImage && (index.h("img", { key: 'd71ccd7449a27133bcf8cc01f3cd654cc9b00142', src: imageUrl, alt: "", class: "avatar-image", ref: (el) => {
162
162
  if (el) {
163
163
  el.referrerPolicy = 'no-referrer';
164
164
  el.crossOrigin = 'anonymous';
165
165
  }
166
- }, onError: this.handleImageError })), showInitials && (index.h("span", { key: '9e71b25b721d18e213da38ceead31ed5cf861bd8', class: "avatar-initials", style: { backgroundColor: this.getBackgroundColor() } }, initials)), showDefaultIcon && (index.h("span", { key: 'f330747bda5862d9e2d19d9dda331bced2bdc6de', class: "avatar-default" }, index.h("svg", { key: '4eefdc2e502669c193fbe54a20c57701f0ecc0ae', viewBox: "0 0 24 24", fill: "currentColor" }, index.h("path", { key: '1788425bba4559c34d9f16069cfa32bc84cf1b17', 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" }))))));
166
+ }, onError: this.handleImageError })), showInitials && (index.h("span", { key: '818ad4a24d6489565dffc8c64b3d4e76b701444c', class: "avatar-initials", style: { backgroundColor: this.getBackgroundColor() } }, initials)), showDefaultIcon && (index.h("span", { key: '87af93fd968f3bf5b1f86295de6bd4b71c94120c', class: "avatar-default" }, index.h("svg", { key: '3267317fe99fb8d90432e207516bb48f603c08ed', viewBox: "0 0 24 24", fill: "currentColor" }, index.h("path", { key: '9e4e225f9a058a24f63b508f36969c8c429fccd6', 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" }))))));
167
167
  }
168
168
  static get watchers() { return {
169
169
  "user": [{
@@ -176,6 +176,262 @@ const KritzelAvatar = class {
176
176
  };
177
177
  KritzelAvatar.style = kritzelAvatarCss();
178
178
 
179
+ const kritzelAwarenessCursorsCss = () => `:host{display:block;position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:9500}.awareness-cursor{position:absolute;top:0;left:0;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform}.awareness-cursor.stale{opacity:0.3}.awareness-cursor.tracking-object{transition-duration:0ms}.cursor-arrow{filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.cursor-label{position:absolute;left:16px;top:16px;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;user-select:none}.edge-indicator{position:absolute;top:-12px;left:-12px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform;pointer-events:auto;user-select:none;cursor:pointer}.edge-indicator.stale{opacity:0.3}.edge-indicator.tracking-object{transition-duration:0ms}.edge-arrow{position:absolute;filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3))}.edge-label{position:absolute;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;opacity:0;transform-origin:center;transition:opacity 150ms ease}.edge-indicator:hover .edge-label{opacity:1}.remote-selection-box{position:absolute;top:0;left:0;border-width:2px;border-style:solid;pointer-events:none;will-change:transform, width, height;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, width var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, height var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out}`;
180
+
181
+ const STALE_THRESHOLD_MS = 10_000;
182
+ const REMOVE_THRESHOLD_MS = 30_000;
183
+ const CLEANUP_INTERVAL_MS = 3_000;
184
+ const KritzelAwarenessCursors = class {
185
+ constructor(hostRef) {
186
+ index.registerInstance(this, hostRef);
187
+ }
188
+ core;
189
+ showEdgeIndicators = true;
190
+ edgeIndicatorPadding = 8;
191
+ remoteCursors = new Map();
192
+ objectVersion = 0;
193
+ cleanupIntervalId;
194
+ objectChangeRafId = null;
195
+ componentDidLoad() {
196
+ this.core.store.state.objects?.onAwarenessChange(states => {
197
+ this.handleAwarenessChange(states);
198
+ });
199
+ this.core.store.state.objects?.onObjectsChange(() => {
200
+ this.handleRemoteObjectChange();
201
+ });
202
+ this.cleanupIntervalId = setInterval(() => {
203
+ this.cleanupStaleCursors();
204
+ }, CLEANUP_INTERVAL_MS);
205
+ }
206
+ disconnectedCallback() {
207
+ if (this.cleanupIntervalId) {
208
+ clearInterval(this.cleanupIntervalId);
209
+ }
210
+ if (this.objectChangeRafId !== null) {
211
+ cancelAnimationFrame(this.objectChangeRafId);
212
+ }
213
+ }
214
+ handleAwarenessChange(states) {
215
+ const localClientId = this.core.store.state.objects?.localClientId;
216
+ const now = Date.now();
217
+ const updated = new Map(this.remoteCursors);
218
+ // Track which clientIds are still present
219
+ const activeClientIds = new Set();
220
+ states.forEach((state, clientId) => {
221
+ if (clientId === localClientId)
222
+ return;
223
+ if (!state.user)
224
+ return;
225
+ activeClientIds.add(clientId);
226
+ const user = state.user;
227
+ const cursor = state.cursor;
228
+ const activeObjectId = state.activeObjectId || null;
229
+ const selectionBox = state.selectionBox || null;
230
+ updated.set(clientId, {
231
+ clientId,
232
+ user,
233
+ cursor,
234
+ activeObjectId,
235
+ selectionBox,
236
+ lastUpdated: now,
237
+ });
238
+ });
239
+ // Remove cursors for disconnected clients
240
+ for (const clientId of updated.keys()) {
241
+ if (!activeClientIds.has(clientId)) {
242
+ updated.delete(clientId);
243
+ }
244
+ }
245
+ this.remoteCursors = updated;
246
+ }
247
+ cleanupStaleCursors() {
248
+ const now = Date.now();
249
+ let changed = false;
250
+ const updated = new Map(this.remoteCursors);
251
+ for (const [clientId, cursor] of updated) {
252
+ if (now - cursor.lastUpdated > REMOVE_THRESHOLD_MS) {
253
+ updated.delete(clientId);
254
+ changed = true;
255
+ }
256
+ }
257
+ if (changed) {
258
+ this.remoteCursors = updated;
259
+ }
260
+ }
261
+ isStale(cursor) {
262
+ return Date.now() - cursor.lastUpdated > STALE_THRESHOLD_MS;
263
+ }
264
+ hasActiveDrawingCursors() {
265
+ for (const cursor of this.remoteCursors.values()) {
266
+ if (cursor.activeObjectId)
267
+ return true;
268
+ }
269
+ return false;
270
+ }
271
+ handleRemoteObjectChange() {
272
+ if (!this.hasActiveDrawingCursors())
273
+ return;
274
+ // Debounce via rAF to batch multiple rapid Yjs updates into a single re-render
275
+ if (this.objectChangeRafId !== null)
276
+ return;
277
+ this.objectChangeRafId = requestAnimationFrame(() => {
278
+ this.objectChangeRafId = null;
279
+ this.objectVersion++;
280
+ });
281
+ }
282
+ getActiveObjectTip(objectId) {
283
+ const obj = this.core.store.state.objects?.findById(objectId);
284
+ if (!obj)
285
+ return null;
286
+ if (obj instanceof workspace_migrations.KritzelPath && !obj.isCompleted) {
287
+ const lastPoint = obj.points[obj.points.length - 1];
288
+ if (!lastPoint)
289
+ return null;
290
+ return {
291
+ x: (lastPoint[0] - obj.x) / obj.scale + obj.translateX,
292
+ y: (lastPoint[1] - obj.y) / obj.scale + obj.translateY,
293
+ };
294
+ }
295
+ if (obj instanceof workspace_migrations.KritzelLine && !obj.isCompleted) {
296
+ return {
297
+ x: (obj.endX - obj.x) / obj.scale + obj.translateX,
298
+ y: (obj.endY - obj.y) / obj.scale + obj.translateY,
299
+ };
300
+ }
301
+ // Shapes normalize their bounding box (x/y always top-left, width/height
302
+ // always positive), so we can't determine which corner the user is actively
303
+ // dragging. Fall back to the throttled awareness cursor position instead.
304
+ return null;
305
+ }
306
+ worldToScreen(worldX, worldY) {
307
+ const { scale, translateX, translateY } = this.core.store.state;
308
+ return {
309
+ x: worldX * scale + translateX,
310
+ y: worldY * scale + translateY,
311
+ };
312
+ }
313
+ isInViewport(screenX, screenY) {
314
+ const { viewportWidth, viewportHeight } = this.core.store.state;
315
+ return screenX >= 0 && screenX <= viewportWidth && screenY >= 0 && screenY <= viewportHeight;
316
+ }
317
+ clampToEdge(screenX, screenY) {
318
+ const { viewportWidth, viewportHeight } = this.core.store.state;
319
+ const padding = this.edgeIndicatorPadding; // 8px
320
+ // Clamp symmetrically
321
+ const clampedX = Math.max(padding, Math.min(viewportWidth - padding, screenX));
322
+ const clampedY = Math.max(padding, Math.min(viewportHeight - padding, screenY));
323
+ // Determine nearest edge to position avatar inside
324
+ const distLeft = clampedX - padding;
325
+ const distRight = (viewportWidth - padding) - clampedX;
326
+ const distTop = clampedY - padding;
327
+ const distBottom = (viewportHeight - padding) - clampedY;
328
+ let edge = 'top';
329
+ const minDist = Math.min(distLeft, distRight, distTop, distBottom);
330
+ if (minDist === distLeft)
331
+ edge = 'left';
332
+ else if (minDist === distRight)
333
+ edge = 'right';
334
+ else if (minDist === distTop)
335
+ edge = 'top';
336
+ else
337
+ edge = 'bottom';
338
+ const angle = Math.atan2(screenY - clampedY, screenX - clampedX);
339
+ return { x: clampedX, y: clampedY, angle, edge };
340
+ }
341
+ getUserDisplayName(user) {
342
+ if (user.displayName)
343
+ return user.displayName;
344
+ if (user.firstName || user.lastName) {
345
+ return [user.firstName, user.lastName].filter(Boolean).join(' ');
346
+ }
347
+ return 'Unknown';
348
+ }
349
+ render() {
350
+ const cursors = Array.from(this.remoteCursors.values());
351
+ return (index.h(index.Host, { key: 'b8676d9a7a3d4a79ea8ad9763355ecf4c2f90e17' }, cursors.map(remoteCursor => {
352
+ if (!remoteCursor.cursor)
353
+ return null;
354
+ // When a remote user is actively drawing, derive cursor position from
355
+ // the object's latest coordinates (synced via Yjs) instead of the
356
+ // throttled awareness cursor position
357
+ let screen;
358
+ let trackingObject = false;
359
+ if (remoteCursor.activeObjectId) {
360
+ const tip = this.getActiveObjectTip(remoteCursor.activeObjectId);
361
+ if (tip) {
362
+ trackingObject = true;
363
+ screen = this.worldToScreen(tip.x, tip.y);
364
+ }
365
+ else {
366
+ screen = this.worldToScreen(remoteCursor.cursor.x, remoteCursor.cursor.y);
367
+ }
368
+ }
369
+ else {
370
+ screen = this.worldToScreen(remoteCursor.cursor.x, remoteCursor.cursor.y);
371
+ }
372
+ const inViewport = this.isInViewport(screen.x, screen.y);
373
+ const stale = this.isStale(remoteCursor);
374
+ const color = remoteCursor.user.color || '#6B7280';
375
+ if (inViewport) {
376
+ return this.renderCursor(remoteCursor, screen.x, screen.y, color, stale, trackingObject);
377
+ }
378
+ if (this.showEdgeIndicators) {
379
+ return this.renderEdgeIndicator(remoteCursor, screen.x, screen.y, color, stale, trackingObject);
380
+ }
381
+ return null;
382
+ }), cursors.map(remoteCursor => {
383
+ if (!remoteCursor.selectionBox)
384
+ return null;
385
+ const color = remoteCursor.user.color || '#6B7280';
386
+ const box = remoteCursor.selectionBox;
387
+ const topLeft = this.worldToScreen(box.x, box.y);
388
+ const { scale } = this.core.store.state;
389
+ const screenWidth = box.width * scale;
390
+ const screenHeight = box.height * scale;
391
+ return (index.h("div", { key: `selection-box-${remoteCursor.clientId}`, class: "remote-selection-box", style: {
392
+ transform: `translate(${topLeft.x}px, ${topLeft.y}px)`,
393
+ width: `${screenWidth}px`,
394
+ height: `${screenHeight}px`,
395
+ backgroundColor: `color-mix(in srgb, ${color} 20%, transparent)`,
396
+ borderColor: `color-mix(in srgb, ${color} 50%, transparent)`,
397
+ } }));
398
+ })));
399
+ }
400
+ renderCursor(cursor, screenX, screenY, color, stale, trackingObject) {
401
+ return (index.h("div", { key: `cursor-${cursor.clientId}`, class: { 'awareness-cursor': true, stale, 'tracking-object': trackingObject }, style: { transform: `translate(${screenX}px, ${screenY}px)` } }, index.h("svg", { class: "cursor-arrow", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, index.h("path", { d: "M5 3L19 12L12 13L9 20L5 3Z", fill: color, stroke: "#ffffff", "stroke-width": "1.5", "stroke-linejoin": "round" })), index.h("span", { class: "cursor-label", style: { backgroundColor: color } }, this.getUserDisplayName(cursor.user))));
402
+ }
403
+ renderEdgeIndicator(cursor, screenX, screenY, color, stale, trackingObject) {
404
+ const clamped = this.clampToEdge(screenX, screenY);
405
+ const arrowDeg = (clamped.angle * 180) / Math.PI + 90;
406
+ // Position the username label opposite to the edge
407
+ const labelOffset = 20;
408
+ let labelX = 0;
409
+ let labelY = 0;
410
+ // Default transform origin is somewhat arbitrary without explicit layout
411
+ // We'll calculate simple translations relative to center point
412
+ if (clamped.edge === 'left') {
413
+ labelX = labelOffset;
414
+ }
415
+ else if (clamped.edge === 'right') {
416
+ labelX = -labelOffset;
417
+ }
418
+ else if (clamped.edge === 'top') {
419
+ labelY = labelOffset;
420
+ }
421
+ else if (clamped.edge === 'bottom') {
422
+ labelY = -labelOffset;
423
+ }
424
+ const displayName = this.getUserDisplayName(cursor.user);
425
+ return (index.h("div", { key: `edge-${cursor.clientId}`, class: { 'edge-indicator': true, stale, 'tracking-object': trackingObject }, style: {
426
+ transform: `translate(${clamped.x}px, ${clamped.y}px)`,
427
+ } }, index.h("svg", { class: "edge-arrow", width: "16", height: "16", viewBox: "0 0 16 16", style: { transform: `rotate(${arrowDeg}deg)` } }, index.h("path", { d: "M8 1L14 13H2L8 1Z", fill: color, stroke: "#ffffff", "stroke-width": "1.5", "stroke-linejoin": "round" })), index.h("span", { class: "edge-label", style: {
428
+ backgroundColor: color,
429
+ transform: `translate(${labelX}px, ${labelY}px)`,
430
+ } }, displayName)));
431
+ }
432
+ };
433
+ KritzelAwarenessCursors.style = kritzelAwarenessCursorsCss();
434
+
179
435
  const kritzelBackToContentCss = () => `:host{display:block;z-index:var(--kritzel-back-to-content-z-index, 1000)}.back-to-content-button{display:flex;align-items:center;justify-content:center;height:var(--kritzel-back-to-content-height, 50px);box-sizing:border-box;gap:var(--kritzel-back-to-content-gap, 6px);padding:var(--kritzel-back-to-content-padding, 0 16px);border:var(--kritzel-back-to-content-border, 1px solid #ebebeb);border-radius:var(--kritzel-back-to-content-border-radius, 12px);background-color:var(--kritzel-back-to-content-background-color, #ffffff);color:var(--kritzel-back-to-content-color, #000000);font-size:var(--kritzel-back-to-content-font-size, 14px);font-weight:var(--kritzel-back-to-content-font-weight, 500);cursor:var(--kritzel-global-pointer-cursor, pointer);box-shadow:var(--kritzel-back-to-content-box-shadow, 0 0 3px rgba(0, 0, 0, 0.08));opacity:0;pointer-events:none;transition:opacity 0.2s ease-out, transform 0.2s ease-out, background-color 0.15s ease-out;-webkit-tap-highlight-color:transparent;user-select:none}.back-to-content-button.visible{opacity:1;pointer-events:auto}@media (hover: hover){.back-to-content-button:hover{background-color:var(--kritzel-back-to-content-hover-background-color, hsl(0, 0%, 0%, 4.3%))}}.back-to-content-button:active{background-color:var(--kritzel-back-to-content-active-background-color, hsl(0, 0%, 0%, 8.6%))}`;
180
436
 
181
437
  const KritzelBackToContent = class {
@@ -199,7 +455,7 @@ const KritzelBackToContent = class {
199
455
  this.backToContent.emit();
200
456
  };
201
457
  render() {
202
- return (index.h(index.Host, { key: 'e42bfb365d1ae7fba820234ae0d59cd1e468a258' }, index.h("button", { key: 'b8c376989685c0aca4ee9c425944289ccc6e351a', class: { 'back-to-content-button': true, visible: this.visible }, onClick: this.handleClick }, index.h("kritzel-icon", { key: '7955fafe06de1d2f6827bbd933dffd575c9ba9ed', name: "chevrons-left" }))));
458
+ return (index.h(index.Host, { key: '890501d05ecfadaedbf298590d43b5efd67f1d5f' }, index.h("button", { key: 'fae3acd5aed5d0a5322fe9e0da2186577d873c5c', class: { 'back-to-content-button': true, visible: this.visible }, onClick: this.handleClick }, index.h("kritzel-icon", { key: 'eaa718ab29fad870464c6b67d5ad27c4300dfd54', name: "chevrons-left" }))));
203
459
  }
204
460
  };
205
461
  KritzelBackToContent.style = kritzelBackToContentCss();
@@ -226,11 +482,11 @@ const KritzelButton = class {
226
482
  this.buttonClick.emit();
227
483
  };
228
484
  render() {
229
- return (index.h(index.Host, { key: '3efc2197a7f10b5da36eba52e03b48e6aa2500dc' }, index.h("button", { key: '0d506f62756b3cd60ebdd78c52c903dddcd1d6ff', type: this.type, class: {
485
+ return (index.h(index.Host, { key: '3a285bfdc1b336fb450304856aa81c1bd9c4e5e3' }, index.h("button", { key: 'f3119bfe9cf8ca1b6e031cdcc8bee9549c18e620', type: this.type, class: {
230
486
  'kritzel-button': true,
231
487
  [this.variant]: true,
232
488
  'disabled': this.disabled,
233
- }, disabled: this.disabled, onClick: this.handleClick }, index.h("slot", { key: '76ab315100ff334ad47dad974cd83f117f73e844' }))));
489
+ }, disabled: this.disabled, onClick: this.handleClick }, index.h("slot", { key: '5a26eee0781c5fafc7a41260364b2a2c11f192a6' }))));
234
490
  }
235
491
  };
236
492
  KritzelButton.style = kritzelButtonCss();
@@ -278,13 +534,13 @@ const KritzelColorComponent = class {
278
534
  render() {
279
535
  const resolvedColor = this.resolveColor();
280
536
  const isColorVeryLight = this.isLightColor(resolvedColor);
281
- return (index.h(index.Host, { key: 'a95e6bc89330b9a5baecf53b5fa3e54316ad153d' }, index.h("div", { key: 'e19d5fc8f59dce48f815a382c358a2068b05f8c3', class: "checkerboard-bg", style: {
537
+ return (index.h(index.Host, { key: '7e36e97c383cff73b3146279e971526dd5c7f0f7' }, index.h("div", { key: 'e8ad3ca5e9771c51a96a846627ea20c75212ceeb', class: "checkerboard-bg", style: {
282
538
  width: `${this.size}px`,
283
539
  height: `${this.size}px`,
284
540
  borderRadius: '50%',
285
541
  display: 'inline-block',
286
542
  position: 'relative',
287
- } }, index.h("div", { key: '61678e783b6ea5890fc0eb74f37592b94b09f59e', class: {
543
+ } }, index.h("div", { key: '9269087c7694604d70d50d846cedb6ce022373cd', class: {
288
544
  'color-circle': true,
289
545
  'white': isColorVeryLight,
290
546
  }, style: {
@@ -340,7 +596,7 @@ const KritzelColorPalette = class {
340
596
  render() {
341
597
  const displayedColors = this.isExpanded ? this.colors : this.colors.slice(0, 6);
342
598
  const expandedHeight = this.isExpanded ? this.calculateHeight() : '32px';
343
- return (index.h(index.Host, { key: '8dd3fe8327b3d5ce4f420cbff752b523e66b8356' }, index.h("div", { key: '554f867b63450fa0f423b71cdfa5b1d6de3e12a9', class: {
599
+ return (index.h(index.Host, { key: '22b6f0362933848cd40ad44c73a48fd4d8330884' }, index.h("div", { key: '38adb9abc39d78ae9528341212acf2de425b4eb8', class: {
344
600
  'color-grid': true,
345
601
  'expanded': this.isExpanded,
346
602
  }, style: {
@@ -538,7 +794,7 @@ const KritzelContextMenu = class {
538
794
  })));
539
795
  }
540
796
  render() {
541
- return (index.h(index.Host, { key: 'e1d2c078548eda801784424fb9ba77a8fc905a90' }, index.h("div", { key: '39cba641f90e2ad6d30553974880cdedd7fe3877', class: "menu-container" }, this.processedItems.map(({ item, isDisabled, processedChildren }, index$1) => {
797
+ return (index.h(index.Host, { key: '11f279a1ef329569988b1c1ce6311a1f81695ebe' }, index.h("div", { key: 'e0aa8ab22df018ae0a2b1c22513575d67099d35d', class: "menu-container" }, this.processedItems.map(({ item, isDisabled, processedChildren }, index$1) => {
542
798
  const prevItem = index$1 > 0 ? this.processedItems[index$1 - 1].item : null;
543
799
  const showDivider = prevItem && prevItem.group !== item.group;
544
800
  const hasChildren = processedChildren && processedChildren.length > 0;
@@ -818,13 +1074,13 @@ const KritzelControls = class {
818
1074
  // Separate tool controls from config control
819
1075
  const toolControls = this.controls.filter(c => c.type === 'tool' || c.type === 'separator');
820
1076
  const configControl = this.controls.find(c => c.type === 'config' && c.name === this.firstConfig?.name);
821
- return (index.h(index.Host, { key: 'f706b101d3ec5dd4453ffd906499f6f4bbbb64bf', class: {
1077
+ return (index.h(index.Host, { key: 'fa03f9eae6ebc0a043d6ac3cb774ae32491ae943', class: {
822
1078
  mobile: this.isTouchDevice,
823
- } }, this.isUtilityPanelVisible && (index.h("kritzel-utility-panel", { key: '414bda49650cb551036654639fbf1d9bb6bbac4e', style: {
1079
+ } }, this.isUtilityPanelVisible && (index.h("kritzel-utility-panel", { key: '2e789118656dc3366f131b520f2be4386e5889c8', style: {
824
1080
  position: 'absolute',
825
1081
  bottom: '56px',
826
1082
  left: '12px',
827
- }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), index.h("div", { key: '1be53e2fba46a37398768d39fbbed0543eb31628', class: "kritzel-controls" }, index.h("div", { key: 'b26fa897d079043ca007a12b8941a9ee474274be', class: { 'scroll-indicator-left': true, 'visible': this.canScrollLeft } }), index.h("div", { key: 'ff7b7fb6bd24fccc5e42dd29fc449cd69340bf79', class: "kritzel-tools-scroll", ref: el => this.toolsScrollRef = el, onScroll: this.handleToolsScroll }, toolControls.map(control => {
1083
+ }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), index.h("div", { key: '29b4d14746f81f7def022ec5769c9d1bf21a6dab', class: "kritzel-controls" }, index.h("div", { key: 'eb5a4ddd2c0ee9d77c008ddae1223e4ce4449116', class: { 'scroll-indicator-left': true, 'visible': this.canScrollLeft } }), index.h("div", { key: '5c41c8fc6770ef5eae7e5b03752f0fe65bcf30e1', class: "kritzel-tools-scroll", ref: el => this.toolsScrollRef = el, onScroll: this.handleToolsScroll }, toolControls.map(control => {
828
1084
  // Check if this control has sub-options (split-button)
829
1085
  if (control.subOptions?.length) {
830
1086
  const selectedSubOption = this.getSelectedSubOption(control);
@@ -854,10 +1110,10 @@ const KritzelControls = class {
854
1110
  'kritzel-control': true,
855
1111
  'selected': this.activeControl?.name === control?.name,
856
1112
  }, key: control.name, "data-testid": `tool-${control.name}`, onClick: _event => this.handleControlClick?.(control) }, index.h("kritzel-icon", { name: control.icon })));
857
- })), index.h("div", { key: '2644af8c1c448ae961e03a6ceab5c978c077b5c9', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight && !(configControl && this.activeControl && hasConfigUI) } }), configControl && this.activeControl && (index.h("div", { class: {
1113
+ })), index.h("div", { key: 'a924ed9102961f992b27afe6945732c8541c13a0', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight && !(configControl && this.activeControl && hasConfigUI) } }), configControl && this.activeControl && (index.h("div", { class: {
858
1114
  'kritzel-config-container': true,
859
1115
  'visible': hasConfigUI,
860
- }, key: configControl.name }, index.h("div", { key: '4f15bb371e325916bdfae2f3137a7cffed47f6ee', class: { 'config-gradient-left': true, 'visible': this.needsScrolling } }), index.h("kritzel-tooltip", { key: 'eac2874ca75faeb68a5623cd1fa96350469a1566', anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), triggerElement: this.configTriggerRef }, index.h("kritzel-tool-config", { key: '139afc8fb1852d196aa9e659a8d9c7a0c97c1453', tool: this.activeControl.tool, theme: this.theme, onToolChange: event => this.handleToolChange?.(event), onDisplayValuesChange: this.handleDisplayValuesChange, style: { width: '100%', height: '100%' } })), index.h("div", { key: 'd8725a81b5bfce5b795175a2ff5f8c3aaef7869e', tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", "data-testid": "tool-config", ref: el => {
1116
+ }, key: configControl.name }, index.h("div", { key: '9709b046ceb6ba0982ca45b93212b2d449c2b2fe', class: { 'config-gradient-left': true, 'visible': this.needsScrolling } }), index.h("kritzel-tooltip", { key: 'b2a2521c810f176f54ef8accff5b98a4a1acf42d', anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), triggerElement: this.configTriggerRef }, index.h("kritzel-tool-config", { key: 'cdfc29f2b8a3cd800789f68879216e73700d546e', tool: this.activeControl.tool, theme: this.theme, onToolChange: event => this.handleToolChange?.(event), onDisplayValuesChange: this.handleDisplayValuesChange, style: { width: '100%', height: '100%' } })), index.h("div", { key: '09d77705945e3e52eff40107c3bcbccbb636586d', tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", "data-testid": "tool-config", ref: el => {
861
1117
  if (el)
862
1118
  this.configTriggerRef = el;
863
1119
  }, onKeyDown: event => {
@@ -901,7 +1157,7 @@ const KritzelCurrentUser = class {
901
1157
  this.dialogRef?.open();
902
1158
  };
903
1159
  render() {
904
- return (index.h(index.Host, { key: '3e35539db9f34e3da3debb1b32973c597ef4a69e' }, index.h("kritzel-avatar", { key: '9db84121465097a39174d26fbec5edab4c4aecaa', user: this.user, size: this.avatarSize, onClick: this.handleAvatarClick }), index.h("kritzel-current-user-dialog", { key: 'da202b64e1c4781d9d49744ed95e9479780e4cd9', ref: el => (this.dialogRef = el), user: this.user })));
1160
+ return (index.h(index.Host, { key: '603389630e3c1fb02c94663bebdc374bbbfaa79e' }, index.h("kritzel-avatar", { key: 'b346db9aaa7681c484725a42c05ab94f468a19f0', user: this.user, size: this.avatarSize, onClick: this.handleAvatarClick }), index.h("kritzel-current-user-dialog", { key: '647b32cff0ffbfa08ce8d00f1a2f42a6d17027a3', ref: el => (this.dialogRef = el), user: this.user })));
905
1161
  }
906
1162
  };
907
1163
  KritzelCurrentUser.style = kritzelCurrentUserCss();
@@ -933,7 +1189,7 @@ const KritzelCurrentUserDialog = class {
933
1189
  }
934
1190
  render() {
935
1191
  const displayName = this.getDisplayName();
936
- return (index.h(index.Host, { key: '3f315637242364ec1a184ab0287e1647903b00df' }, index.h("kritzel-dialog", { key: '124cbb9e0236e7f1f9abfbf22a7de1ad8e9d418c', dialogTitle: "Account", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small" }, index.h("div", { key: '6e74746bb20197e8cf85bc0f9745a7da3a73c248', class: "user-info" }, index.h("kritzel-avatar", { key: '57f6f79b2eb61f503891065845915ca1de8534ad', user: this.user, size: 80 }), displayName && index.h("div", { key: '35d94a67ee50d764484c5f7ad1cbf2a188b030dc', class: "user-name" }, displayName), this.user?.email && index.h("div", { key: '9c118a62a29bc32dfc383d59616581a73f52b6f5', class: "user-email" }, this.user.email)))));
1192
+ return (index.h(index.Host, { key: 'f2b3c74f360bc81671e405d05e9a250878722911' }, index.h("kritzel-dialog", { key: 'c042f0574d72ed8705d27730aea8106e089cbcf6', dialogTitle: "Account", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small" }, index.h("div", { key: '3d02828a3444eebb92bdb317923315873e791831', class: "user-info" }, index.h("kritzel-avatar", { key: '172f2a529de8f397e327cbe6395272e12f7a51d0', user: this.user, size: 80 }), displayName && index.h("div", { key: '8537c57e8eaa2172caebcc5d53700f4835b4d1bf', class: "user-name" }, displayName), this.user?.email && index.h("div", { key: '446e60e1d422455518f38049eda813d7c52c8c0d', class: "user-email" }, this.user.email)))));
937
1193
  }
938
1194
  };
939
1195
  KritzelCurrentUserDialog.style = kritzelCurrentUserDialogCss();
@@ -1006,7 +1262,7 @@ const KritzelCursorTrail = class {
1006
1262
  }
1007
1263
  }
1008
1264
  render() {
1009
- return (index.h(index.Host, { key: '603a385702512357af1fbcc778b29feecf8cf208' }, this.cursorTrailPoints.length > 1 && (index.h("svg", { key: '5f7e4478599d482e624b77a752f601088b343f4e', class: "cursor-trail-svg", xmlns: "http://www.w3.org/2000/svg", style: {
1265
+ return (index.h(index.Host, { key: 'b6a9e84ce49dd72f353e0b87ae0bc02b0e5aeb1e' }, this.cursorTrailPoints.length > 1 && (index.h("svg", { key: '238ea314a937d556c75ae080495c2e6559340c6f', class: "cursor-trail-svg", xmlns: "http://www.w3.org/2000/svg", style: {
1010
1266
  position: 'absolute',
1011
1267
  left: '0',
1012
1268
  top: '0',
@@ -1557,7 +1813,7 @@ const KritzelDropdown = class {
1557
1813
  'open-up': this.openDirection === 'up',
1558
1814
  'open-down': this.openDirection === 'down',
1559
1815
  };
1560
- return (index.h(index.Host, { key: 'cc92e22245b066a6fdaa821d97f00b94d7bd6232' }, index.h("div", { key: '281288744c360fba3437394bb0a0270986f49de8', class: "dropdown-wrapper", ref: el => (this.wrapperElement = el) }, index.h("slot", { key: '5fc4058de36acd05f2ab356ac50aa23a526809e1', name: "prefix", ref: el => (this.prefixSlotElement = el), onSlotchange: this.evaluatePrefixContent }), index.h("div", { key: '317fa76695aad0fad9951def45ee3517e5de316d', class: "dropdown-container", style: { width: this.width } }, index.h("button", { key: 'd1221c8b8ea645a6ea19e7ada683405a287f3fe1', type: "button", class: triggerClasses, style: { ...this.selectStyles, ...this.getSelectedStyle() }, onClick: this.toggleMenu, onKeyDown: this.handleTriggerKeyDown, "aria-haspopup": "listbox", "aria-expanded": this.isOpen ? 'true' : 'false', ref: el => (this.triggerElement = el) }, index.h("span", { key: 'd485070f35bddd9af068a361ce3d94407d011af0', class: "dropdown-trigger-label" }, this.getSelectedLabel()), index.h("span", { key: '5d617070fb91996b9af3056b294484794953bfcb', class: "dropdown-trigger-arrow", "aria-hidden": "true" }, index.h("svg", { key: 'd462cbd0d9172f1617abc6bfeaf990e88bd4300d', xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("polyline", { key: '762c57ae655bcf7b3d3f7e29100400c82728f177', points: "6 9 12 15 18 9" }))))), index.h("slot", { key: '8ed792a632ac9b0e91b4b75ce89c4b024ee9b1b9', name: "suffix", ref: el => (this.suffixSlotElement = el), onSlotchange: this.evaluateSuffixContent }), index.h("ul", { key: '7cb57ef569aa447198f98714eb0ed41d9e198d75', class: menuClasses, role: "listbox", tabindex: "-1", onKeyDown: this.handleMenuKeyDown, ref: el => (this.menuElement = el) }, this.options.map((option, index$1) => {
1816
+ return (index.h(index.Host, { key: 'b753a3cf28a0f21625c213c9ec5337b922023206' }, index.h("div", { key: 'dffe30b1d6968ad612c94718eea72f118c43f378', class: "dropdown-wrapper", ref: el => (this.wrapperElement = el) }, index.h("slot", { key: '0a08fa5aa5a2040e85ac80643d11ec778ed89b31', name: "prefix", ref: el => (this.prefixSlotElement = el), onSlotchange: this.evaluatePrefixContent }), index.h("div", { key: '8a8b4519787863d53fa7c72928e7632de6daa848', class: "dropdown-container", style: { width: this.width } }, index.h("button", { key: '803fe1e4ef138a58cde8941771875dd5eadfe944', type: "button", class: triggerClasses, style: { ...this.selectStyles, ...this.getSelectedStyle() }, onClick: this.toggleMenu, onKeyDown: this.handleTriggerKeyDown, "aria-haspopup": "listbox", "aria-expanded": this.isOpen ? 'true' : 'false', ref: el => (this.triggerElement = el) }, index.h("span", { key: '05d9dfe94e62134c91316f33cf64f2a9efb57fbb', class: "dropdown-trigger-label" }, this.getSelectedLabel()), index.h("span", { key: '332d5206a67ae8ef4ce0982d62c108210bca6252', class: "dropdown-trigger-arrow", "aria-hidden": "true" }, index.h("svg", { key: 'f73539b0decf59fd59d075618dc5028ac82d11b5', xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("polyline", { key: '7b45a14ab898233a68845a40363ee37c9922098c', points: "6 9 12 15 18 9" }))))), index.h("slot", { key: '8936739a53b5dca8debdce27c4bead487a5bf803', name: "suffix", ref: el => (this.suffixSlotElement = el), onSlotchange: this.evaluateSuffixContent }), index.h("ul", { key: 'f20417649f8988e46db5992100d93fd7bd827b96', class: menuClasses, role: "listbox", tabindex: "-1", onKeyDown: this.handleMenuKeyDown, ref: el => (this.menuElement = el) }, this.options.map((option, index$1) => {
1561
1817
  const isSelected = option.value === this.internalValue;
1562
1818
  const isFocused = index$1 === this.focusedIndex;
1563
1819
  const optionClasses = {
@@ -1772,7 +2028,7 @@ const KritzelEditor = class {
1772
2028
  isControlsVisible = true;
1773
2029
  isUtilityPanelVisible = true;
1774
2030
  syncConfig = {
1775
- providers: [workspace_migrations.IndexedDBSyncProvider],
2031
+ providers: [workspace_migrations.IndexedDBSyncProvider]
1776
2032
  };
1777
2033
  /** Optional login configuration. When provided, a "Sign in" button is shown that opens a login dialog with the configured providers. */
1778
2034
  loginConfig;
@@ -2289,7 +2545,7 @@ const KritzelEditor = class {
2289
2545
  const isLoggedIn = this.isLoggedIn;
2290
2546
  const shouldShowCurrentUser = isLoggedIn;
2291
2547
  const shouldShowLoginButton = !!this.loginConfig && !isLoggedIn;
2292
- return (index.h(index.Host, { key: '84231308ca4b12863139aeeafa754d544345df3f' }, index.h("div", { key: 'b8cdd2862e9bd892dfeed356e85d608759307bf7', class: "top-left-buttons" }, index.h("kritzel-workspace-manager", { key: '2bce2b9564f5a083ba32667718685310d060f23a', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), index.h("kritzel-back-to-content", { key: 'b013213fc04fe86eefcc324911693fcffb8eea51', visible: this.isBackToContentButtonVisible, onBackToContent: () => this.backToContent() })), index.h("kritzel-engine", { key: '7338446462bfdc3995c0897704e9594a875895e1', ref: el => (this.engineRef = el), workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, scaleMin: this.scaleMin, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, wheelEnabled: this.wheelEnabled, theme: this.currentTheme, 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) }), index.h("kritzel-controls", { key: '97b9d8805446afc101f01d99bc96bc24a7054722', class: { 'keyboard-open': this.isVirtualKeyboardOpen }, style: { display: this.isControlsVisible ? 'flex' : 'none' }, ref: el => (this.controlsRef = el), controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState, theme: this.currentTheme, onIsControlsReady: () => (this.isControlsReady = true) }), index.h("div", { key: '0986b1e82b8fbf989e716fe07a401faca618f642', class: "top-right-buttons" }, index.h("kritzel-settings", { key: '6f4cfe92bb54f0de291b0a0482742b859e86ecf3', ref: el => (this.settingsRef = el), shortcuts: this.shortcuts, editorId: this.editorId, onSettingsChange: event => this.handleSettingsChange(event) }), index.h("kritzel-export", { key: 'd669ecae49f721e765d1f8d2f6725633c28cd635', ref: el => (this.exportRef = el), workspaceName: this.activeWorkspace?.name || 'workspace', onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), index.h("kritzel-active-users", { key: 'e44843dcdc9c0ff1e760aadac3f2ec79a7371aba', users: this.activeUsers }), shouldShowCurrentUser && index.h("kritzel-current-user", { key: '364e09a58fd147f29ce7402406907c6c633124bc', user: this.user }), shouldShowLoginButton && index.h("kritzel-button", { key: '79705ba4c5f991229ae08b90a3397bd3bac56333', onButtonClick: () => this.loginDialogRef?.open() }, "Sign in"), index.h("kritzel-more-menu", { key: '95bd3a7f755aa941bd3d9ffdbc2c7f433e5beaaa', items: this.moreMenuItems }), index.h("kritzel-share-dialog", { key: '94f6cfdb67b82d35c6e86f37c88c2ec323234360', ref: el => (this.shareDialogRef = el), isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (index.h("kritzel-login-dialog", { key: 'bc4fd1ec6e61f4d4e61c043ad33a0b0e2c3c8792', ref: el => (this.loginDialogRef = el), providers: this.loginConfig.providers, dialogTitle: this.loginConfig.title, subtitle: this.loginConfig.subtitle, onProviderLogin: this.handleProviderLogin })))));
2548
+ return (index.h(index.Host, { key: 'd1944655652eb2939e3696ed70050baaca0871e2' }, index.h("div", { key: 'ce5dfd9b8f8d9927e9a6ace4056f9053163c7e0a', class: "top-left-buttons" }, index.h("kritzel-workspace-manager", { key: 'e127a667c6e4e853bb4f0c9ffd0edaa6d80d9dfa', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), index.h("kritzel-back-to-content", { key: 'a9abb378f3c79f356d2164cbddf6badba10188a8', visible: this.isBackToContentButtonVisible, onBackToContent: () => this.backToContent() })), index.h("kritzel-engine", { key: 'c5f764580f3623eeb9381e54769a891a48c04620', ref: el => (this.engineRef = el), workspace: this.activeWorkspace, activeWorkspaceId: this.activeWorkspaceId, editorId: this.editorId, syncConfig: this.syncConfig, user: this.user, scaleMax: this.scaleMax, lockDrawingScale: this.lockDrawingScale, scaleMin: this.scaleMin, viewportBoundaryLeft: this.viewportBoundaryLeft, viewportBoundaryRight: this.viewportBoundaryRight, viewportBoundaryTop: this.viewportBoundaryTop, viewportBoundaryBottom: this.viewportBoundaryBottom, wheelEnabled: this.wheelEnabled, theme: this.currentTheme, 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) }), index.h("kritzel-controls", { key: '2479b6979d5b2b6c55a873bd1d8468b5fd967f72', class: { 'keyboard-open': this.isVirtualKeyboardOpen }, style: { display: this.isControlsVisible ? 'flex' : 'none' }, ref: el => (this.controlsRef = el), controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState, theme: this.currentTheme, onIsControlsReady: () => (this.isControlsReady = true) }), index.h("div", { key: 'ec6ac72ba154abe0738259464f20cae9e7dbdd9e', class: "top-right-buttons" }, index.h("kritzel-settings", { key: 'f68f791d673b30331e55f358b9c71b3e2a9a8388', ref: el => (this.settingsRef = el), shortcuts: this.shortcuts, editorId: this.editorId, onSettingsChange: event => this.handleSettingsChange(event) }), index.h("kritzel-export", { key: 'dfe5a051caccee504df4f347705303fa1f812556', ref: el => (this.exportRef = el), workspaceName: this.activeWorkspace?.name || 'workspace', onExportPng: () => this.engineRef.exportViewportAsPng(), onExportSvg: () => this.engineRef.exportViewportAsSvg(), onExportJson: event => this.engineRef.downloadAsJson(event.detail) }), index.h("kritzel-active-users", { key: '48983a14d3902cd238cf0d9ed302ed561a655a9a', users: this.activeUsers }), shouldShowCurrentUser && index.h("kritzel-current-user", { key: '589bdc895741a2d17f60c4b549d14fe8fff74c17', user: this.user }), shouldShowLoginButton && index.h("kritzel-button", { key: '50414a9d7ade623678c767ce63c49f5e99336bed', onButtonClick: () => this.loginDialogRef?.open() }, "Sign in"), index.h("kritzel-more-menu", { key: 'db3069ee0b093acd238750df81c6f426139c9b87', items: this.moreMenuItems }), index.h("kritzel-share-dialog", { key: 'e16057838f00b2c3f239e794b4310bd2690d16e2', ref: el => (this.shareDialogRef = el), isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (index.h("kritzel-login-dialog", { key: 'a6d1fc06c983542c6af95e7bb4b2b2d522a03605', ref: el => (this.loginDialogRef = el), providers: this.loginConfig.providers, dialogTitle: this.loginConfig.title, subtitle: this.loginConfig.subtitle, onProviderLogin: this.handleProviderLogin })))));
2293
2549
  }
2294
2550
  static get watchers() { return {
2295
2551
  "isEngineReady": [{
@@ -19712,6 +19968,7 @@ class KritzelViewport {
19712
19968
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
19713
19969
  const activePointers = Array.from(this._core.store.state.pointers.values());
19714
19970
  if (activePointers.length === 2) {
19971
+ this._core.store.state.objects?.clearCursorPosition();
19715
19972
  const currentPath = this._core.store.currentPath;
19716
19973
  if (currentPath) {
19717
19974
  this._core.store.state.objects.remove(obj => obj.id === currentPath.id);
@@ -19772,10 +20029,15 @@ class KritzelViewport {
19772
20029
  const hostRect = this._core.store.state.host.getBoundingClientRect();
19773
20030
  const xRelativeToHost = event.clientX - hostRect.left;
19774
20031
  const yRelativeToHost = event.clientY - hostRect.top;
19775
- this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
19776
- this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
19777
- this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
19778
20032
  const activePointers = Array.from(this._core.store.state.pointers.values());
20033
+ if (this._core.store.state.isScaling || activePointers.length > 1) {
20034
+ this._core.store.state.objects?.clearCursorPosition();
20035
+ }
20036
+ else {
20037
+ this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
20038
+ this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
20039
+ this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
20040
+ }
19779
20041
  if (activePointers.length === 2) {
19780
20042
  const firstTouchX = activePointers[0].clientX - this._core.store.offsetX;
19781
20043
  const firstTouchY = activePointers[0].clientY - this._core.store.offsetY;
@@ -20414,6 +20676,7 @@ class KritzelCustomElement extends workspace_migrations.KritzelBaseObject {
20414
20676
  object._core = core;
20415
20677
  object.id = object.generateId();
20416
20678
  object.workspaceId = core.store.state.activeWorkspace.id;
20679
+ object.userId = core.user?.id;
20417
20680
  return object;
20418
20681
  }
20419
20682
  /**
@@ -21002,6 +21265,7 @@ class KritzelObjectMap {
21002
21265
  _stackItemPoppedHandler = null;
21003
21266
  _awarenessChangeHandler = null;
21004
21267
  _awarenessChangeCallbacks = [];
21268
+ _objectsChangeCallbacks = [];
21005
21269
  _lastAwarenessEmitTime = 0;
21006
21270
  _awarenessEmitTimeout = null;
21007
21271
  AWARENESS_THROTTLE_INTERVAL = 100; // milliseconds
@@ -21018,6 +21282,19 @@ class KritzelObjectMap {
21018
21282
  get awareness() {
21019
21283
  return this._awareness;
21020
21284
  }
21285
+ /**
21286
+ * Whether a network awareness instance is available.
21287
+ */
21288
+ get hasAwareness() {
21289
+ return !!this._awareness;
21290
+ }
21291
+ /**
21292
+ * Returns the local client ID from the awareness instance.
21293
+ * Used to filter out the local user when rendering remote cursors.
21294
+ */
21295
+ get localClientId() {
21296
+ return this._awareness?.clientID ?? null;
21297
+ }
21021
21298
  /**
21022
21299
  * Sets the local user identity in the awareness state.
21023
21300
  * This broadcasts the user's identity to all connected peers.
@@ -21026,12 +21303,21 @@ class KritzelObjectMap {
21026
21303
  if (!this._awareness || !user) {
21027
21304
  return;
21028
21305
  }
21306
+ const displayName = user.displayName || user.firstName || 'Anonymous';
21029
21307
  this._awareness.setLocalStateField('user', {
21030
21308
  id: user.id,
21031
- name: user.displayName || user.firstName || 'Anonymous',
21032
- color: user.color,
21309
+ displayName,
21310
+ color: user.color || this.generateColorFromName(displayName),
21033
21311
  });
21034
21312
  }
21313
+ generateColorFromName(name) {
21314
+ let hash = 0;
21315
+ for (let i = 0; i < name.length; i++) {
21316
+ hash = name.charCodeAt(i) + ((hash << 5) - hash);
21317
+ }
21318
+ const hue = Math.abs(hash % 360);
21319
+ return `hsl(${hue}, 45%, 55%)`;
21320
+ }
21035
21321
  /**
21036
21322
  * Updates the local cursor position in the awareness state.
21037
21323
  * This broadcasts the cursor position to all connected peers.
@@ -21051,6 +21337,37 @@ class KritzelObjectMap {
21051
21337
  }
21052
21338
  this._awareness.setLocalStateField('cursor', null);
21053
21339
  }
21340
+ /**
21341
+ * Sets the ID of the object currently being drawn by the local user.
21342
+ * Remote clients use this to derive cursor position from the object's latest
21343
+ * coordinates instead of the throttled awareness cursor, reducing desync.
21344
+ * Pass `null` when drawing ends.
21345
+ */
21346
+ setActiveDrawingObject(objectId) {
21347
+ if (!this._awareness) {
21348
+ return;
21349
+ }
21350
+ this._awareness.setLocalStateField('activeObjectId', objectId);
21351
+ }
21352
+ /**
21353
+ * Broadcasts the local user's selection box bounds via awareness.
21354
+ * Remote clients use this to render the selection rectangle in the user's color.
21355
+ */
21356
+ setLocalSelectionBox(box) {
21357
+ if (!this._awareness) {
21358
+ return;
21359
+ }
21360
+ this._awareness.setLocalStateField('selectionBox', box);
21361
+ }
21362
+ /**
21363
+ * Clears the local selection box from awareness (e.g., when selection completes or is cancelled).
21364
+ */
21365
+ clearLocalSelectionBox() {
21366
+ if (!this._awareness) {
21367
+ return;
21368
+ }
21369
+ this._awareness.setLocalStateField('selectionBox', null);
21370
+ }
21054
21371
  /**
21055
21372
  * Registers a callback to be invoked when the awareness state changes.
21056
21373
  * The callback receives the full awareness states map.
@@ -21058,6 +21375,14 @@ class KritzelObjectMap {
21058
21375
  onAwarenessChange(callback) {
21059
21376
  this._awarenessChangeCallbacks.push(callback);
21060
21377
  }
21378
+ /**
21379
+ * Registers a callback to be invoked when remote object changes are received.
21380
+ * Used by the awareness cursors component to re-derive cursor positions
21381
+ * from the latest object data when a remote user is actively drawing.
21382
+ */
21383
+ onObjectsChange(callback) {
21384
+ this._objectsChangeCallbacks.push(callback);
21385
+ }
21061
21386
  /**
21062
21387
  * Returns the Yjs UndoManager instance for managing undo/redo operations.
21063
21388
  * @returns The UndoManager instance, or `null` if not initialized
@@ -21176,16 +21501,28 @@ class KritzelObjectMap {
21176
21501
  this.handleObjectsChange(event);
21177
21502
  };
21178
21503
  this._objectsMap.observe(this._objectsObserver);
21179
- // Connect all providers in parallel (settle individually so one failure doesn't block the rest)
21180
- const results = await Promise.allSettled(this._providers.map(p => p.connect()));
21181
- results.forEach((result, i) => {
21504
+ // Separate local providers (IndexedDB, BroadcastChannel) from network providers (Hocuspocus, WebSocket)
21505
+ // Local providers are awaited so data is available immediately; network providers sync in the background
21506
+ // via Yjs observers that are already registered above.
21507
+ const localProviders = this._providers.filter(p => p.type === 'local');
21508
+ const networkProviders = this._providers.filter(p => p.type === 'network');
21509
+ // Await local providers for immediate data availability
21510
+ const localResults = await Promise.allSettled(localProviders.map(p => p.connect()));
21511
+ localResults.forEach((result, i) => {
21182
21512
  if (result.status === 'rejected') {
21183
- console.error(`[Kritzel] Sync provider "${this._providers[i]?.constructor.name}" failed to connect:`, result.reason);
21513
+ console.error(`[Kritzel] Sync provider "${localProviders[i]?.constructor.name}" failed to connect:`, result.reason);
21184
21514
  }
21185
21515
  });
21516
+ // Connect network providers in the background (remote data arrives via Yjs observers)
21517
+ for (const provider of networkProviders) {
21518
+ provider.connect().catch(err => {
21519
+ console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
21520
+ });
21521
+ }
21186
21522
  this._isReady = true;
21187
21523
  // Find the first provider that exposes awareness (network providers)
21188
- for (const provider of this._providers) {
21524
+ // Awareness is available immediately after provider construction, before connect() resolves
21525
+ for (const provider of networkProviders) {
21189
21526
  if (provider.awareness) {
21190
21527
  this._awareness = provider.awareness;
21191
21528
  break;
@@ -21356,6 +21693,10 @@ class KritzelObjectMap {
21356
21693
  if (updatedObjects.length > 0) {
21357
21694
  this._core?.engine.emitObjectsUpdated(updatedObjects.map(obj => ({ object: obj, changedProperties: [] })));
21358
21695
  }
21696
+ // Notify subscribers of remote object changes (e.g., awareness cursors)
21697
+ for (const callback of this._objectsChangeCallbacks) {
21698
+ callback();
21699
+ }
21359
21700
  }
21360
21701
  /**
21361
21702
  * Initializes document metadata if not already set.
@@ -21782,6 +22123,7 @@ class KritzelStore {
21782
22123
  _cachedSelectionGroup = null;
21783
22124
  _selectionBoxCacheValid = false;
21784
22125
  _selectionGroupCacheValid = false;
22126
+ _localUserId = null;
21785
22127
  /**
21786
22128
  * Gets the current engine state.
21787
22129
  * @returns The mutable engine state object
@@ -21859,9 +22201,7 @@ class KritzelStore {
21859
22201
  height: this._state.viewportHeight / this._state.scale,
21860
22202
  depth: 100,
21861
22203
  };
21862
- return this._state.objects
21863
- .query(viewportBounds)
21864
- .sort((a, b) => a.zIndex - b.zIndex);
22204
+ return this._state.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
21865
22205
  }
21866
22206
  /**
21867
22207
  * Gets all objects excluding selection-related objects (selection group and selection box).
@@ -21892,16 +22232,22 @@ class KritzelStore {
21892
22232
  return this._cachedSelectionBox;
21893
22233
  }
21894
22234
  /**
21895
- * Gets the current selection group if one exists.
22235
+ * Gets the current local user's selection group if one exists.
22236
+ * When a local user ID is set, returns only the selection group belonging to that user.
21896
22237
  * Uses caching for O(1) access after initial lookup.
21897
- * @returns The selection group or null if none exists
22238
+ * @returns The local user's selection group or null if none exists
21898
22239
  */
21899
22240
  get selectionGroup() {
21900
22241
  if (this._selectionGroupCacheValid) {
21901
22242
  return this._cachedSelectionGroup;
21902
22243
  }
21903
22244
  const selectionGroups = this._state.objects.filter(o => o instanceof workspace_migrations.KritzelSelectionGroup);
21904
- this._cachedSelectionGroup = selectionGroups.length > 0 ? selectionGroups[0] : null;
22245
+ if (this._localUserId) {
22246
+ this._cachedSelectionGroup = selectionGroups.find(sg => sg.userId === this._localUserId) ?? null;
22247
+ }
22248
+ else {
22249
+ this._cachedSelectionGroup = selectionGroups.length > 0 ? selectionGroups[0] : null;
22250
+ }
21905
22251
  this._selectionGroupCacheValid = true;
21906
22252
  return this._cachedSelectionGroup;
21907
22253
  }
@@ -21930,6 +22276,15 @@ class KritzelStore {
21930
22276
  this._cachedSelectionGroup = group;
21931
22277
  this._selectionGroupCacheValid = true;
21932
22278
  }
22279
+ /**
22280
+ * Sets the local user ID for scoping selection group lookups.
22281
+ * When set, the selectionGroup getter returns only the group owned by this user.
22282
+ * @param userId - The local user's ID, or null for non-collaborative mode
22283
+ */
22284
+ setLocalUserId(userId) {
22285
+ this._localUserId = userId;
22286
+ this.invalidateSelectionCache();
22287
+ }
21933
22288
  /**
21934
22289
  * Gets the currently active text object being edited.
21935
22290
  * @returns The text object in editing mode, or null if none
@@ -22114,13 +22469,24 @@ class KritzelAppStateMap {
22114
22469
  this.handleWorkspacesChange(event);
22115
22470
  };
22116
22471
  this._workspacesMap.observe(this._workspacesObserver);
22117
- // Connect all providers in parallel (settle individually so one failure doesn't block the rest)
22118
- const results = await Promise.allSettled(this._providers.map(p => p.connect()));
22119
- results.forEach((result, i) => {
22472
+ // Separate local providers (IndexedDB, BroadcastChannel) from network providers (Hocuspocus, WebSocket)
22473
+ // Local providers are awaited so data is available immediately; network providers sync in the background
22474
+ // via Yjs observers that are already registered above.
22475
+ const localProviders = this._providers.filter(p => p.type === 'local');
22476
+ const networkProviders = this._providers.filter(p => p.type === 'network');
22477
+ // Await local providers for immediate data availability
22478
+ const localResults = await Promise.allSettled(localProviders.map(p => p.connect()));
22479
+ localResults.forEach((result, i) => {
22120
22480
  if (result.status === 'rejected') {
22121
- console.error(`[Kritzel] Sync provider "${this._providers[i]?.constructor.name}" failed to connect:`, result.reason);
22481
+ console.error(`[Kritzel] Sync provider "${localProviders[i]?.constructor.name}" failed to connect:`, result.reason);
22122
22482
  }
22123
22483
  });
22484
+ // Connect network providers in the background (remote data arrives via Yjs observers)
22485
+ for (const provider of networkProviders) {
22486
+ provider.connect().catch(err => {
22487
+ console.error(`[Kritzel] Network sync provider "${provider.constructor.name}" failed to connect:`, err);
22488
+ });
22489
+ }
22124
22490
  this._isReady = true;
22125
22491
  // Run any pending schema migrations before loading data
22126
22492
  const quietMigrations = !core.store.state.debugInfo.showMigrationInfo;
@@ -22679,6 +23045,7 @@ class KritzelCore {
22679
23045
  */
22680
23046
  setUser(user) {
22681
23047
  this._user = user;
23048
+ this._store.setLocalUserId(user?.id ?? null);
22682
23049
  this._store.state.objects?.setLocalUser(user);
22683
23050
  }
22684
23051
  /**
@@ -23078,6 +23445,7 @@ class KritzelCore {
23078
23445
  if (selectionBox) {
23079
23446
  this._store.state.objects.remove(object => object.id === selectionBox.id);
23080
23447
  this._store.setSelectionBox(null);
23448
+ this._store.state.objects.clearLocalSelectionBox();
23081
23449
  }
23082
23450
  }
23083
23451
  /**
@@ -23550,7 +23918,9 @@ class KritzelCore {
23550
23918
  */
23551
23919
  clearSelection() {
23552
23920
  this.removeSelectionGroup();
23553
- this._store.state.objects.remove(o => o instanceof workspace_migrations.KritzelSelectionBox || o instanceof workspace_migrations.KritzelSelectionGroup);
23921
+ const localUserId = this._user?.id;
23922
+ this._store.state.objects.remove(o => o instanceof workspace_migrations.KritzelSelectionBox ||
23923
+ (o instanceof workspace_migrations.KritzelSelectionGroup && (localUserId == null || o.userId === localUserId || o.userId == null)));
23554
23924
  this._store.setSelectionBox(null);
23555
23925
  this._store.setSelectionGroup(null);
23556
23926
  this._store.state.isSelecting = false;
@@ -23704,6 +24074,10 @@ class KritzelCore {
23704
24074
  if (!object.isSelected) {
23705
24075
  return false;
23706
24076
  }
24077
+ // Remote selection groups always show group UI (border only, no handles)
24078
+ if (object instanceof workspace_migrations.KritzelSelectionGroup && this._user?.id != null && object.userId != null && object.userId !== this._user.id) {
24079
+ return true;
24080
+ }
23707
24081
  const selectionGroup = this._store.selectionGroup;
23708
24082
  if (!selectionGroup) {
23709
24083
  // During selection phase (no group yet), hide UI for KritzelLine objects
@@ -25436,6 +25810,7 @@ const KritzelEngine = class {
25436
25810
  this.core.store.state.isContextMenuVisible = false;
25437
25811
  this.core.store.state.objects.remove(o => o instanceof workspace_migrations.KritzelSelectionBox);
25438
25812
  this.core.store.setSelectionBox(null);
25813
+ this.core.store.state.objects.clearLocalSelectionBox();
25439
25814
  this.core.store.state.isSelecting = false;
25440
25815
  this.core.store.state.isEnabled = true;
25441
25816
  this.core.rerender();
@@ -26383,6 +26758,7 @@ const KritzelEngine = class {
26383
26758
  this.core.clearSelection();
26384
26759
  this.core.store.state.objects.remove(o => o instanceof workspace_migrations.KritzelSelectionBox);
26385
26760
  this.core.store.setSelectionBox(null);
26761
+ this.core.store.state.objects.clearLocalSelectionBox();
26386
26762
  this.core.store.state.isSelecting = false;
26387
26763
  this.core.store.state.isResizeHandleSelected = false;
26388
26764
  this.core.store.state.isRotationHandleSelected = false;
@@ -26559,114 +26935,153 @@ const KritzelEngine = class {
26559
26935
  left: `${object.totalWidth}px`,
26560
26936
  top: '0',
26561
26937
  zIndex: (object.zIndex + 2).toString(),
26562
- } }, index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "Id: ", object.id), 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' } }, "rotationDegrees: ", object.rotationDegrees), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "zIndex: ", object.zIndex))), (this.core.displaySelectionGroupUI(object) || this.core.displaySelectionLineUI(object)) && (index.h("svg", { xmlns: "http://www.w3.org/2000/svg", style: {
26563
- zIndex: (object.zIndex + 1).toString(),
26564
- height: object?.totalHeight.toString(),
26565
- width: object?.totalWidth.toString(),
26566
- left: '0',
26567
- top: '0',
26568
- position: 'absolute',
26569
- transform: `rotate(${object.rotationDegrees}deg)`,
26570
- transformOrigin: `${object.totalWidth / 2}px ${object.totalHeight / 2}px`,
26571
- overflow: 'visible',
26572
- pointerEvents: 'none',
26573
- } }, this.core.displaySelectionGroupUI(object) && (index.h("g", { class: "selection-group-ui", style: { pointerEvents: 'none' } }, index.h("g", { class: "selection-group-borders" }, index.h("line", { x1: "0", y1: "0", x2: object.totalWidth, y2: "0", style: {
26574
- stroke: 'var(--kritzel-selection-border-color, #007AFF)',
26575
- strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26576
- strokeLinecap: 'square',
26577
- } }), index.h("line", { x1: "0", y1: "0", x2: "0", y2: object.totalHeight, style: {
26578
- stroke: 'var(--kritzel-selection-border-color, #007AFF)',
26579
- strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26580
- strokeLinecap: 'square',
26581
- } }), index.h("line", { x1: "0", y1: object.totalHeight, x2: object.totalWidth, y2: object.totalHeight, style: {
26582
- stroke: 'var(--kritzel-selection-border-color, #007AFF)',
26583
- strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26584
- strokeLinecap: 'square',
26585
- } }), index.h("line", { x1: object.totalWidth, y1: "0", x2: object.totalWidth, y2: object.totalHeight, style: {
26586
- stroke: 'var(--kritzel-selection-border-color, #007AFF)',
26587
- strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26588
- strokeLinecap: 'square',
26589
- } })), !this.isSelecting && (index.h("g", { class: "selection-group-handles", style: { pointerEvents: 'auto' } }, index.h("rect", { class: "resize-handle top-left", x: `${(-(baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, y: `${(-(baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, width: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, height: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, style: {
26590
- fill: 'var(--kritzel-selection-handle-color, #ffffff)',
26591
- stroke: 'var(--kritzel-selection-handle-stroke-color, #007AFF)',
26592
- strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
26593
- paintOrder: 'fill',
26594
- } }), index.h("rect", { class: "resize-handle-overlay top-left", x: `${(-baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, y: `${(-baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, width: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, height: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, style: {
26595
- fill: 'transparent',
26596
- paintOrder: 'fill',
26597
- } }), index.h("rect", { class: "resize-handle top-right", x: `${object.totalWidth - ((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, y: `${(-(baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, width: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, height: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, style: {
26598
- fill: 'var(--kritzel-selection-handle-color, #ffffff)',
26599
- stroke: 'var(--kritzel-selection-handle-stroke-color, #007AFF)',
26600
- strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
26601
- paintOrder: 'fill',
26602
- } }), index.h("rect", { class: "resize-handle-overlay top-right", x: `${object.totalWidth - (baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, y: `${(-baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, width: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, height: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, style: {
26603
- fill: 'transparent',
26604
- paintOrder: 'fill',
26605
- } }), index.h("rect", { class: "resize-handle bottom-left", x: `${(-(baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, y: `${object.totalHeight - ((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, width: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, height: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, style: {
26606
- fill: 'var(--kritzel-selection-handle-color, #ffffff)',
26607
- stroke: 'var(--kritzel-selection-handle-stroke-color, #007AFF)',
26608
- strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
26609
- paintOrder: 'fill',
26610
- } }), index.h("rect", { class: "resize-handle-overlay bottom-left", x: `${(-baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, y: `${object.totalHeight - (baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, width: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, height: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, style: {
26611
- fill: 'transparent',
26612
- paintOrder: 'fill',
26613
- } }), index.h("rect", { class: "resize-handle bottom-right", x: `${object.totalWidth - ((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, y: `${object.totalHeight - ((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, width: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, height: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, style: {
26614
- fill: 'var(--kritzel-selection-handle-color, #ffffff)',
26615
- stroke: 'var(--kritzel-selection-handle-stroke-color, #007AFF)',
26616
- strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
26617
- paintOrder: 'fill',
26618
- } }), index.h("rect", { class: "resize-handle-overlay bottom-right", x: `${object.totalWidth - (baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, y: `${object.totalHeight - (baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, width: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, height: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, style: {
26619
- fill: 'transparent',
26620
- paintOrder: 'fill',
26621
- } }), index.h("line", { x1: object.totalWidth / 2, y1: "0", x2: object.totalWidth / 2, y2: -((15 * object.scale) / this.core.store.state?.scale), style: {
26622
- stroke: 'var(--kritzel-selection-border-color, #007AFF)',
26623
- strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26624
- } }), index.h("circle", { class: "rotation-handle", cx: object.totalWidth / 2, cy: -((15 * object.scale) / this.core.store.state?.scale), r: `${((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, style: {
26625
- fill: 'var(--kritzel-selection-handle-color, #ffffff)',
26626
- stroke: 'var(--kritzel-selection-handle-stroke-color, #007AFF)',
26627
- strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
26628
- paintOrder: 'fill',
26629
- } }), index.h("circle", { class: "rotation-handle-overlay", cx: object.totalWidth / 2, cy: -((15 * object.scale) / this.core.store.state?.scale), r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
26630
- fill: 'transparent',
26631
- paintOrder: 'fill',
26632
- } }))))), this.core.displaySelectionLineUI(object) && workspace_migrations.KritzelClassHelper.isInstanceOf(object, 'KritzelLine') && (index.h("g", { class: "selection-line-ui", style: { pointerEvents: 'none' } }, index.h("g", { class: "selection-line-borders" }, index.h("path", { class: "selection-line-border", d: this.core.anchorManager.computeClippedLinePath(object, true), style: {
26633
- stroke: 'var(--kritzel-selection-border-color, #007AFF)',
26634
- strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26635
- strokeLinecap: 'round',
26636
- fill: 'none',
26637
- } })), !this.isSelecting && (index.h("g", { class: "selection-line-handles", style: { pointerEvents: 'auto' } }, index.h("circle", { class: "selection-line-handle start", cx: object.startX - object.x, cy: object.startY - object.y, r: `${((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, style: {
26638
- fill: 'var(--kritzel-selection-handle-color, #000000)',
26639
- stroke: 'var(--kritzel-selection-handle-stroke-color, #007AFF)',
26640
- strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
26641
- paintOrder: 'fill',
26642
- } }), index.h("circle", { class: "selection-line-handle-overlay start", "data-testid": "line-handle-start", cx: object.startX - object.x, cy: object.startY - object.y, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
26643
- fill: 'transparent',
26644
- paintOrder: 'fill',
26645
- } }), index.h("circle", { class: "selection-line-handle center", cx: object.controlX !== undefined
26646
- ? (object.startX + 2 * object.controlX + object.endX) / 4 - object.x
26647
- : (object.startX - object.x + object.endX - object.x) / 2, cy: object.controlY !== undefined
26648
- ? (object.startY + 2 * object.controlY + object.endY) / 4 - object.y
26649
- : (object.startY - object.y + object.endY - object.y) / 2, r: `${((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, style: {
26650
- fill: 'var(--kritzel-selection-handle-color, #000000)',
26651
- stroke: 'var(--kritzel-selection-handle-stroke-color, #007AFF)',
26652
- strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
26653
- paintOrder: 'fill',
26654
- } }), index.h("circle", { class: "selection-line-handle-overlay center", "data-testid": "line-handle-center", cx: object.controlX !== undefined
26655
- ? (object.startX + 2 * object.controlX + object.endX) / 4 - object.x
26656
- : (object.startX - object.x + object.endX - object.x) / 2, cy: object.controlY !== undefined
26657
- ? (object.startY + 2 * object.controlY + object.endY) / 4 - object.y
26658
- : (object.startY - object.y + object.endY - object.y) / 2, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
26659
- fill: 'transparent',
26660
- paintOrder: 'fill',
26661
- } }), index.h("circle", { class: "selection-line-handle end", cx: object.endX - object.x, cy: object.endY - object.y, r: `${((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, style: {
26662
- fill: 'var(--kritzel-selection-handle-color, #000000)',
26663
- stroke: 'var(--kritzel-selection-handle-stroke-color, #007AFF)',
26664
- strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
26665
- paintOrder: 'fill',
26666
- } }), index.h("circle", { class: "selection-line-handle-overlay end", "data-testid": "line-handle-end", cx: object.endX - object.x, cy: object.endY - object.y, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
26667
- fill: 'transparent',
26668
- paintOrder: 'fill',
26669
- } })))))))));
26938
+ } }, 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' } }, "rotationDegrees: ", object.rotationDegrees), index.h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "zIndex: ", object.zIndex))), (this.core.displaySelectionGroupUI(object) || this.core.displaySelectionLineUI(object)) &&
26939
+ (() => {
26940
+ const isRemoteSelection = workspace_migrations.KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup') &&
26941
+ object.userId != null &&
26942
+ this.core.user?.id != null &&
26943
+ object.userId !== this.core.user.id;
26944
+ let remoteUserColor;
26945
+ if (isRemoteSelection) {
26946
+ const awarenessStates = this.core.store.state.objects?.awareness?.getStates();
26947
+ if (awarenessStates) {
26948
+ for (const state of awarenessStates.values()) {
26949
+ if (state.user?.id === object.userId) {
26950
+ remoteUserColor = state.user.color;
26951
+ break;
26952
+ }
26953
+ }
26954
+ }
26955
+ }
26956
+ const selectionBorderColor = remoteUserColor
26957
+ ?? (workspace_migrations.KritzelColorHelper.resolveThemeColor(object.borderColor, currentTheme) || 'var(--kritzel-selection-border-color, #007AFF)');
26958
+ const selectionHandleStrokeColor = remoteUserColor
26959
+ ?? 'var(--kritzel-selection-handle-stroke-color, #007AFF)';
26960
+ return (index.h("svg", { xmlns: "http://www.w3.org/2000/svg", style: {
26961
+ zIndex: (object.zIndex + 1).toString(),
26962
+ height: object?.totalHeight.toString(),
26963
+ width: object?.totalWidth.toString(),
26964
+ left: '0',
26965
+ top: '0',
26966
+ position: 'absolute',
26967
+ transform: `rotate(${object.rotationDegrees}deg)`,
26968
+ transformOrigin: `${object.totalWidth / 2}px ${object.totalHeight / 2}px`,
26969
+ overflow: 'visible',
26970
+ pointerEvents: 'none',
26971
+ } }, this.core.displaySelectionGroupUI(object) && (index.h("g", { class: "selection-group-ui", style: { pointerEvents: 'none' } }, (() => {
26972
+ const remoteLineObject = isRemoteSelection &&
26973
+ workspace_migrations.KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup') &&
26974
+ object.objects.length === 1 &&
26975
+ workspace_migrations.KritzelClassHelper.isInstanceOf(object.objects[0], 'KritzelLine')
26976
+ ? object.objects[0]
26977
+ : null;
26978
+ if (remoteLineObject) {
26979
+ return (index.h("g", { class: "selection-line-borders" }, index.h("path", { class: "selection-line-border", d: this.core.anchorManager.computeClippedLinePath(remoteLineObject, true), style: {
26980
+ stroke: selectionBorderColor,
26981
+ strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26982
+ strokeLinecap: 'round',
26983
+ fill: 'none',
26984
+ } })));
26985
+ }
26986
+ return (index.h("g", { class: "selection-group-borders" }, index.h("line", { x1: "0", y1: "0", x2: object.totalWidth, y2: "0", style: {
26987
+ stroke: selectionBorderColor,
26988
+ strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26989
+ strokeLinecap: 'square',
26990
+ } }), index.h("line", { x1: "0", y1: "0", x2: "0", y2: object.totalHeight, style: {
26991
+ stroke: selectionBorderColor,
26992
+ strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26993
+ strokeLinecap: 'square',
26994
+ } }), index.h("line", { x1: "0", y1: object.totalHeight, x2: object.totalWidth, y2: object.totalHeight, style: {
26995
+ stroke: selectionBorderColor,
26996
+ strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
26997
+ strokeLinecap: 'square',
26998
+ } }), index.h("line", { x1: object.totalWidth, y1: "0", x2: object.totalWidth, y2: object.totalHeight, style: {
26999
+ stroke: selectionBorderColor,
27000
+ strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
27001
+ strokeLinecap: 'square',
27002
+ } })));
27003
+ })(), !this.isSelecting && !isRemoteSelection && (index.h("g", { class: "selection-group-handles", style: { pointerEvents: 'auto' } }, index.h("rect", { class: "resize-handle top-left", x: `${(-(baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, y: `${(-(baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, width: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, height: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, style: {
27004
+ fill: 'var(--kritzel-selection-handle-color, #ffffff)',
27005
+ stroke: selectionHandleStrokeColor,
27006
+ strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
27007
+ paintOrder: 'fill',
27008
+ } }), index.h("rect", { class: "resize-handle-overlay top-left", x: `${(-baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, y: `${(-baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, width: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, height: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, style: {
27009
+ fill: 'transparent',
27010
+ paintOrder: 'fill',
27011
+ } }), index.h("rect", { class: "resize-handle top-right", x: `${object.totalWidth - ((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, y: `${(-(baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, width: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, height: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, style: {
27012
+ fill: 'var(--kritzel-selection-handle-color, #ffffff)',
27013
+ stroke: selectionHandleStrokeColor,
27014
+ strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
27015
+ paintOrder: 'fill',
27016
+ } }), index.h("rect", { class: "resize-handle-overlay top-right", x: `${object.totalWidth - (baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, y: `${(-baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, width: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, height: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, style: {
27017
+ fill: 'transparent',
27018
+ paintOrder: 'fill',
27019
+ } }), index.h("rect", { class: "resize-handle bottom-left", x: `${(-(baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, y: `${object.totalHeight - ((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, width: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, height: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, style: {
27020
+ fill: 'var(--kritzel-selection-handle-color, #ffffff)',
27021
+ stroke: selectionHandleStrokeColor,
27022
+ strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
27023
+ paintOrder: 'fill',
27024
+ } }), index.h("rect", { class: "resize-handle-overlay bottom-left", x: `${(-baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, y: `${object.totalHeight - (baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, width: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, height: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, style: {
27025
+ fill: 'transparent',
27026
+ paintOrder: 'fill',
27027
+ } }), index.h("rect", { class: "resize-handle bottom-right", x: `${object.totalWidth - ((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, y: `${object.totalHeight - ((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, width: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, height: `${((baseHandleSize - 1) * 2 * object.scale) / this.core.store.state?.scale}`, style: {
27028
+ fill: 'var(--kritzel-selection-handle-color, #ffffff)',
27029
+ stroke: selectionHandleStrokeColor,
27030
+ strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
27031
+ paintOrder: 'fill',
27032
+ } }), index.h("rect", { class: "resize-handle-overlay bottom-right", x: `${object.totalWidth - (baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, y: `${object.totalHeight - (baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, width: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, height: `${(baseHandleTouchSize * 2 * object.scale) / this.core.store.state?.scale}`, style: {
27033
+ fill: 'transparent',
27034
+ paintOrder: 'fill',
27035
+ } }), index.h("line", { x1: object.totalWidth / 2, y1: "0", x2: object.totalWidth / 2, y2: -((15 * object.scale) / this.core.store.state?.scale), style: {
27036
+ stroke: selectionBorderColor,
27037
+ strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
27038
+ } }), index.h("circle", { class: "rotation-handle", cx: object.totalWidth / 2, cy: -((15 * object.scale) / this.core.store.state?.scale), r: `${((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, style: {
27039
+ fill: 'var(--kritzel-selection-handle-color, #ffffff)',
27040
+ stroke: selectionHandleStrokeColor,
27041
+ strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
27042
+ paintOrder: 'fill',
27043
+ } }), index.h("circle", { class: "rotation-handle-overlay", cx: object.totalWidth / 2, cy: -((15 * object.scale) / this.core.store.state?.scale), r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
27044
+ fill: 'transparent',
27045
+ paintOrder: 'fill',
27046
+ } }))))), this.core.displaySelectionLineUI(object) && workspace_migrations.KritzelClassHelper.isInstanceOf(object, 'KritzelLine') && (index.h("g", { class: "selection-line-ui", style: { pointerEvents: 'none' } }, index.h("g", { class: "selection-line-borders" }, index.h("path", { class: "selection-line-border", d: this.core.anchorManager.computeClippedLinePath(object, true), style: {
27047
+ stroke: selectionBorderColor,
27048
+ strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
27049
+ strokeLinecap: 'round',
27050
+ fill: 'none',
27051
+ } })), !this.isSelecting && !isRemoteSelection && (index.h("g", { class: "selection-line-handles", style: { pointerEvents: 'auto' } }, index.h("circle", { class: "selection-line-handle start", cx: object.startX - object.x, cy: object.startY - object.y, r: `${((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, style: {
27052
+ fill: 'var(--kritzel-selection-handle-color, #000000)',
27053
+ stroke: selectionHandleStrokeColor,
27054
+ strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
27055
+ paintOrder: 'fill',
27056
+ } }), index.h("circle", { class: "selection-line-handle-overlay start", "data-testid": "line-handle-start", cx: object.startX - object.x, cy: object.startY - object.y, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
27057
+ fill: 'transparent',
27058
+ paintOrder: 'fill',
27059
+ } }), index.h("circle", { class: "selection-line-handle center", cx: object.controlX !== undefined
27060
+ ? (object.startX + 2 * object.controlX + object.endX) / 4 - object.x
27061
+ : (object.startX - object.x + object.endX - object.x) / 2, cy: object.controlY !== undefined
27062
+ ? (object.startY + 2 * object.controlY + object.endY) / 4 - object.y
27063
+ : (object.startY - object.y + object.endY - object.y) / 2, r: `${((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, style: {
27064
+ fill: 'var(--kritzel-selection-handle-color, #000000)',
27065
+ stroke: selectionHandleStrokeColor,
27066
+ strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
27067
+ paintOrder: 'fill',
27068
+ } }), index.h("circle", { class: "selection-line-handle-overlay center", "data-testid": "line-handle-center", cx: object.controlX !== undefined
27069
+ ? (object.startX + 2 * object.controlX + object.endX) / 4 - object.x
27070
+ : (object.startX - object.x + object.endX - object.x) / 2, cy: object.controlY !== undefined
27071
+ ? (object.startY + 2 * object.controlY + object.endY) / 4 - object.y
27072
+ : (object.startY - object.y + object.endY - object.y) / 2, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
27073
+ fill: 'transparent',
27074
+ paintOrder: 'fill',
27075
+ } }), index.h("circle", { class: "selection-line-handle end", cx: object.endX - object.x, cy: object.endY - object.y, r: `${((baseHandleSize - 1) * object.scale) / this.core.store.state?.scale}`, style: {
27076
+ fill: 'var(--kritzel-selection-handle-color, #000000)',
27077
+ stroke: selectionHandleStrokeColor,
27078
+ strokeWidth: `calc(2px * ${object.scale} / ${this.core.store.state?.scale})`,
27079
+ paintOrder: 'fill',
27080
+ } }), index.h("circle", { class: "selection-line-handle-overlay end", "data-testid": "line-handle-end", cx: object.endX - object.x, cy: object.endY - object.y, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
27081
+ fill: 'transparent',
27082
+ paintOrder: 'fill',
27083
+ } })))))));
27084
+ })()));
26670
27085
  }), (() => {
26671
27086
  const data = this.core.anchorManager.getAnchorLinesRenderData();
26672
27087
  if (!data)
@@ -26751,7 +27166,7 @@ const KritzelEngine = class {
26751
27166
  y: (-this.core.store.state.translateY + this.core.store.state.contextMenuY) / this.core.store.state.scale,
26752
27167
  }, this.core.store.selectionGroup?.objects);
26753
27168
  this.hideContextMenu();
26754
- }, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof workspace_migrations.KritzelEraserTool && !this.core.store.state.isScaling && index.h("kritzel-cursor-trail", { core: this.core })));
27169
+ }, onClose: () => this.hideContextMenu() })), this.core.store.state.objects?.hasAwareness && index.h("kritzel-awareness-cursors", { core: this.core }), this.core.store.state?.activeTool instanceof workspace_migrations.KritzelEraserTool && !this.core.store.state.isScaling && index.h("kritzel-cursor-trail", { core: this.core })));
26755
27170
  }
26756
27171
  static get watchers() { return {
26757
27172
  "workspace": [{
@@ -26874,7 +27289,7 @@ const KritzelExport = class {
26874
27289
  return (index.h("div", { class: "export-tab-content" }, index.h("kritzel-input", { label: "Filename", value: this.exportFilename, placeholder: "Enter filename", suffix: ".json", onValueChange: this.handleFilenameChange })));
26875
27290
  }
26876
27291
  render() {
26877
- return (index.h(index.Host, { key: 'e0b87fc15a375fdd82283d75b477d987db8b81ca' }, index.h("kritzel-dialog", { key: 'bd360887428ce5f43f6fc8871c6acb8b5e373216', isOpen: this.isDialogOpen, dialogTitle: "Export", closable: true, onDialogClose: this.closeDialog }, index.h("div", { key: '7787b3be81cfbc812bc7430399a9c3ad4c230cdb', class: "export-content" }, index.h("kritzel-pill-tabs", { key: '942677f661c85092233d69b681fbf174968cbb61', tabs: this.tabs, value: this.activeTab, onValueChange: this.handleTabChange }), this.activeTab === 'viewport' && this.renderViewportExport(), this.activeTab === 'workspace' && this.renderWorkspaceExport(), index.h("button", { key: '19a39246beff8b8b36770581e8d1c45b0a9ea5f4', class: "export-primary-button", onClick: this.handleExport }, "Export")))));
27292
+ return (index.h(index.Host, { key: '89824b452ccc845f4dfd1e13c01a24a72e5d9488' }, index.h("kritzel-dialog", { key: 'a9190d093e84b5850146db39e1016abce9de0d83', isOpen: this.isDialogOpen, dialogTitle: "Export", closable: true, onDialogClose: this.closeDialog }, index.h("div", { key: '3068dbb979a029bf580e2d00a721768bc08d12b3', class: "export-content" }, index.h("kritzel-pill-tabs", { key: '6241491cc549f576b822bc9fda37c9edffd9143f', tabs: this.tabs, value: this.activeTab, onValueChange: this.handleTabChange }), this.activeTab === 'viewport' && this.renderViewportExport(), this.activeTab === 'workspace' && this.renderWorkspaceExport(), index.h("button", { key: 'a9fe2a7ccb5b97095271a6e00fe9585f39b137fc', class: "export-primary-button", onClick: this.handleExport }, "Export")))));
26878
27293
  }
26879
27294
  };
26880
27295
  KritzelExport.style = kritzelExportCss();
@@ -26889,7 +27304,7 @@ const KritzelFont = class {
26889
27304
  size = 24;
26890
27305
  color = '#000000';
26891
27306
  render() {
26892
- return (index.h(index.Host, { key: '70bc1c26fc43bffbd3df59c60453e3229725c8d1' }, index.h("div", { key: 'd7ed7c78581f8ce760dd6c143a311ee540a4df94', class: "font-preview", style: {
27307
+ return (index.h(index.Host, { key: '3d5fd9aecf7abc96c438b890f5831281d6194922' }, index.h("div", { key: '2b3df82e475177e803f172f440ffcee97dbe5aa5', class: "font-preview", style: {
26893
27308
  fontFamily: this.fontFamily,
26894
27309
  fontSize: `${this.size}px`,
26895
27310
  color: this.color
@@ -26964,7 +27379,7 @@ const KritzelFontSize = class {
26964
27379
  }
26965
27380
  render() {
26966
27381
  const color = 'var(--kritzel-global-text-primary)';
26967
- return (index.h(index.Host, { key: 'ceea18a28a3b292ee89839a7a183da2000eaf156' }, this.sizes.map(size => (index.h("div", { tabIndex: 0, class: {
27382
+ return (index.h(index.Host, { key: '32615ac4756efd047219b93642f7b5cc7d80669b' }, this.sizes.map(size => (index.h("div", { tabIndex: 0, class: {
26968
27383
  'size-container': true,
26969
27384
  'selected': this.selectedSize === size,
26970
27385
  }, onClick: () => this.handleSizeClick(size), onKeyDown: event => this.handleKeyDown(event, size) }, index.h("kritzel-font", { fontFamily: this.fontFamily, size: size, color: color }))))));
@@ -27031,7 +27446,7 @@ const KritzelInput = class {
27031
27446
  this.valueChange.emit(input.value);
27032
27447
  };
27033
27448
  render() {
27034
- return (index.h(index.Host, { key: '1b151bb26c9398c5c29e3d3367cb6ba21e6864fe' }, index.h("div", { key: 'd2ba0fa75ad80e357d6684928bd48aeaa102a706', class: "input-container" }, this.label && index.h("label", { key: '47ed17ec74d26b29fc7857780d070d6cfac157f1', class: "input-label" }, this.label), index.h("div", { key: 'bfcbe25bf4b876e7f5466a2f48dd63309fb310a5', class: { 'input-wrapper': true, 'has-suffix': !!this.suffix } }, index.h("input", { key: 'ef58442a0bd2fee845a9d35ddc9d04b357d93940', type: this.type, class: "text-input", value: this.inputValue, placeholder: this.placeholder, disabled: this.disabled, onInput: this.handleInput }), this.suffix && index.h("span", { key: '8b6f73557fa2c44cf0ba7720685306e29df44179', class: "input-suffix" }, this.suffix)))));
27449
+ return (index.h(index.Host, { key: '5cfc93853afd27dd24ff9709dcd277571ae4d0a1' }, index.h("div", { key: 'fa0a43610b98ffbc30bf9485a4f8730b80f74ab7', class: "input-container" }, this.label && index.h("label", { key: '151346732ef4a00a1ea78bbc19ac56a94488dfc4', class: "input-label" }, this.label), index.h("div", { key: '79b28f6f45cdacf7d122988bb4c844c929544cce', class: { 'input-wrapper': true, 'has-suffix': !!this.suffix } }, index.h("input", { key: 'e22abd0e10ee231b2e66f50085693769584a1254', type: this.type, class: "text-input", value: this.inputValue, placeholder: this.placeholder, disabled: this.disabled, onInput: this.handleInput }), this.suffix && index.h("span", { key: '5c96287d24dc28f4ac321f108a356527194f4e81', class: "input-suffix" }, this.suffix)))));
27035
27450
  }
27036
27451
  static get watchers() { return {
27037
27452
  "value": [{
@@ -27171,7 +27586,7 @@ const KritzelLoginDialog = class {
27171
27586
  this.dialogClosed.emit();
27172
27587
  };
27173
27588
  render() {
27174
- return (index.h(index.Host, { key: '1daa4d598e883bb5d212d681692bde9de4a09969' }, index.h("kritzel-dialog", { key: 'ae21cc3a8978ea19cba8a07a3a22f791efd20915', dialogTitle: this.dialogTitle, isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small" }, index.h("div", { key: '81c278e44685a040066caccb747f272182b10ee6', class: "login-content" }, this.subtitle && (index.h("p", { key: '9a2478c3713cc119b933fbf7f928f4928d14b0d7', class: "login-subtitle" }, this.subtitle)), index.h("div", { key: '0e273e0c48886f68d0ffec9c46a5427cc8949ddc', class: "login-providers" }, this.providers.map(provider => (index.h("button", { key: provider.name, class: {
27589
+ return (index.h(index.Host, { key: '21b548c3d1391203a02a33e66afa5d3dae603071' }, index.h("kritzel-dialog", { key: '1254e8bdc8e84e71e606109e20f938ecb42b05e3', dialogTitle: this.dialogTitle, isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small" }, index.h("div", { key: '99d23d7c0d52da043c3d59ee9d527d02e68d3b2b', class: "login-content" }, this.subtitle && (index.h("p", { key: '7ce2131761e0127a190dbc1f7b4cd32884e4657b', class: "login-subtitle" }, this.subtitle)), index.h("div", { key: 'bc32c6e61856c963cf9c0d22bfe5935c6c55568d', class: "login-providers" }, this.providers.map(provider => (index.h("button", { key: provider.name, class: {
27175
27590
  'provider-button': true,
27176
27591
  'is-loading': this.loadingProvider === provider.name,
27177
27592
  'is-disabled': this.loadingProvider !== null && this.loadingProvider !== provider.name,
@@ -27277,15 +27692,15 @@ const KritzelMasterDetail = class {
27277
27692
  const selectedItem = this.items.find(item => item.id === this.selectedItemId);
27278
27693
  const panelId = 'master-detail-panel';
27279
27694
  const selectedTabId = selectedItem ? `tab-${selectedItem.id}` : undefined;
27280
- return (index.h(index.Host, { key: 'fbdbf1151992c991945de48b593a8c9d0b6ed788' }, index.h("div", { key: 'e46113c8f3f0aa7ca376a288f5bccc8d25789880', class: {
27695
+ return (index.h(index.Host, { key: '25913464ef131f1332146a5372c0caf9f7050f24' }, index.h("div", { key: '206346f9bee671f1b283ebfaca4c4401706a074f', class: {
27281
27696
  'master-detail-container': true,
27282
27697
  'is-mobile-detail-visible': this.showMobileDetail,
27283
- } }, index.h("nav", { key: '7c125b758e71eb9082d2c0583ba6aad95123776d', class: "master-menu", role: "tablist", "aria-orientation": "vertical", "aria-label": "Settings categories" }, this.items.map((item, index$1) => (index.h("button", { key: item.id, id: `tab-${item.id}`, ref: el => this.setTabRef(el, index$1), class: {
27698
+ } }, index.h("nav", { key: 'f4cf41f060d1d25887d2b11a264de040e878d25c', class: "master-menu", role: "tablist", "aria-orientation": "vertical", "aria-label": "Settings categories" }, this.items.map((item, index$1) => (index.h("button", { key: item.id, id: `tab-${item.id}`, ref: el => this.setTabRef(el, index$1), class: {
27284
27699
  'menu-item': true,
27285
27700
  'is-selected': item.id === this.selectedItemId,
27286
27701
  'is-disabled': !!item.disabled,
27287
27702
  'is-focused': index$1 === this.focusedIndex,
27288
- }, role: "tab", "aria-selected": item.id === this.selectedItemId ? 'true' : 'false', "aria-controls": panelId, "aria-disabled": item.disabled ? 'true' : undefined, tabIndex: this.getTabIndex(item, index$1), disabled: item.disabled, onClick: () => this.handleItemClick(item), onKeyDown: e => this.handleKeyDown(e, item, index$1), onFocus: () => this.handleFocus(index$1), onBlur: this.handleBlur }, item.icon && (index.h("kritzel-icon", { name: item.icon, size: 20, class: "menu-item-icon" })), index.h("span", { class: "menu-item-label" }, item.label), index.h("span", { class: "menu-item-chevron", "aria-hidden": "true" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("path", { d: "m9 18 6-6-6-6" }))))))), index.h("div", { key: '1ead5122c67ba7ea40065240d921deaf0c3ca76f', id: panelId, class: "detail-panel", role: "tabpanel", "aria-labelledby": selectedTabId }, index.h("button", { key: 'a31f779aa0a70ce76dadb7028d2cee46e0e8be05', class: "mobile-back-button", onClick: this.handleBackClick, "aria-label": "Back to menu" }, index.h("kritzel-icon", { key: '355c38dfe879ef89a431d0c674304b32cbb2022b', name: "chevron-left", size: 20, class: "mobile-back-icon" }), "Back"), index.h("slot", { key: 'f70d9250d8f7a0c1d31b3f706a1494778b60f5dc' })))));
27703
+ }, role: "tab", "aria-selected": item.id === this.selectedItemId ? 'true' : 'false', "aria-controls": panelId, "aria-disabled": item.disabled ? 'true' : undefined, tabIndex: this.getTabIndex(item, index$1), disabled: item.disabled, onClick: () => this.handleItemClick(item), onKeyDown: e => this.handleKeyDown(e, item, index$1), onFocus: () => this.handleFocus(index$1), onBlur: this.handleBlur }, item.icon && (index.h("kritzel-icon", { name: item.icon, size: 20, class: "menu-item-icon" })), index.h("span", { class: "menu-item-label" }, item.label), index.h("span", { class: "menu-item-chevron", "aria-hidden": "true" }, index.h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }, index.h("path", { d: "m9 18 6-6-6-6" }))))))), index.h("div", { key: 'efb20b2c1c6dfcf03bee2c1ef06c2c7a6de27e7a', id: panelId, class: "detail-panel", role: "tabpanel", "aria-labelledby": selectedTabId }, index.h("button", { key: '13b97670c0c0b7f8fe493438d26179cd5039cd9c', class: "mobile-back-button", onClick: this.handleBackClick, "aria-label": "Back to menu" }, index.h("kritzel-icon", { key: '6a52e00dd7fc61bcacce71127bf78637bbff7e30', name: "chevron-left", size: 20, class: "mobile-back-icon" }), "Back"), index.h("slot", { key: '99bd31682d0e8accc6c2eacfcbaf6778f3b92d05' })))));
27289
27704
  }
27290
27705
  static get watchers() { return {
27291
27706
  "selectedItemId": [{
@@ -27358,7 +27773,7 @@ const KritzelMenu = class {
27358
27773
  this.itemCloseChildMenu.emit(event.detail);
27359
27774
  };
27360
27775
  render() {
27361
- return (index.h(index.Host, { key: '9c5cf77bd92351cac399076343ca35d26c9d2230', tabIndex: 0, onClick: e => e.stopPropagation() }, this.openChildMenuItem && index.h("div", { key: 'b7df3b9b683c51093701abd789290ccfcfbdc3cf', class: "has-open-child-overlay", onClick: this.onOverlayClick }), this.items.map(item => (index.h("kritzel-menu-item", { key: item.id, item: item, parent: this.parent, style: { pointerEvents: this.editingMenuItem && !item.isEditing ? 'none' : 'auto' }, onItemSelect: this.handleItemSelect, onItemSave: this.handleSave, onItemCancel: this.handleCancel, onItemToggleChildMenu: this.handleToggleChildMenu, onItemCloseChildMenu: this.handleCloseChildMenu })))));
27776
+ return (index.h(index.Host, { key: '9a14983012bb0c286a17811d3e3c41743799c560', tabIndex: 0, onClick: e => e.stopPropagation() }, this.openChildMenuItem && index.h("div", { key: '0540a587c117a09857e9d87b65e019c8fa85c694', class: "has-open-child-overlay", onClick: this.onOverlayClick }), this.items.map(item => (index.h("kritzel-menu-item", { key: item.id, item: item, parent: this.parent, style: { pointerEvents: this.editingMenuItem && !item.isEditing ? 'none' : 'auto' }, onItemSelect: this.handleItemSelect, onItemSave: this.handleSave, onItemCancel: this.handleCancel, onItemToggleChildMenu: this.handleToggleChildMenu, onItemCloseChildMenu: this.handleCloseChildMenu })))));
27362
27777
  }
27363
27778
  };
27364
27779
  KritzelMenu.style = kritzelMenuCss();
@@ -27463,12 +27878,12 @@ const KritzelMenuItem = class {
27463
27878
  ];
27464
27879
  }
27465
27880
  render() {
27466
- return (index.h(index.Host, { key: 'f81a52f2536c759530b0b069ff02e0a71c363b0a', tabIndex: this.item.isDisabled ? -1 : 0, class: {
27881
+ return (index.h(index.Host, { key: '5a0d685c83cb3f82e64e74feef124ae0c9bca162', tabIndex: this.item.isDisabled ? -1 : 0, class: {
27467
27882
  'selected': this.item.isSelected,
27468
27883
  'editing': this.item.isEditing,
27469
27884
  'disabled': this.item.isDisabled,
27470
27885
  'child-open': this.item.isChildMenuOpen,
27471
- }, onClick: this.handleItemSelect }, index.h("div", { key: '212dbdbfc7a1cac000c8b25f9f51ad3de5420219', class: "menu-item-overlay" }), this.item.isEditing ? this.renderEditMode() : this.renderViewMode()));
27886
+ }, onClick: this.handleItemSelect }, index.h("div", { key: '02415ff1e0abf326ae284ed5b31b2a0241d4c7e2', class: "menu-item-overlay" }), this.item.isEditing ? this.renderEditMode() : this.renderViewMode()));
27472
27887
  }
27473
27888
  static get watchers() { return {
27474
27889
  "item": [{
@@ -27535,7 +27950,7 @@ const KritzelMoreMenu = class {
27535
27950
  this.closeMenu();
27536
27951
  };
27537
27952
  render() {
27538
- return (index.h(index.Host, { key: '02d18106b6a932cd4508bebc6ada3416f38eafc6', class: { mobile: this.isTouchDevice } }, index.h("button", { key: 'dff3f7bd69f8f1eafbff03b9ecc2f96c85784a85', class: "more-menu-button", onClick: this.toggleMenu }, index.h("kritzel-icon", { key: '28586ce5dd15be347cc59555608a2cbbf2978a2b', name: this.icon, size: this.iconSize })), index.h("kritzel-portal", { key: '3ca9260c3602a639687bc1341736c15a1b5c10a7', anchor: this.menuAnchor, offsetY: this.offsetY, onClose: this.closeMenu }, index.h("kritzel-menu", { key: 'e8e88cc9f827d93d9adcc86ad1041774cfc282af', items: this.visibleItems, onItemSelect: this.handleMenuItemSelect }))));
27953
+ return (index.h(index.Host, { key: '951577ab19a6bb10e7c578546a386e6159d16aff', class: { mobile: this.isTouchDevice } }, index.h("button", { key: '2b5112a4458544945c91536cb974e230401c6cd2', class: "more-menu-button", onClick: this.toggleMenu }, index.h("kritzel-icon", { key: 'e37122c20fa642ad4e53ae039ab43539f3b5b195', name: this.icon, size: this.iconSize })), index.h("kritzel-portal", { key: '5b4fd597eb4ec299ca672194f0dd69d3d50c270b', anchor: this.menuAnchor, offsetY: this.offsetY, onClose: this.closeMenu }, index.h("kritzel-menu", { key: 'a3a48b1ebb064293ecb6bb06f1783cba849d2ade', items: this.visibleItems, onItemSelect: this.handleMenuItemSelect }))));
27539
27954
  }
27540
27955
  };
27541
27956
  KritzelMoreMenu.style = kritzelMoreMenuCss();
@@ -27633,7 +28048,7 @@ const KritzelNumericInput = class {
27633
28048
  this.valueChange.emit(newValue);
27634
28049
  };
27635
28050
  render() {
27636
- return (index.h(index.Host, { key: '8f8984a3b29bad180be48c0d0cfdb269cbfb4c68' }, index.h("div", { key: '94e161c3dd1a8d05270f4611a5ba19323f8d9657', class: "input-container" }, this.label && index.h("label", { key: '53d8ba3d7fd0fcc888aaa167754854cc3ab9c98b', class: "input-label" }, this.label), index.h("div", { key: '4b0d15134fbdacf2a64213e73265adfb1e468525', class: "input-wrapper" }, index.h("input", { key: '59f62088b3a85aa8dfa3d9b1c04f50c289e4ab67', type: "number", class: "numeric-input", title: "", min: this.min === Number.MIN_SAFE_INTEGER ? undefined : this.min, max: this.max === Number.MAX_SAFE_INTEGER ? undefined : this.max, step: this.step, value: this.inputValue, placeholder: this.placeholder, onInput: this.handleInput, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown, onInvalid: this.handleInvalid }), index.h("div", { key: '2fe741f055b0a05e1605096287bda58a84760363', class: "spinner-buttons" }, index.h("button", { key: '0777d4483dc2ec8835a232d5da3f8fb8ce7acce2', type: "button", class: "spinner-button spinner-up", onClick: this.handleIncrement, tabIndex: -1, "aria-label": "Increase value" }, index.h("svg", { key: '37b962d0fcae37f6e8cf552e7a36d015b377f3ff', viewBox: "0 0 10 6", class: "spinner-icon" }, index.h("path", { key: 'd2916bc1cc40476418b9ee2e8c5c16172be90c2b', d: "M1 5L5 1L9 5", stroke: "currentColor", "stroke-width": "1.5", fill: "none", "stroke-linecap": "round", "stroke-linejoin": "round" }))), index.h("button", { key: 'c3f8c27fef2f95787d3a6e1a1d46c92f4bf20a8e', type: "button", class: "spinner-button spinner-down", onClick: this.handleDecrement, tabIndex: -1, "aria-label": "Decrease value" }, index.h("svg", { key: '35658bd8d44502509b3b6bda3490962e180fb692', viewBox: "0 0 10 6", class: "spinner-icon" }, index.h("path", { key: '67672b3a12f4973570698a8bd0a8cb856c0d0f82', d: "M1 1L5 5L9 1", stroke: "currentColor", "stroke-width": "1.5", fill: "none", "stroke-linecap": "round", "stroke-linejoin": "round" }))))))));
28051
+ return (index.h(index.Host, { key: '9f5b165e9c4db1cc6724020e12d18beb0a70ad11' }, index.h("div", { key: 'e5e3d790f9d9bd7bb2d25367bb7d7d86c731091f', class: "input-container" }, this.label && index.h("label", { key: '0ee62b45cbfe34405a888d0d61af472936b0a453', class: "input-label" }, this.label), index.h("div", { key: '49bbdca156346c1e0dd7b08d6790ae5829fa6d1f', class: "input-wrapper" }, index.h("input", { key: '986e05275c90bee119ff3c803d90ba16a0005f8d', type: "number", class: "numeric-input", title: "", min: this.min === Number.MIN_SAFE_INTEGER ? undefined : this.min, max: this.max === Number.MAX_SAFE_INTEGER ? undefined : this.max, step: this.step, value: this.inputValue, placeholder: this.placeholder, onInput: this.handleInput, onBlur: this.handleBlur, onKeyDown: this.handleKeyDown, onInvalid: this.handleInvalid }), index.h("div", { key: '4c468fdca0018390bdc57f1b2fd7fa2e68b09f98', class: "spinner-buttons" }, index.h("button", { key: '7b433424c6817049b6229e249d715ebd43dfe350', type: "button", class: "spinner-button spinner-up", onClick: this.handleIncrement, tabIndex: -1, "aria-label": "Increase value" }, index.h("svg", { key: '666b0c184d00d000a1fa97d4bdda3a9a6b0d299d', viewBox: "0 0 10 6", class: "spinner-icon" }, index.h("path", { key: '223f829a0b808e9fdde656294729121e2c8ae0d3', d: "M1 5L5 1L9 5", stroke: "currentColor", "stroke-width": "1.5", fill: "none", "stroke-linecap": "round", "stroke-linejoin": "round" }))), index.h("button", { key: '40f72b01e59b98c7bca4952c94eeb631fe3d17a1', type: "button", class: "spinner-button spinner-down", onClick: this.handleDecrement, tabIndex: -1, "aria-label": "Decrease value" }, index.h("svg", { key: '5f304877010fc9fa1c7559270b5105659b2f4863', viewBox: "0 0 10 6", class: "spinner-icon" }, index.h("path", { key: 'e31b6f1edb583eebbf608fc5360c53d49d4d2d5c', d: "M1 1L5 5L9 1", stroke: "currentColor", "stroke-width": "1.5", fill: "none", "stroke-linecap": "round", "stroke-linejoin": "round" }))))))));
27637
28052
  }
27638
28053
  static get watchers() { return {
27639
28054
  "value": [{
@@ -27672,7 +28087,7 @@ const KritzelOpacitySlider = class {
27672
28087
  }
27673
28088
  render() {
27674
28089
  const percentage = this.getPercentage();
27675
- return (index.h(index.Host, { key: '01ab026ae319eaf7bebdd74f5d06e79be4871630' }, index.h("div", { key: '67a5dec5570d7f16162fb5d43f18e020f642df68', class: "opacity-container" }, index.h("div", { key: '87362a95645b164331c58c46add78e1497e27ba1', class: "slider-wrapper" }, index.h("input", { key: '0ec74a8e864c7ac47a5ac4eea4d1c960d10214d4', type: "range", class: "opacity-slider", min: this.min, max: this.max, step: this.step, value: this.value, onInput: (e) => this.handleInput(e), style: {
28090
+ return (index.h(index.Host, { key: 'a0307b2fc97221881f0e2a21035427fcf0c5fdb3' }, index.h("div", { key: 'a77c6e973f92671daa3a8299b51d70b2d2fd98db', class: "opacity-container" }, index.h("div", { key: 'c5df7c6fe6db3aa9a8430ea7580d6cc7a23666b8', class: "slider-wrapper" }, index.h("input", { key: '6d3cae027e5a9b391f36ede911dedb201630d525', type: "range", class: "opacity-slider", min: this.min, max: this.max, step: this.step, value: this.value, onInput: (e) => this.handleInput(e), style: {
27676
28091
  '--slider-progress': `${percentage}%`,
27677
28092
  } })))));
27678
28093
  }
@@ -28008,7 +28423,7 @@ const KritzelPortal = class {
28008
28423
  this.portal.style.visibility = 'visible';
28009
28424
  }
28010
28425
  render() {
28011
- return (index.h(index.Host, { key: '14116403d3ecb83928b66999da1561f233fd23e9', style: { display: this.anchor ? 'block' : 'none' } }, index.h("slot", { key: '1f5d5789b30699de4d332b89e51356fa06b34bf3' })));
28426
+ return (index.h(index.Host, { key: '70a29385ee7f1bef37f903e1e6821b46fc9a147f', style: { display: this.anchor ? 'block' : 'none' } }, index.h("slot", { key: '8f50bce48c3aa82abbfc34331f8e80f205a45f03' })));
28012
28427
  }
28013
28428
  static get watchers() { return {
28014
28429
  "anchor": [{
@@ -28022,7 +28437,7 @@ const KritzelPortal = class {
28022
28437
  * This file is auto-generated by the version bump scripts.
28023
28438
  * Do not modify manually.
28024
28439
  */
28025
- const KRITZEL_VERSION = '0.1.73';
28440
+ const KRITZEL_VERSION = '0.1.75';
28026
28441
 
28027
28442
  const kritzelSettingsCss = () => `:host{display:contents}kritzel-dialog{--kritzel-dialog-body-padding:0;--kritzel-dialog-width-large:800px;--kritzel-dialog-height-large:500px}.footer-button{padding:8px 16px;border-radius:6px;cursor:pointer;font-size:14px}.cancel-button{border:1px solid #ebebeb;background:#fff;color:inherit}.cancel-button:hover{background:#f5f5f5}.settings-content{padding:0}.settings-content h3{margin:0 0 16px 0;font-size:18px;font-weight:600;color:var(--kritzel-settings-content-heading-color, #333333)}.settings-content p{margin:0;font-size:14px;color:var(--kritzel-settings-content-text-color, #666666);line-height:1.5}.settings-group{display:flex;flex-direction:column;gap:24px}.settings-item{display:flex;flex-direction:column;gap:8px}.settings-row{display:flex;align-items:center;justify-content:space-between;gap:16px}.settings-label{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.settings-description{font-size:12px;color:var(--kritzel-settings-description-color, #888888);margin:0;line-height:1.4}.shortcuts-list{display:flex;flex-direction:column;gap:24px}.shortcuts-category{display:flex;flex-direction:column;gap:8px}.shortcuts-category-title{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.shortcuts-group{display:flex;flex-direction:column;gap:4px}.shortcut-item{display:flex;justify-content:space-between;align-items:center;padding:6px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-item-bg, rgba(0, 0, 0, 0.02))}.shortcut-label{font-size:14px;color:var(--kritzel-settings-content-text-color, #666666)}.shortcut-key{font-family:monospace;font-size:12px;padding:2px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-key-bg, #f0f0f0);color:var(--kritzel-settings-shortcut-key-color, #333333);border:1px solid var(--kritzel-settings-shortcut-key-border, #ddd)}`;
28028
28443
 
@@ -28217,7 +28632,7 @@ const KritzelSettings = class {
28217
28632
  }
28218
28633
  }
28219
28634
  render() {
28220
- return (index.h(index.Host, { key: 'fc940d4154b06378cf13f7cde5238f4a1ac1d2ba' }, index.h("kritzel-dialog", { key: '0c50f5cb3570eba67ce4c04f9f453361e0c9626a', isOpen: this.isDialogOpen, dialogTitle: "Settings", size: "large", onDialogClose: this.closeDialog }, index.h("kritzel-master-detail", { key: '974adda5868d1261784366b59e285a0c45d265ca', items: SETTINGS_CATEGORIES, selectedItemId: this.selectedCategoryId, onItemSelect: this.handleCategorySelect }, this.renderCategoryContent()))));
28635
+ return (index.h(index.Host, { key: '0d4cd5ad565a1ff1140f308765ff887ba68ba6c8' }, index.h("kritzel-dialog", { key: '170a15a41d18f56981a99aae266026625d7fe644', isOpen: this.isDialogOpen, dialogTitle: "Settings", size: "large", onDialogClose: this.closeDialog }, index.h("kritzel-master-detail", { key: '6569f351fdef57dd189af63f7d6c4d98ce815c1b', items: SETTINGS_CATEGORIES, selectedItemId: this.selectedCategoryId, onItemSelect: this.handleCategorySelect }, this.renderCategoryContent()))));
28221
28636
  }
28222
28637
  };
28223
28638
  KritzelSettings.style = kritzelSettingsCss();
@@ -28343,9 +28758,9 @@ const KritzelShareDialog = class {
28343
28758
  this.dialogClosed.emit();
28344
28759
  };
28345
28760
  render() {
28346
- return (index.h(index.Host, { key: '0e498848cf6c680cd7ce0233b258317ae967262a' }, index.h("kritzel-dialog", { key: 'f5d1c6fbf454d5a46e60a915a405ca0926e1b5e3', dialogTitle: "Share Workspace", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small" }, index.h("div", { key: '1348d2969058a5d4d2e992c9c2df5c7ec57591ed', class: "share-content" }, index.h("div", { key: '7c7c349c0af3b125eff301d229731b64c4a51d0a', class: "share-section" }, index.h("div", { key: 'c1498b2b2ac6717925ce806bc3fea6af3e611709', class: "share-row" }, index.h("div", { key: '44c268fa61994cff17d6a0082347c18374d92243', class: "share-label-group" }, index.h("label", { key: '1a56501131c2dd0666d9755930d52cacc3b703c5', class: "share-label" }, "Link sharing"), index.h("p", { key: 'fd4fca65f5258e040eff95d1f27059240ed7aa6a', class: "share-description" }, this.internalIsPublic
28761
+ return (index.h(index.Host, { key: 'f1d9bac1ed73b3c028d77bec2eb0882b08ec12c8' }, index.h("kritzel-dialog", { key: '952e6c6c8ed5b2260c7b540524044a8704796cd9', dialogTitle: "Share Workspace", isOpen: this.isDialogOpen, onDialogClose: this.closeDialog, size: "small" }, index.h("div", { key: '1834e98b32be60eebf499ff7d021b16f2d9ad018', class: "share-content" }, index.h("div", { key: 'bc19e19e35de1513a1b337ad96a5e58b1c987751', class: "share-section" }, index.h("div", { key: '0310309b3389827d94be755d926eeda87d434148', class: "share-row" }, index.h("div", { key: 'ed3171611a010df28ead03955f413083e615f908', class: "share-label-group" }, index.h("label", { key: '90cc0d814c973bd02e4fb8b871b20561804e08dc', class: "share-label" }, "Link sharing"), index.h("p", { key: '02ae9eb9aeaf7e1153873da7f6501bdf0fc27096', class: "share-description" }, this.internalIsPublic
28347
28762
  ? 'Anyone with the link can access this workspace.'
28348
- : 'Link sharing is disabled. Only you can access this workspace.')), index.h("kritzel-slide-toggle", { key: '69214469344850ac81dad9462e6a3547ebf6f48f', checked: this.internalIsPublic, onCheckedChange: this.handleToggleChange, label: "Enable link sharing" }))), this.internalIsPublic && (index.h("div", { key: '9a26ca0ca3e447ae4cc0b4a20de666b97a90b20f', class: "share-section" }, index.h("div", { key: '753dbfd83f83ac81c6296cfc6bbf59ddf514897c', class: "share-url-container" }, index.h("input", { key: '2515209593b5c59792606f8915d62f83c7b35160', type: "text", class: "share-url-input", value: this.getShareUrl(), readOnly: true, onClick: (e) => e.target.select() }), index.h("button", { key: '1d62fb016514e8a16eb354aff139db43af7585f3', class: { 'copy-button': true, 'copy-success': this.copySuccess }, onClick: this.handleCopyUrl, title: this.copySuccess ? 'Copied!' : 'Copy link' }, index.h("kritzel-icon", { key: 'a9824261a6914aff927866409c02a42ca8faf44f', name: this.copySuccess ? 'check' : 'copy', size: 18 })))))))));
28763
+ : 'Link sharing is disabled. Only you can access this workspace.')), index.h("kritzel-slide-toggle", { key: '656ff6bff6dff3956b73072766b73642c13cf968', checked: this.internalIsPublic, onCheckedChange: this.handleToggleChange, label: "Enable link sharing" }))), this.internalIsPublic && (index.h("div", { key: 'dfc591b157b9ae5c268d77b7a7aab4168c80690d', class: "share-section" }, index.h("div", { key: '54f71d05705ddca92ccf1637e0ad7318127b6e72', class: "share-url-container" }, index.h("input", { key: 'fa1a9fad703a30e87daa8c3f83c782349103809a', type: "text", class: "share-url-input", value: this.getShareUrl(), readOnly: true, onClick: (e) => e.target.select() }), index.h("button", { key: 'd879775f7df013855e748c48dda63470ee0f66c4', class: { 'copy-button': true, 'copy-success': this.copySuccess }, onClick: this.handleCopyUrl, title: this.copySuccess ? 'Copied!' : 'Copy link' }, index.h("kritzel-icon", { key: '985ee904561df3bd182697f7b95bc0e64c7f70d5', name: this.copySuccess ? 'check' : 'copy', size: 18 })))))))));
28349
28764
  }
28350
28765
  static get watchers() { return {
28351
28766
  "isPublic": [{
@@ -28383,7 +28798,7 @@ const KritzelSlideToggle = class {
28383
28798
  }
28384
28799
  };
28385
28800
  render() {
28386
- return (index.h(index.Host, { key: 'd294d9becd13e8373bd029508ded3f47ee344184', class: { checked: this.checked, disabled: this.disabled }, tabIndex: this.disabled ? -1 : 0, role: "switch", "aria-checked": this.checked ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.label, onClick: this.handleToggle, onKeyDown: this.handleKeyDown }, index.h("div", { key: '3b5349c9b1f2126c9075f979c8b1a7b098060abb', class: "toggle-track" }, index.h("div", { key: '0bb5a44e73071d819611863e24af433ed56fb80d', class: "toggle-thumb" }))));
28801
+ return (index.h(index.Host, { key: 'fa4a31e3978f5a7187c8e011d58278829f60f912', class: { checked: this.checked, disabled: this.disabled }, tabIndex: this.disabled ? -1 : 0, role: "switch", "aria-checked": this.checked ? 'true' : 'false', "aria-disabled": this.disabled ? 'true' : 'false', "aria-label": this.label, onClick: this.handleToggle, onKeyDown: this.handleKeyDown }, index.h("div", { key: '1ae5a58c6cedf19626cba1e04abe566669582081', class: "toggle-track" }, index.h("div", { key: 'e11d42017fd624837f9e44f2671b74d38e4d015f', class: "toggle-thumb" }))));
28387
28802
  }
28388
28803
  };
28389
28804
  KritzelSlideToggle.style = kritzelSlideToggleCss();
@@ -28483,7 +28898,7 @@ const KritzelSplitButton = class {
28483
28898
  this.menuScrollTop = event.target.scrollTop;
28484
28899
  };
28485
28900
  render() {
28486
- return (index.h(index.Host, { key: '1e85f2b22cde41378d03710bd1332e2eb91ebe5a', class: { mobile: this.isTouchDevice } }, index.h("button", { key: '56573ad5655b2f61e1ebf3c4d85b489375beb62e', class: "split-main-button", tabIndex: 0, onClick: this.handleButtonClick, disabled: this.mainButtonDisabled }, this.buttonIcon && index.h("kritzel-icon", { key: '335c2f5418e305f3e48e3caabc4c48001ddd5ec3', name: this.buttonIcon })), index.h("div", { key: '733e48020f3b21b22de3774323e5a2d024686c2c', class: "split-divider" }), index.h("button", { key: '650cd557b856b5527910cbdeb6d14fe7a88bc0ab', ref: el => (this.splitMenuButtonRef = el), class: "split-menu-button", tabIndex: 0, onClick: this.toggleMenu, disabled: this.menuButtonDisabled }, index.h("kritzel-icon", { key: '3aaacbcdd66583e3884fe44777794209753fe594', name: this.dropdownIcon })), index.h("kritzel-portal", { key: 'd0ca70c21dfbf1684ab84650a31aae77df5cb52e', anchor: this.anchorElement, offsetY: 4, onClose: this.closeMenu }, index.h("kritzel-menu", { key: '9e38d4d6e1c82b6dca3c6d23fb5c0df12bfbefb4', ref: el => (this.menuRef = el), items: this.items, onItemSelect: this.handleItemSelect, onItemSave: this.handleItemSave, onItemCancel: this.handleItemCancel, onItemToggleChildMenu: this.handleItemToggleChildMenu, onItemCloseChildMenu: this.handleItemCloseChildMenu, onClose: this.closeMenu, onScroll: this.handleScroll }))));
28901
+ return (index.h(index.Host, { key: 'dbb1a5f61cae17d47d3d31959172189d842a1e0c', class: { mobile: this.isTouchDevice } }, index.h("button", { key: '081650f6ee9f8f4c6c7d8d0d72b72d116162034b', class: "split-main-button", tabIndex: 0, onClick: this.handleButtonClick, disabled: this.mainButtonDisabled }, this.buttonIcon && index.h("kritzel-icon", { key: '870353d092212556316e5881cd817208f138dfff', name: this.buttonIcon })), index.h("div", { key: '2254a771e8b73a5e0037e48f45cbf3819f6d83a6', class: "split-divider" }), index.h("button", { key: '0c42a970441e6f3530f686b8825f7de9b8395a07', ref: el => (this.splitMenuButtonRef = el), class: "split-menu-button", tabIndex: 0, onClick: this.toggleMenu, disabled: this.menuButtonDisabled }, index.h("kritzel-icon", { key: 'a958954d14c3f8f0e86d7d927f116c9a28a1fb43', name: this.dropdownIcon })), index.h("kritzel-portal", { key: '98b72e3da54dadb51a51f591dbae47dab439ac8e', anchor: this.anchorElement, offsetY: 4, onClose: this.closeMenu }, index.h("kritzel-menu", { key: '9f505415219b5741f483c659a00c1655c6652ca7', ref: el => (this.menuRef = el), items: this.items, onItemSelect: this.handleItemSelect, onItemSave: this.handleItemSave, onItemCancel: this.handleItemCancel, onItemToggleChildMenu: this.handleItemToggleChildMenu, onItemCloseChildMenu: this.handleItemCloseChildMenu, onClose: this.closeMenu, onScroll: this.handleScroll }))));
28487
28902
  }
28488
28903
  };
28489
28904
  KritzelSplitButton.style = kritzelSplitButtonCss();
@@ -28503,7 +28918,7 @@ const KritzelStrokeSize = class {
28503
28918
  this.sizeChange.emit(size);
28504
28919
  }
28505
28920
  render() {
28506
- return (index.h(index.Host, { key: '2095245a9a1ae7781cf1030baaa93e6462b3c09e' }, index.h("div", { key: 'b83777024ef29d90f072a0ab002cf17a766825be', class: "size-grid" }, this.sizes.map(size => (index.h("div", { tabIndex: 0, class: {
28921
+ return (index.h(index.Host, { key: 'd7b77c754e247879f94b05f9c7b282d2aec22035' }, index.h("div", { key: '1f1c95ccb4b8f5500d73e4eafa775aa1dd93d83c', class: "size-grid" }, this.sizes.map(size => (index.h("div", { tabIndex: 0, class: {
28507
28922
  'size-container': true,
28508
28923
  'selected': this.selectedSize === size,
28509
28924
  }, onClick: () => this.handleSizeClick(size) }, index.h("kritzel-color", { value: 'var(--kritzel-global-text-primary)', size: size })))))));
@@ -28847,14 +29262,14 @@ const KritzelTooltip = class {
28847
29262
  }
28848
29263
  }
28849
29264
  render() {
28850
- return (index.h(index.Host, { key: '788500c800ec72ed8fd1ac7972d503c0a60a35b1', style: {
29265
+ return (index.h(index.Host, { key: '0f67bc5736d65b2dcb277bfea3a052e165213c01', style: {
28851
29266
  position: 'fixed',
28852
29267
  zIndex: '9999',
28853
29268
  transition: 'opacity 0.3s ease-in-out, transform 0.3s ease-in-out',
28854
29269
  visibility: this.isVisible ? 'visible' : 'hidden',
28855
29270
  left: `${this.positionX}px`,
28856
29271
  bottom: `${this.positionY}px`,
28857
- } }, index.h("div", { key: '7d0633d32290a5c0dc2cef32fac5cf78bb36619b', class: "tooltip-content", onClick: event => event.stopPropagation(), onPointerDown: event => event.stopPropagation(), onMouseDown: event => event.stopPropagation() }, index.h("slot", { key: '512e9e39b890672e09542913783f7b16fae6f444' }))));
29272
+ } }, index.h("div", { key: 'd193b1b6dd28022da87bcce832ae72d630fcd7e0', class: "tooltip-content", onClick: event => event.stopPropagation(), onPointerDown: event => event.stopPropagation(), onMouseDown: event => event.stopPropagation() }, index.h("slot", { key: 'a06478f4a9c995bad3558287341003effa6fc834' }))));
28858
29273
  }
28859
29274
  static get watchers() { return {
28860
29275
  "triggerElement": [{
@@ -28893,7 +29308,7 @@ const KritzelUtilityPanel = class {
28893
29308
  this.redo.emit();
28894
29309
  }
28895
29310
  render() {
28896
- return (index.h(index.Host, { key: '02831a892fee80cc9300282eceb6ee8abcba017e' }, index.h("button", { key: 'a22749cc8772e23d1ed848be2f4173ca9e0023b6', class: "utility-button", "data-testid": "utility-undo", disabled: !this.undoState?.canUndo, onClick: event => this.handleUndo(event) }, index.h("kritzel-icon", { key: '52f08e9e950501151f2f9196d248caa493d46f14', name: "undo" })), index.h("button", { key: '5a858ce4415d29d50928b42c8b7e9bf5380ab08e', class: "utility-button", "data-testid": "utility-redo", disabled: !this.undoState?.canRedo, onClick: event => this.handleRedo(event) }, index.h("kritzel-icon", { key: '9d995aedbcedc5b55021c7bb9c10dddb90602ea0', name: "redo" })), index.h("div", { key: '248c5499808553b0ac587b0aedeaf9db82e21991', class: "utility-separator" }), index.h("button", { key: '901f30f009abc873d360118088886b6f20d672c5', class: "utility-button", "data-testid": "utility-delete", onClick: () => this.delete.emit() }, index.h("kritzel-icon", { key: '82d4e1531360cc57038f161f13212cccd9df1285', name: "delete" }))));
29311
+ return (index.h(index.Host, { key: '0277e775741c91fe53daea8b14f9fdec550bef45' }, index.h("button", { key: '7eff5491f732dc6871b382612cf64a4d661fa39b', class: "utility-button", "data-testid": "utility-undo", disabled: !this.undoState?.canUndo, onClick: event => this.handleUndo(event) }, index.h("kritzel-icon", { key: '4eff979e1b6158df5a1b17c702765432bd18f642', name: "undo" })), index.h("button", { key: '73d75b36bfbd9d4299aea16be54ed6e1687a47d1', class: "utility-button", "data-testid": "utility-redo", disabled: !this.undoState?.canRedo, onClick: event => this.handleRedo(event) }, index.h("kritzel-icon", { key: 'd1556e8b7aa32b102aad9ea572d040e8863197c3', name: "redo" })), index.h("div", { key: 'eefbdb33bb73d5a48db79fe9621a91da77ddd46d', class: "utility-separator" }), index.h("button", { key: '15630243403ff4475123e209d0c3e7919718bc00', class: "utility-button", "data-testid": "utility-delete", onClick: () => this.delete.emit() }, index.h("kritzel-icon", { key: '7a423865d246a4752fc8fa3411133d0bd4f1c75d', name: "delete" }))));
28897
29312
  }
28898
29313
  };
28899
29314
  KritzelUtilityPanel.style = kritzelUtilityPanelCss();
@@ -29043,6 +29458,7 @@ KritzelWorkspaceManager.style = kritzelWorkspaceManagerCss();
29043
29458
 
29044
29459
  exports.kritzel_active_users = KritzelActiveUsers;
29045
29460
  exports.kritzel_avatar = KritzelAvatar;
29461
+ exports.kritzel_awareness_cursors = KritzelAwarenessCursors;
29046
29462
  exports.kritzel_back_to_content = KritzelBackToContent;
29047
29463
  exports.kritzel_button = KritzelButton;
29048
29464
  exports.kritzel_color = KritzelColorComponent;