kritzel-stencil 0.1.72 → 0.1.74

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 (177) hide show
  1. package/dist/cjs/index-Dc7LOVhs.js +2 -2
  2. package/dist/cjs/index.cjs.js +58 -18
  3. package/dist/cjs/{kritzel-active-users_41.cjs.entry.js → kritzel-active-users_42.cjs.entry.js} +586 -172
  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-Dyt35LBC.js} +58 -5
  8. package/dist/collection/classes/core/core.class.js +9 -3
  9. package/dist/collection/classes/core/store.class.js +20 -6
  10. package/dist/collection/classes/handlers/selection.handler.js +15 -2
  11. package/dist/collection/classes/objects/base-object.class.js +2 -0
  12. package/dist/collection/classes/objects/custom-element.class.js +1 -0
  13. package/dist/collection/classes/objects/group.class.js +1 -0
  14. package/dist/collection/classes/objects/image.class.js +1 -0
  15. package/dist/collection/classes/objects/line.class.js +1 -0
  16. package/dist/collection/classes/objects/path.class.js +1 -0
  17. package/dist/collection/classes/objects/selection-box.class.js +1 -0
  18. package/dist/collection/classes/objects/selection-group.class.js +13 -1
  19. package/dist/collection/classes/objects/shape.class.js +1 -0
  20. package/dist/collection/classes/objects/text.class.js +1 -0
  21. package/dist/collection/classes/providers/hocuspocus-sync-provider.class.js +57 -17
  22. package/dist/collection/classes/structures/object-map.structure.js +102 -7
  23. package/dist/collection/classes/tools/brush-tool.class.js +4 -0
  24. package/dist/collection/classes/tools/line-tool.class.js +4 -0
  25. package/dist/collection/classes/tools/shape-tool.class.js +2 -0
  26. package/dist/collection/collection-manifest.json +3 -2
  27. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.css +110 -0
  28. package/dist/collection/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.js +347 -0
  29. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
  30. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +3 -3
  31. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +150 -109
  32. package/dist/collection/components/shared/kritzel-avatar/kritzel-avatar.js +3 -3
  33. package/dist/collection/components/shared/kritzel-brush-style/kritzel-brush-style.js +1 -1
  34. package/dist/collection/components/shared/kritzel-button/kritzel-button.js +2 -2
  35. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  36. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
  37. package/dist/collection/components/shared/kritzel-dropdown/kritzel-dropdown.js +1 -1
  38. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  39. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  40. package/dist/collection/components/shared/kritzel-input/kritzel-input.js +1 -1
  41. package/dist/collection/components/shared/kritzel-master-detail/kritzel-master-detail.js +3 -3
  42. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  43. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  44. package/dist/collection/components/shared/kritzel-numeric-input/kritzel-numeric-input.js +1 -1
  45. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +1 -1
  46. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  47. package/dist/collection/components/shared/kritzel-slide-toggle/kritzel-slide-toggle.js +1 -1
  48. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  49. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
  50. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +2 -2
  51. package/dist/collection/components/ui/kritzel-back-to-content/kritzel-back-to-content.js +1 -1
  52. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +1 -1
  53. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +5 -5
  54. package/dist/collection/components/ui/kritzel-current-user/kritzel-current-user.js +1 -1
  55. package/dist/collection/components/ui/kritzel-current-user-dialog/kritzel-current-user-dialog.js +1 -1
  56. package/dist/collection/components/ui/kritzel-export/kritzel-export.js +1 -1
  57. package/dist/collection/components/ui/kritzel-login-dialog/kritzel-login-dialog.js +1 -1
  58. package/dist/collection/components/ui/kritzel-more-menu/kritzel-more-menu.js +1 -1
  59. package/dist/collection/components/ui/kritzel-settings/kritzel-settings.js +1 -1
  60. package/dist/collection/components/ui/kritzel-share-dialog/kritzel-share-dialog.js +2 -2
  61. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  62. package/dist/collection/constants/schema.constants.js +1 -1
  63. package/dist/collection/constants/version.js +1 -1
  64. package/dist/collection/interfaces/remote-cursor.interface.js +1 -0
  65. package/dist/collection/migrations/workspace.migrations.js +10 -1
  66. package/dist/components/index.d.ts +2 -0
  67. package/dist/components/index.js +1 -1
  68. package/dist/components/kritzel-active-users.js +1 -1
  69. package/dist/components/kritzel-avatar.js +1 -1
  70. package/dist/components/kritzel-awareness-cursors.d.ts +11 -0
  71. package/dist/components/kritzel-awareness-cursors.js +1 -0
  72. package/dist/components/kritzel-back-to-content.js +1 -1
  73. package/dist/components/kritzel-brush-style.js +1 -1
  74. package/dist/components/kritzel-button.js +1 -1
  75. package/dist/components/kritzel-color-palette.js +1 -1
  76. package/dist/components/kritzel-color.js +1 -1
  77. package/dist/components/kritzel-context-menu.js +1 -1
  78. package/dist/components/kritzel-controls.js +1 -1
  79. package/dist/components/kritzel-current-user-dialog.js +1 -1
  80. package/dist/components/kritzel-current-user.js +1 -1
  81. package/dist/components/kritzel-cursor-trail.js +1 -1
  82. package/dist/components/kritzel-dropdown.js +1 -1
  83. package/dist/components/kritzel-editor.js +1 -1
  84. package/dist/components/kritzel-engine.js +1 -1
  85. package/dist/components/kritzel-export.js +1 -1
  86. package/dist/components/kritzel-font-family.js +1 -1
  87. package/dist/components/kritzel-font-size.js +1 -1
  88. package/dist/components/kritzel-font.js +1 -1
  89. package/dist/components/kritzel-input.js +1 -1
  90. package/dist/components/kritzel-login-dialog.js +1 -1
  91. package/dist/components/kritzel-master-detail.js +1 -1
  92. package/dist/components/kritzel-menu-item.js +1 -1
  93. package/dist/components/kritzel-menu.js +1 -1
  94. package/dist/components/kritzel-more-menu.js +1 -1
  95. package/dist/components/kritzel-numeric-input.js +1 -1
  96. package/dist/components/kritzel-opacity-slider.js +1 -1
  97. package/dist/components/kritzel-portal.js +1 -1
  98. package/dist/components/kritzel-settings.js +1 -1
  99. package/dist/components/kritzel-share-dialog.js +1 -1
  100. package/dist/components/kritzel-slide-toggle.js +1 -1
  101. package/dist/components/kritzel-split-button.js +1 -1
  102. package/dist/components/kritzel-stroke-size.js +1 -1
  103. package/dist/components/kritzel-tool-config.js +1 -1
  104. package/dist/components/kritzel-tooltip.js +1 -1
  105. package/dist/components/kritzel-utility-panel.js +1 -1
  106. package/dist/components/kritzel-workspace-manager.js +1 -1
  107. package/dist/components/{p-Dp8idtVD.js → p-0kShCfeb.js} +1 -1
  108. package/dist/components/{p-B47JuZiD.js → p-2OYw6GJ7.js} +1 -1
  109. package/dist/components/p-7o2FWtFx.js +1 -0
  110. package/dist/components/{p-dR_q96Q3.js → p-B4Oqnl55.js} +1 -1
  111. package/dist/components/{p-C5KuV1pK.js → p-BA0ayKqO.js} +1 -1
  112. package/dist/components/{p-NbNVTRk6.js → p-BEJQ2kP7.js} +1 -1
  113. package/dist/components/p-BSipRoFx.js +1 -0
  114. package/dist/components/{p-CDadAOMw.js → p-BeFUNGEI.js} +1 -1
  115. package/dist/components/{p-35nrk8s0.js → p-BiByyU2C.js} +1 -1
  116. package/dist/components/{p-CCAWSyDD.js → p-BiouZo1q.js} +1 -1
  117. package/dist/components/{p-CSExtYKI.js → p-ByR0VXeU.js} +1 -1
  118. package/dist/components/{p-1MGcXTLv.js → p-C1uR_ZNW.js} +1 -1
  119. package/dist/components/{p-x8PzaMuD.js → p-C69Stayh.js} +1 -1
  120. package/dist/components/{p-Ch0UlFwq.js → p-C7SBI_0T.js} +1 -1
  121. package/dist/components/{p-DEzfXrGX.js → p-CAIGuV2J.js} +1 -1
  122. package/dist/components/p-CJ2eHeoV.js +1 -0
  123. package/dist/components/p-CW-VyJgK.js +1 -0
  124. package/dist/components/{p-DW4ADV9w.js → p-CZhyKp-f.js} +1 -1
  125. package/dist/components/p-CsR4owzk.js +1 -0
  126. package/dist/components/{p-BG1IxseV.js → p-CsoDfhD5.js} +1 -1
  127. package/dist/components/{p-DpFu5yAt.js → p-D1O7DxL4.js} +1 -1
  128. package/dist/components/{p-B5ouV8EQ.js → p-DRbG92F9.js} +1 -1
  129. package/dist/components/{p-C3eaM9TB.js → p-DS0xx1eT.js} +1 -1
  130. package/dist/components/{p-jx8VOz7S.js → p-DSzQ6H2j.js} +1 -1
  131. package/dist/components/{p-DsIlDGDO.js → p-DXjuuVq9.js} +1 -1
  132. package/dist/components/p-DXpYcAnT.js +1 -0
  133. package/dist/components/{p-DiFVw6IQ.js → p-Da46jw3N.js} +1 -1
  134. package/dist/components/{p-C6kZf91d.js → p-Dj_Qjga5.js} +1 -1
  135. package/dist/components/{p-Do0Q5-iC.js → p-DvIEvoZu.js} +1 -1
  136. package/dist/components/{p-CnVzLD5e.js → p-GYI7sDxr.js} +1 -1
  137. package/dist/components/{p-CcBM_ClD.js → p-HLbqRJGs.js} +1 -1
  138. package/dist/components/{p-VHyNcODZ.js → p-KQzWumjB.js} +1 -1
  139. package/dist/components/p-RJWe82kG.js +9 -0
  140. package/dist/components/{p-VAkeZOZL.js → p-TyR-YTXm.js} +1 -1
  141. package/dist/components/{p-CHtn5xr6.js → p-b4gyXoju.js} +1 -1
  142. package/dist/components/p-iRL0wQHQ.js +1 -0
  143. package/dist/components/{p-CqLaHE27.js → p-kj9wbLY8.js} +1 -1
  144. package/dist/components/{p-DaHq4iG1.js → p-xM-_OeRO.js} +1 -1
  145. package/dist/esm/index-MV-81ybv.js +2 -2
  146. package/dist/esm/index.js +59 -19
  147. package/dist/esm/{kritzel-active-users_41.entry.js → kritzel-active-users_42.entry.js} +586 -173
  148. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  149. package/dist/esm/loader.js +1 -1
  150. package/dist/esm/stencil.js +1 -1
  151. package/dist/esm/{workspace.migrations-BGixvB76.js → workspace.migrations-B99F1MdT.js} +58 -5
  152. package/dist/stencil/index.esm.js +1 -1
  153. package/dist/stencil/p-2a60e1bc.entry.js +9 -0
  154. package/dist/stencil/p-B99F1MdT.js +1 -0
  155. package/dist/stencil/{p-016ad76a.entry.js → p-fc21e29c.entry.js} +1 -1
  156. package/dist/stencil/stencil.esm.js +1 -1
  157. package/dist/types/classes/core/store.class.d.ts +10 -2
  158. package/dist/types/classes/objects/base-object.class.d.ts +1 -0
  159. package/dist/types/classes/objects/selection-group.class.d.ts +5 -0
  160. package/dist/types/classes/providers/hocuspocus-sync-provider.class.d.ts +3 -0
  161. package/dist/types/classes/structures/object-map.structure.d.ts +41 -0
  162. package/dist/types/components/core/kritzel-awareness-cursors/kritzel-awareness-cursors.d.ts +26 -0
  163. package/dist/types/components.d.ts +39 -4
  164. package/dist/types/constants/schema.constants.d.ts +1 -1
  165. package/dist/types/constants/version.d.ts +1 -1
  166. package/dist/types/interfaces/object.interface.d.ts +1 -0
  167. package/dist/types/interfaces/remote-cursor.interface.d.ts +17 -0
  168. package/dist/types/interfaces/theme.interface.d.ts +7 -0
  169. package/package.json +1 -1
  170. package/dist/components/p-B2vjbWy-.js +0 -9
  171. package/dist/components/p-BvToKcu1.js +0 -1
  172. package/dist/components/p-CNro30tB.js +0 -1
  173. package/dist/components/p-Duv3EM3w.js +0 -1
  174. package/dist/components/p-KFsLHwYm.js +0 -1
  175. package/dist/components/p-hCORwbZh.js +0 -1
  176. package/dist/stencil/p-BGixvB76.js +0 -1
  177. package/dist/stencil/p-a0f5c4ad.entry.js +0 -9
@@ -0,0 +1,347 @@
1
+ import { Host, h } from "@stencil/core";
2
+ import { KritzelPath } from "../../../classes/objects/path.class";
3
+ import { KritzelLine } from "../../../classes/objects/line.class";
4
+ const STALE_THRESHOLD_MS = 10_000;
5
+ const REMOVE_THRESHOLD_MS = 30_000;
6
+ const CLEANUP_INTERVAL_MS = 3_000;
7
+ export class KritzelAwarenessCursors {
8
+ core;
9
+ showEdgeIndicators = true;
10
+ edgeIndicatorPadding = 8;
11
+ remoteCursors = new Map();
12
+ objectVersion = 0;
13
+ cleanupIntervalId;
14
+ objectChangeRafId = null;
15
+ componentDidLoad() {
16
+ this.core.store.state.objects?.onAwarenessChange(states => {
17
+ this.handleAwarenessChange(states);
18
+ });
19
+ this.core.store.state.objects?.onObjectsChange(() => {
20
+ this.handleRemoteObjectChange();
21
+ });
22
+ this.cleanupIntervalId = setInterval(() => {
23
+ this.cleanupStaleCursors();
24
+ }, CLEANUP_INTERVAL_MS);
25
+ }
26
+ disconnectedCallback() {
27
+ if (this.cleanupIntervalId) {
28
+ clearInterval(this.cleanupIntervalId);
29
+ }
30
+ if (this.objectChangeRafId !== null) {
31
+ cancelAnimationFrame(this.objectChangeRafId);
32
+ }
33
+ }
34
+ handleAwarenessChange(states) {
35
+ const localClientId = this.core.store.state.objects?.localClientId;
36
+ const now = Date.now();
37
+ const updated = new Map(this.remoteCursors);
38
+ // Track which clientIds are still present
39
+ const activeClientIds = new Set();
40
+ states.forEach((state, clientId) => {
41
+ if (clientId === localClientId)
42
+ return;
43
+ if (!state.user)
44
+ return;
45
+ activeClientIds.add(clientId);
46
+ const user = state.user;
47
+ const cursor = state.cursor;
48
+ const activeObjectId = state.activeObjectId || null;
49
+ const selectionBox = state.selectionBox || null;
50
+ updated.set(clientId, {
51
+ clientId,
52
+ user,
53
+ cursor,
54
+ activeObjectId,
55
+ selectionBox,
56
+ lastUpdated: now,
57
+ });
58
+ });
59
+ // Remove cursors for disconnected clients
60
+ for (const clientId of updated.keys()) {
61
+ if (!activeClientIds.has(clientId)) {
62
+ updated.delete(clientId);
63
+ }
64
+ }
65
+ this.remoteCursors = updated;
66
+ }
67
+ cleanupStaleCursors() {
68
+ const now = Date.now();
69
+ let changed = false;
70
+ const updated = new Map(this.remoteCursors);
71
+ for (const [clientId, cursor] of updated) {
72
+ if (now - cursor.lastUpdated > REMOVE_THRESHOLD_MS) {
73
+ updated.delete(clientId);
74
+ changed = true;
75
+ }
76
+ }
77
+ if (changed) {
78
+ this.remoteCursors = updated;
79
+ }
80
+ }
81
+ isStale(cursor) {
82
+ return Date.now() - cursor.lastUpdated > STALE_THRESHOLD_MS;
83
+ }
84
+ hasActiveDrawingCursors() {
85
+ for (const cursor of this.remoteCursors.values()) {
86
+ if (cursor.activeObjectId)
87
+ return true;
88
+ }
89
+ return false;
90
+ }
91
+ handleRemoteObjectChange() {
92
+ if (!this.hasActiveDrawingCursors())
93
+ return;
94
+ // Debounce via rAF to batch multiple rapid Yjs updates into a single re-render
95
+ if (this.objectChangeRafId !== null)
96
+ return;
97
+ this.objectChangeRafId = requestAnimationFrame(() => {
98
+ this.objectChangeRafId = null;
99
+ this.objectVersion++;
100
+ });
101
+ }
102
+ getActiveObjectTip(objectId) {
103
+ const obj = this.core.store.state.objects?.findById(objectId);
104
+ if (!obj)
105
+ return null;
106
+ if (obj instanceof KritzelPath && !obj.isCompleted) {
107
+ const lastPoint = obj.points[obj.points.length - 1];
108
+ if (!lastPoint)
109
+ return null;
110
+ return {
111
+ x: (lastPoint[0] - obj.x) / obj.scale + obj.translateX,
112
+ y: (lastPoint[1] - obj.y) / obj.scale + obj.translateY,
113
+ };
114
+ }
115
+ if (obj instanceof KritzelLine && !obj.isCompleted) {
116
+ return {
117
+ x: (obj.endX - obj.x) / obj.scale + obj.translateX,
118
+ y: (obj.endY - obj.y) / obj.scale + obj.translateY,
119
+ };
120
+ }
121
+ // Shapes normalize their bounding box (x/y always top-left, width/height
122
+ // always positive), so we can't determine which corner the user is actively
123
+ // dragging. Fall back to the throttled awareness cursor position instead.
124
+ return null;
125
+ }
126
+ worldToScreen(worldX, worldY) {
127
+ const { scale, translateX, translateY } = this.core.store.state;
128
+ return {
129
+ x: worldX * scale + translateX,
130
+ y: worldY * scale + translateY,
131
+ };
132
+ }
133
+ isInViewport(screenX, screenY) {
134
+ const { viewportWidth, viewportHeight } = this.core.store.state;
135
+ return screenX >= 0 && screenX <= viewportWidth && screenY >= 0 && screenY <= viewportHeight;
136
+ }
137
+ clampToEdge(screenX, screenY) {
138
+ const { viewportWidth, viewportHeight } = this.core.store.state;
139
+ const padding = this.edgeIndicatorPadding; // 8px
140
+ // Clamp symmetrically
141
+ const clampedX = Math.max(padding, Math.min(viewportWidth - padding, screenX));
142
+ const clampedY = Math.max(padding, Math.min(viewportHeight - padding, screenY));
143
+ // Determine nearest edge to position avatar inside
144
+ const distLeft = clampedX - padding;
145
+ const distRight = (viewportWidth - padding) - clampedX;
146
+ const distTop = clampedY - padding;
147
+ const distBottom = (viewportHeight - padding) - clampedY;
148
+ let edge = 'top';
149
+ const minDist = Math.min(distLeft, distRight, distTop, distBottom);
150
+ if (minDist === distLeft)
151
+ edge = 'left';
152
+ else if (minDist === distRight)
153
+ edge = 'right';
154
+ else if (minDist === distTop)
155
+ edge = 'top';
156
+ else
157
+ edge = 'bottom';
158
+ const angle = Math.atan2(screenY - clampedY, screenX - clampedX);
159
+ return { x: clampedX, y: clampedY, angle, edge };
160
+ }
161
+ getUserDisplayName(user) {
162
+ if (user.displayName)
163
+ return user.displayName;
164
+ if (user.firstName || user.lastName) {
165
+ return [user.firstName, user.lastName].filter(Boolean).join(' ');
166
+ }
167
+ return 'Unknown';
168
+ }
169
+ render() {
170
+ // Read reactive viewport state so Stencil re-renders on pan/zoom
171
+ const _scale = this.core.store.state.scale;
172
+ const _tx = this.core.store.state.translateX;
173
+ const _ty = this.core.store.state.translateY;
174
+ // Suppress unused variable warnings — these reads trigger Stencil reactivity
175
+ void _scale;
176
+ void _tx;
177
+ void _ty;
178
+ void this.objectVersion;
179
+ const cursors = Array.from(this.remoteCursors.values());
180
+ return (h(Host, { key: 'b8676d9a7a3d4a79ea8ad9763355ecf4c2f90e17' }, cursors.map(remoteCursor => {
181
+ if (!remoteCursor.cursor)
182
+ return null;
183
+ // When a remote user is actively drawing, derive cursor position from
184
+ // the object's latest coordinates (synced via Yjs) instead of the
185
+ // throttled awareness cursor position
186
+ let screen;
187
+ let trackingObject = false;
188
+ if (remoteCursor.activeObjectId) {
189
+ const tip = this.getActiveObjectTip(remoteCursor.activeObjectId);
190
+ if (tip) {
191
+ trackingObject = true;
192
+ screen = this.worldToScreen(tip.x, tip.y);
193
+ }
194
+ else {
195
+ screen = this.worldToScreen(remoteCursor.cursor.x, remoteCursor.cursor.y);
196
+ }
197
+ }
198
+ else {
199
+ screen = this.worldToScreen(remoteCursor.cursor.x, remoteCursor.cursor.y);
200
+ }
201
+ const inViewport = this.isInViewport(screen.x, screen.y);
202
+ const stale = this.isStale(remoteCursor);
203
+ const color = remoteCursor.user.color || '#6B7280';
204
+ if (inViewport) {
205
+ return this.renderCursor(remoteCursor, screen.x, screen.y, color, stale, trackingObject);
206
+ }
207
+ if (this.showEdgeIndicators) {
208
+ return this.renderEdgeIndicator(remoteCursor, screen.x, screen.y, color, stale, trackingObject);
209
+ }
210
+ return null;
211
+ }), cursors.map(remoteCursor => {
212
+ if (!remoteCursor.selectionBox)
213
+ return null;
214
+ const color = remoteCursor.user.color || '#6B7280';
215
+ const box = remoteCursor.selectionBox;
216
+ const topLeft = this.worldToScreen(box.x, box.y);
217
+ const { scale } = this.core.store.state;
218
+ const screenWidth = box.width * scale;
219
+ const screenHeight = box.height * scale;
220
+ return (h("div", { key: `selection-box-${remoteCursor.clientId}`, class: "remote-selection-box", style: {
221
+ transform: `translate(${topLeft.x}px, ${topLeft.y}px)`,
222
+ width: `${screenWidth}px`,
223
+ height: `${screenHeight}px`,
224
+ backgroundColor: `color-mix(in srgb, ${color} 20%, transparent)`,
225
+ borderColor: `color-mix(in srgb, ${color} 50%, transparent)`,
226
+ } }));
227
+ })));
228
+ }
229
+ renderCursor(cursor, screenX, screenY, color, stale, trackingObject) {
230
+ return (h("div", { key: `cursor-${cursor.clientId}`, class: { 'awareness-cursor': true, stale, 'tracking-object': trackingObject }, style: { transform: `translate(${screenX}px, ${screenY}px)` } }, h("svg", { class: "cursor-arrow", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, h("path", { d: "M5 3L19 12L12 13L9 20L5 3Z", fill: color, stroke: "#ffffff", "stroke-width": "1.5", "stroke-linejoin": "round" })), h("span", { class: "cursor-label", style: { backgroundColor: color } }, this.getUserDisplayName(cursor.user))));
231
+ }
232
+ renderEdgeIndicator(cursor, screenX, screenY, color, stale, trackingObject) {
233
+ const clamped = this.clampToEdge(screenX, screenY);
234
+ const arrowDeg = (clamped.angle * 180) / Math.PI + 90;
235
+ // Position the username label opposite to the edge
236
+ const labelOffset = 20;
237
+ let labelX = 0;
238
+ let labelY = 0;
239
+ // Default transform origin is somewhat arbitrary without explicit layout
240
+ // We'll calculate simple translations relative to center point
241
+ if (clamped.edge === 'left') {
242
+ labelX = labelOffset;
243
+ }
244
+ else if (clamped.edge === 'right') {
245
+ labelX = -labelOffset;
246
+ }
247
+ else if (clamped.edge === 'top') {
248
+ labelY = labelOffset;
249
+ }
250
+ else if (clamped.edge === 'bottom') {
251
+ labelY = -labelOffset;
252
+ }
253
+ const displayName = this.getUserDisplayName(cursor.user);
254
+ return (h("div", { key: `edge-${cursor.clientId}`, class: { 'edge-indicator': true, stale, 'tracking-object': trackingObject }, style: {
255
+ transform: `translate(${clamped.x}px, ${clamped.y}px)`,
256
+ } }, h("svg", { class: "edge-arrow", width: "16", height: "16", viewBox: "0 0 16 16", style: { transform: `rotate(${arrowDeg}deg)` } }, h("path", { d: "M8 1L14 13H2L8 1Z", fill: color, stroke: "#ffffff", "stroke-width": "1.5", "stroke-linejoin": "round" })), h("span", { class: "edge-label", style: {
257
+ backgroundColor: color,
258
+ transform: `translate(${labelX}px, ${labelY}px)`,
259
+ } }, displayName)));
260
+ }
261
+ static get is() { return "kritzel-awareness-cursors"; }
262
+ static get encapsulation() { return "shadow"; }
263
+ static get originalStyleUrls() {
264
+ return {
265
+ "$": ["kritzel-awareness-cursors.css"]
266
+ };
267
+ }
268
+ static get styleUrls() {
269
+ return {
270
+ "$": ["kritzel-awareness-cursors.css"]
271
+ };
272
+ }
273
+ static get properties() {
274
+ return {
275
+ "core": {
276
+ "type": "unknown",
277
+ "mutable": false,
278
+ "complexType": {
279
+ "original": "KritzelCore",
280
+ "resolved": "KritzelCore",
281
+ "references": {
282
+ "KritzelCore": {
283
+ "location": "import",
284
+ "path": "../../../classes/core/core.class",
285
+ "id": "src/classes/core/core.class.ts::KritzelCore",
286
+ "referenceLocation": "KritzelCore"
287
+ }
288
+ }
289
+ },
290
+ "required": false,
291
+ "optional": false,
292
+ "docs": {
293
+ "tags": [],
294
+ "text": ""
295
+ },
296
+ "getter": false,
297
+ "setter": false
298
+ },
299
+ "showEdgeIndicators": {
300
+ "type": "boolean",
301
+ "mutable": false,
302
+ "complexType": {
303
+ "original": "boolean",
304
+ "resolved": "boolean",
305
+ "references": {}
306
+ },
307
+ "required": false,
308
+ "optional": false,
309
+ "docs": {
310
+ "tags": [],
311
+ "text": ""
312
+ },
313
+ "getter": false,
314
+ "setter": false,
315
+ "reflect": false,
316
+ "attribute": "show-edge-indicators",
317
+ "defaultValue": "true"
318
+ },
319
+ "edgeIndicatorPadding": {
320
+ "type": "number",
321
+ "mutable": false,
322
+ "complexType": {
323
+ "original": "number",
324
+ "resolved": "number",
325
+ "references": {}
326
+ },
327
+ "required": false,
328
+ "optional": false,
329
+ "docs": {
330
+ "tags": [],
331
+ "text": ""
332
+ },
333
+ "getter": false,
334
+ "setter": false,
335
+ "reflect": false,
336
+ "attribute": "edge-indicator-padding",
337
+ "defaultValue": "8"
338
+ }
339
+ };
340
+ }
341
+ static get states() {
342
+ return {
343
+ "remoteCursors": {},
344
+ "objectVersion": {}
345
+ };
346
+ }
347
+ }
@@ -63,7 +63,7 @@ export class KritzelCursorTrail {
63
63
  }
64
64
  }
65
65
  render() {
66
- return (h(Host, { key: '603a385702512357af1fbcc778b29feecf8cf208' }, this.cursorTrailPoints.length > 1 && (h("svg", { key: '5f7e4478599d482e624b77a752f601088b343f4e', class: "cursor-trail-svg", xmlns: "http://www.w3.org/2000/svg", style: {
66
+ return (h(Host, { key: 'b6a9e84ce49dd72f353e0b87ae0bc02b0e5aeb1e' }, this.cursorTrailPoints.length > 1 && (h("svg", { key: '238ea314a937d556c75ae080495c2e6559340c6f', class: "cursor-trail-svg", xmlns: "http://www.w3.org/2000/svg", style: {
67
67
  position: 'absolute',
68
68
  left: '0',
69
69
  top: '0',
@@ -180,7 +180,7 @@ export class KritzelEditor {
180
180
  isControlsVisible = true;
181
181
  isUtilityPanelVisible = true;
182
182
  syncConfig = {
183
- providers: [IndexedDBSyncProvider],
183
+ providers: [IndexedDBSyncProvider]
184
184
  };
185
185
  /** Optional login configuration. When provided, a "Sign in" button is shown that opens a login dialog with the configured providers. */
186
186
  loginConfig;
@@ -697,7 +697,7 @@ export class KritzelEditor {
697
697
  const isLoggedIn = this.isLoggedIn;
698
698
  const shouldShowCurrentUser = isLoggedIn;
699
699
  const shouldShowLoginButton = !!this.loginConfig && !isLoggedIn;
700
- return (h(Host, { key: '84231308ca4b12863139aeeafa754d544345df3f' }, h("div", { key: 'b8cdd2862e9bd892dfeed356e85d608759307bf7', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: '2bce2b9564f5a083ba32667718685310d060f23a', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-back-to-content", { key: 'b013213fc04fe86eefcc324911693fcffb8eea51', visible: this.isBackToContentButtonVisible, onBackToContent: () => this.backToContent() })), 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) }), 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) }), h("div", { key: '0986b1e82b8fbf989e716fe07a401faca618f642', class: "top-right-buttons" }, h("kritzel-settings", { key: '6f4cfe92bb54f0de291b0a0482742b859e86ecf3', ref: el => (this.settingsRef = el), shortcuts: this.shortcuts, editorId: this.editorId, onSettingsChange: event => this.handleSettingsChange(event) }), 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) }), h("kritzel-active-users", { key: 'e44843dcdc9c0ff1e760aadac3f2ec79a7371aba', users: this.activeUsers }), shouldShowCurrentUser && (h("kritzel-current-user", { key: '123f7968427b4420c8e29749b6ec679e1df92e95', user: this.user })), shouldShowLoginButton && h("kritzel-button", { key: '35b611178e9dfcc3c78a7495965d2844aa33435d', onButtonClick: () => this.loginDialogRef?.open() }, "Sign in"), h("kritzel-more-menu", { key: 'bec3ebf5a7a6d40fc334ef34d9b2c030e0c1faf2', items: this.moreMenuItems }), h("kritzel-share-dialog", { key: 'ba72f6cc43372cc7fe93090fd6254391494443ff', ref: el => (this.shareDialogRef = el), isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (h("kritzel-login-dialog", { key: '319bf700bd96bbdec9d566a7e054cdce4de2a251', ref: el => (this.loginDialogRef = el), providers: this.loginConfig.providers, dialogTitle: this.loginConfig.title, subtitle: this.loginConfig.subtitle, onProviderLogin: this.handleProviderLogin })))));
700
+ return (h(Host, { key: 'd1944655652eb2939e3696ed70050baaca0871e2' }, h("div", { key: 'ce5dfd9b8f8d9927e9a6ace4056f9053163c7e0a', class: "top-left-buttons" }, h("kritzel-workspace-manager", { key: 'e127a667c6e4e853bb4f0c9ffd0edaa6d80d9dfa', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-back-to-content", { key: 'a9abb378f3c79f356d2164cbddf6badba10188a8', visible: this.isBackToContentButtonVisible, onBackToContent: () => this.backToContent() })), 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) }), 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) }), h("div", { key: 'ec6ac72ba154abe0738259464f20cae9e7dbdd9e', class: "top-right-buttons" }, h("kritzel-settings", { key: 'f68f791d673b30331e55f358b9c71b3e2a9a8388', ref: el => (this.settingsRef = el), shortcuts: this.shortcuts, editorId: this.editorId, onSettingsChange: event => this.handleSettingsChange(event) }), 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) }), h("kritzel-active-users", { key: '48983a14d3902cd238cf0d9ed302ed561a655a9a', users: this.activeUsers }), shouldShowCurrentUser && h("kritzel-current-user", { key: '589bdc895741a2d17f60c4b549d14fe8fff74c17', user: this.user }), shouldShowLoginButton && h("kritzel-button", { key: '50414a9d7ade623678c767ce63c49f5e99336bed', onButtonClick: () => this.loginDialogRef?.open() }, "Sign in"), h("kritzel-more-menu", { key: 'db3069ee0b093acd238750df81c6f426139c9b87', items: this.moreMenuItems }), h("kritzel-share-dialog", { key: 'e16057838f00b2c3f239e794b4310bd2690d16e2', ref: el => (this.shareDialogRef = el), isPublic: this.currentIsPublic, workspaceId: this.activeWorkspace?.id, onToggleIsPublic: this.handleToggleIsPublic }), this.loginConfig && (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 })))));
701
701
  }
702
702
  static get is() { return "kritzel-editor"; }
703
703
  static get originalStyleUrls() {
@@ -1106,7 +1106,7 @@ export class KritzelEditor {
1106
1106
  },
1107
1107
  "getter": false,
1108
1108
  "setter": false,
1109
- "defaultValue": "{\r\n providers: [IndexedDBSyncProvider],\r\n }"
1109
+ "defaultValue": "{\r\n providers: [IndexedDBSyncProvider]\r\n }"
1110
1110
  },
1111
1111
  "loginConfig": {
1112
1112
  "type": "unknown",