kritzel-stencil 0.3.30 → 0.3.32

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 (53) hide show
  1. package/dist/cjs/index.cjs.js +1 -1
  2. package/dist/cjs/kritzel-active-users_45.cjs.entry.js +256 -113
  3. package/dist/cjs/{schema.constants-TiseVL4a.js → schema.constants-DItwOm2z.js} +32 -2
  4. package/dist/collection/classes/handlers/line-handle.handler.js +4 -2
  5. package/dist/collection/classes/managers/browser-permission.manager.js +4 -4
  6. package/dist/collection/classes/managers/clipboard.manager.js +36 -104
  7. package/dist/collection/classes/managers/clipboard.types.js +1 -0
  8. package/dist/collection/classes/objects/base-object.class.js +7 -0
  9. package/dist/collection/classes/objects/line.class.js +21 -0
  10. package/dist/collection/classes/providers/paste-intent/event-paste-intent.provider.js +48 -0
  11. package/dist/collection/classes/providers/paste-intent/ios-paste-intent.provider.js +16 -0
  12. package/dist/collection/classes/providers/paste-intent/navigator-paste-intent.provider.js +86 -0
  13. package/dist/collection/classes/registries/paste-intent-provider.registry.js +26 -0
  14. package/dist/collection/classes/structures/object-map.structure.js +29 -5
  15. package/dist/collection/classes/structures/quadtree.structure.js +14 -0
  16. package/dist/collection/constants/version.js +1 -1
  17. package/dist/collection/interfaces/paste-intent-provider.interface.js +1 -0
  18. package/dist/components/index.js +1 -1
  19. package/dist/components/kritzel-awareness-cursors.js +1 -1
  20. package/dist/components/kritzel-controls.js +1 -1
  21. package/dist/components/kritzel-editor.js +1 -1
  22. package/dist/components/kritzel-engine.js +1 -1
  23. package/dist/components/kritzel-settings.js +1 -1
  24. package/dist/components/kritzel-tool-config.js +1 -1
  25. package/dist/components/{p-BC7jWR_G.js → p-14NPx-_P.js} +1 -1
  26. package/dist/components/{p-B4WfPDj0.js → p-BmX2Vb4Z.js} +1 -1
  27. package/dist/components/{p-pCB0JKlD.js → p-COhjyuRa.js} +1 -1
  28. package/dist/components/{p-E4tWs1Ga.js → p-DK2ZByiU.js} +2 -2
  29. package/dist/components/p-DfIknZRl.js +1 -0
  30. package/dist/components/{p-CPnjRgkl.js → p-Dl3MwBL1.js} +1 -1
  31. package/dist/components/{p-CRQEFKjb.js → p-FjazwgrY.js} +1 -1
  32. package/dist/esm/index.js +2 -2
  33. package/dist/esm/kritzel-active-users_45.entry.js +256 -113
  34. package/dist/esm/{schema.constants-7ayQfjzt.js → schema.constants-CH2FWTVr.js} +32 -2
  35. package/dist/stencil/index.esm.js +1 -1
  36. package/dist/stencil/p-CH2FWTVr.js +1 -0
  37. package/dist/stencil/p-be38d770.entry.js +9 -0
  38. package/dist/stencil/stencil.esm.js +1 -1
  39. package/dist/types/classes/managers/clipboard.manager.d.ts +21 -37
  40. package/dist/types/classes/managers/clipboard.types.d.ts +23 -0
  41. package/dist/types/classes/objects/line.class.d.ts +11 -0
  42. package/dist/types/classes/providers/paste-intent/event-paste-intent.provider.d.ts +8 -0
  43. package/dist/types/classes/providers/paste-intent/ios-paste-intent.provider.d.ts +6 -0
  44. package/dist/types/classes/providers/paste-intent/navigator-paste-intent.provider.d.ts +9 -0
  45. package/dist/types/classes/registries/paste-intent-provider.registry.d.ts +10 -0
  46. package/dist/types/classes/structures/object-map.structure.d.ts +1 -0
  47. package/dist/types/classes/structures/quadtree.structure.d.ts +8 -0
  48. package/dist/types/constants/version.d.ts +1 -1
  49. package/dist/types/interfaces/paste-intent-provider.interface.d.ts +10 -0
  50. package/package.json +1 -1
  51. package/dist/components/p-BNu3Akp5.js +0 -1
  52. package/dist/stencil/p-0adfecf4.entry.js +0 -9
  53. package/dist/stencil/p-7ayQfjzt.js +0 -1
@@ -3,17 +3,32 @@ import { KritzelText } from "../objects/text.class";
3
3
  import { KritzelSelectionGroup } from "../objects/selection-group.class";
4
4
  import { KritzelClassHelper } from "../../helpers/class.helper";
5
5
  import { KritzelReviver } from "../core/reviver.class";
6
+ import { KritzelPasteIntentProviderRegistry } from "../registries/paste-intent-provider.registry";
7
+ import { KritzelEventPasteIntentProvider } from "../providers/paste-intent/event-paste-intent.provider";
8
+ import { KritzelNavigatorPasteIntentProvider } from "../providers/paste-intent/navigator-paste-intent.provider";
9
+ import { KritzelIosPasteIntentProvider } from "../providers/paste-intent/ios-paste-intent.provider";
10
+ import { CLIPBOARD_PAYLOAD_PREFIX } from "./clipboard.types";
6
11
  import imageCompression from "browser-image-compression";
7
- const CLIPBOARD_PAYLOAD_PREFIX = 'kritzel-clipboard/v1:';
8
12
  /**
9
13
  * Manages clipboard operations (copy/cut/paste) for a core instance.
10
14
  *
11
- * The system clipboard is the single source of truth: copy serializes the current
12
- * selection into a Kritzel payload, paste reads + revives it. No copied objects are
13
- * cached on the engine state.
15
+ * The system clipboard is the preferred source of truth: copy serializes the current
16
+ * selection into a Kritzel payload, paste reads + revives it.
17
+ *
18
+ * A copy also mirrors the payload into an in-memory session buffer. On platforms where
19
+ * eventless programmatic clipboard access is unavailable (notably iOS Safari, where the
20
+ * context menu triggers copy/paste without a native ClipboardEvent), the session buffer
21
+ * is used as the fallback source so internal object copy/paste keeps working.
14
22
  */
15
23
  export class KritzelClipboardManager {
16
24
  _core;
25
+ _pasteIntentRegistry;
26
+ /**
27
+ * In-memory mirror of the most recent copy payload. Acts as the fallback clipboard
28
+ * source when the system clipboard cannot be read programmatically (e.g. iOS Safari
29
+ * context-menu paste). Reset is not required; a new copy overwrites it.
30
+ */
31
+ _sessionClipboardPayload = null;
17
32
  /**
18
33
  * In-memory cascade offset (world units) applied to successive pastes of the same
19
34
  * clipboard content so they don't stack exactly on top of each other. Reset on copy/cut.
@@ -21,6 +36,19 @@ export class KritzelClipboardManager {
21
36
  _pasteOffset = 0;
22
37
  constructor(core) {
23
38
  this._core = core;
39
+ this._pasteIntentRegistry = new KritzelPasteIntentProviderRegistry(this._core);
40
+ // Provider order is intentional: iOS eventless path should short-circuit,
41
+ // native paste events remain preferred, and async navigator APIs are fallback.
42
+ this._pasteIntentRegistry.register(new KritzelIosPasteIntentProvider());
43
+ this._pasteIntentRegistry.register(new KritzelEventPasteIntentProvider());
44
+ this._pasteIntentRegistry.register(new KritzelNavigatorPasteIntentProvider());
45
+ }
46
+ /**
47
+ * The most recent copy payload mirrored in memory, used as a fallback clipboard
48
+ * source when the system clipboard cannot be read programmatically.
49
+ */
50
+ get sessionClipboardPayload() {
51
+ return this._sessionClipboardPayload;
24
52
  }
25
53
  /**
26
54
  * Serializes an object for the clipboard payload. Groups additionally embed their
@@ -36,31 +64,6 @@ export class KritzelClipboardManager {
36
64
  serializeClipboardPayload(payload) {
37
65
  return `${CLIPBOARD_PAYLOAD_PREFIX}${JSON.stringify(payload)}`;
38
66
  }
39
- parseClipboardPayload(text) {
40
- if (!text || !text.startsWith(CLIPBOARD_PAYLOAD_PREFIX)) {
41
- return null;
42
- }
43
- try {
44
- const parsed = JSON.parse(text.slice(CLIPBOARD_PAYLOAD_PREFIX.length));
45
- if (parsed?.version !== 1 || !Array.isArray(parsed.objects)) {
46
- return null;
47
- }
48
- return parsed;
49
- }
50
- catch {
51
- return null;
52
- }
53
- }
54
- determineTextPasteIntent(text) {
55
- const payload = this.parseClipboardPayload(text);
56
- if (payload) {
57
- return { type: 'kritzel_objects', payload };
58
- }
59
- if (text && text.trim().length > 0) {
60
- return { type: 'plain_text', text };
61
- }
62
- return null;
63
- }
64
67
  async writeBrowserClipboardPayload(payload, event) {
65
68
  const serialized = this.serializeClipboardPayload(payload);
66
69
  if (event?.clipboardData) {
@@ -140,80 +143,6 @@ export class KritzelClipboardManager {
140
143
  }
141
144
  }
142
145
  }
143
- /**
144
- * Evaluates the clipboard event to determine exactly what the user is trying to paste.
145
- * Returns a PasteIntent covering the 3 main cases: Kritzel objects, Images, or Plain text.
146
- */
147
- async determinePasteIntent(event) {
148
- if (event?.clipboardData) {
149
- const data = event.clipboardData;
150
- const imageFiles = Array.from(data.files ?? []).filter(file => file.type.startsWith('image/'));
151
- if (imageFiles.length > 0) {
152
- return { type: 'image_files', files: imageFiles };
153
- }
154
- if (data.types.includes('text/plain')) {
155
- const textIntent = this.determineTextPasteIntent(data.getData('text/plain'));
156
- if (textIntent)
157
- return textIntent;
158
- }
159
- return { type: 'unknown' };
160
- }
161
- if (typeof navigator === 'undefined' || !navigator.clipboard) {
162
- return { type: 'unknown' };
163
- }
164
- const { clipboard } = navigator;
165
- const permissionState = await this._core.permissionManager.checkPermission('clipboard-read');
166
- if (permissionState !== 'granted') {
167
- const requestedState = await this._core.permissionManager.requestPermission('clipboard-read');
168
- if (requestedState !== 'granted') {
169
- return { type: 'unknown' };
170
- }
171
- }
172
- if (clipboard.read) {
173
- try {
174
- const items = await clipboard.read();
175
- const { imageFiles, text } = await this.processClipboardItems(items);
176
- if (imageFiles.length > 0) {
177
- return { type: 'image_files', files: imageFiles };
178
- }
179
- if (text !== null) {
180
- const textIntent = this.determineTextPasteIntent(text);
181
- if (textIntent)
182
- return textIntent;
183
- }
184
- }
185
- catch {
186
- // Fall back to readText below when read() is denied or unavailable at runtime.
187
- }
188
- }
189
- if (clipboard.readText) {
190
- const textIntent = this.determineTextPasteIntent(await clipboard.readText());
191
- if (textIntent)
192
- return textIntent;
193
- }
194
- return { type: 'unknown' };
195
- }
196
- /**
197
- * Helper to parse clipboard items in parallel
198
- */
199
- async processClipboardItems(items) {
200
- const imageFiles = [];
201
- let text = null;
202
- // Process items concurrently
203
- await Promise.all(items.map(async (item) => {
204
- const imageType = item.types.find(type => type.startsWith('image/'));
205
- if (imageType) {
206
- const blob = await item.getType(imageType);
207
- imageFiles.push(new File([blob], 'pasted-image', { type: imageType }));
208
- return;
209
- }
210
- if (text === null && item.types.includes('text/plain')) {
211
- const blob = await item.getType('text/plain');
212
- text = await blob.text();
213
- }
214
- }));
215
- return { imageFiles, text };
216
- }
217
146
  /**
218
147
  * Copies currently selected objects into internal clipboard state.
219
148
  */
@@ -229,6 +158,9 @@ export class KritzelClipboardManager {
229
158
  };
230
159
  // A fresh copy resets the cascade so the first paste lands at the default offset.
231
160
  this._pasteOffset = 0;
161
+ // Mirror the payload in memory so eventless paste still works where the system
162
+ // clipboard is not programmatically accessible (e.g. iOS Safari context menu).
163
+ this._sessionClipboardPayload = payload;
232
164
  void this.writeBrowserClipboardPayload(payload, event);
233
165
  }
234
166
  /**
@@ -287,7 +219,7 @@ export class KritzelClipboardManager {
287
219
  * Pastes objects from clipboard with optional explicit world coordinates.
288
220
  */
289
221
  async paste(x, y, event) {
290
- const pasteIntent = await this.determinePasteIntent(event);
222
+ const pasteIntent = await this._pasteIntentRegistry.resolve(event);
291
223
  let objects = null;
292
224
  if (pasteIntent.type === 'kritzel_objects') {
293
225
  objects = this.materializeKritzelObjects(pasteIntent);
@@ -0,0 +1 @@
1
+ export const CLIPBOARD_PAYLOAD_PREFIX = 'kritzel-clipboard/v1:';
@@ -381,6 +381,13 @@ export class KritzelBaseObject {
381
381
  this.resizing = false;
382
382
  this.markedForRemoval = false;
383
383
  this.isVisible = true;
384
+ // Reset known transient geometry caches if present on subclasses.
385
+ if ('_adjustedPoints' in this) {
386
+ this._adjustedPoints = null;
387
+ }
388
+ if ('_clipInfo' in this) {
389
+ this._clipInfo = null;
390
+ }
384
391
  return this;
385
392
  }
386
393
  /**
@@ -680,6 +680,27 @@ export class KritzelLine extends KritzelBaseObject {
680
680
  calculateBoundingBox() {
681
681
  return KritzelLine.calculateBoundingBox(this.startX, this.startY, this.endX, this.endY, this.controlX, this.controlY, this.strokeWidth);
682
682
  }
683
+ /**
684
+ * Serializes the line while excluding transient cache fields.
685
+ * @returns A serializable line object without runtime caches
686
+ */
687
+ serialize() {
688
+ const serialized = super.serialize();
689
+ delete serialized._adjustedPoints;
690
+ delete serialized._clipInfo;
691
+ return serialized;
692
+ }
693
+ /**
694
+ * Deserializes and resets transient caches so geometry is recomputed lazily.
695
+ * @param object - The serialized line object
696
+ * @returns This instance cast to type T
697
+ */
698
+ deserialize(object) {
699
+ const result = super.deserialize(object);
700
+ this._adjustedPoints = null;
701
+ this._clipInfo = null;
702
+ return result;
703
+ }
683
704
  /**
684
705
  * Recalculates and updates the width, height, x, y, and translation properties
685
706
  * based on the current endpoint positions and stroke width.
@@ -0,0 +1,48 @@
1
+ import { CLIPBOARD_PAYLOAD_PREFIX } from "../../managers/clipboard.types";
2
+ export class KritzelEventPasteIntentProvider {
3
+ canHandle(context) {
4
+ return !!context.event?.clipboardData;
5
+ }
6
+ async getPasteIntent(context) {
7
+ const data = context.event?.clipboardData;
8
+ if (!data) {
9
+ return { type: 'unknown' };
10
+ }
11
+ const imageFiles = Array.from(data.files ?? []).filter(file => file.type.startsWith('image/'));
12
+ if (imageFiles.length > 0) {
13
+ return { type: 'image_files', files: imageFiles };
14
+ }
15
+ if (data.types.includes('text/plain')) {
16
+ const textIntent = this.determineTextPasteIntent(data.getData('text/plain'));
17
+ if (textIntent) {
18
+ return textIntent;
19
+ }
20
+ }
21
+ return { type: 'unknown' };
22
+ }
23
+ parseClipboardPayload(text) {
24
+ if (!text || !text.startsWith(CLIPBOARD_PAYLOAD_PREFIX)) {
25
+ return null;
26
+ }
27
+ try {
28
+ const parsed = JSON.parse(text.slice(CLIPBOARD_PAYLOAD_PREFIX.length));
29
+ if (parsed?.version !== 1 || !Array.isArray(parsed.objects)) {
30
+ return null;
31
+ }
32
+ return parsed;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ determineTextPasteIntent(text) {
39
+ const payload = this.parseClipboardPayload(text);
40
+ if (payload) {
41
+ return { type: 'kritzel_objects', payload };
42
+ }
43
+ if (text && text.trim().length > 0) {
44
+ return { type: 'plain_text', text };
45
+ }
46
+ return null;
47
+ }
48
+ }
@@ -0,0 +1,16 @@
1
+ import { KritzelDevicesHelper } from "../../../helpers/devices.helper";
2
+ export class KritzelIosPasteIntentProvider {
3
+ canHandle(context) {
4
+ return !context.event?.clipboardData && KritzelDevicesHelper.isIOS();
5
+ }
6
+ async getPasteIntent(context) {
7
+ // On iOS Safari, eventless programmatic clipboard reads are commonly unavailable
8
+ // (e.g. the context-menu paste calls engine.paste() without a native ClipboardEvent).
9
+ // Fall back to the in-memory session buffer so internal object copy/paste still works.
10
+ const payload = context.core.clipboardManager.sessionClipboardPayload;
11
+ if (payload) {
12
+ return { type: 'kritzel_objects', payload };
13
+ }
14
+ return { type: 'unknown' };
15
+ }
16
+ }
@@ -0,0 +1,86 @@
1
+ import { CLIPBOARD_PAYLOAD_PREFIX } from "../../managers/clipboard.types";
2
+ export class KritzelNavigatorPasteIntentProvider {
3
+ canHandle(context) {
4
+ return !context.event?.clipboardData && typeof navigator !== 'undefined' && !!navigator.clipboard;
5
+ }
6
+ async getPasteIntent(context) {
7
+ if (typeof navigator === 'undefined' || !navigator.clipboard) {
8
+ return { type: 'unknown' };
9
+ }
10
+ const { clipboard } = navigator;
11
+ const permissionState = await context.core.permissionManager.checkPermission('clipboard-read');
12
+ if (permissionState !== 'granted') {
13
+ const requestedState = await context.core.permissionManager.requestPermission('clipboard-read');
14
+ if (requestedState !== 'granted') {
15
+ return { type: 'unknown' };
16
+ }
17
+ }
18
+ if (clipboard.read) {
19
+ try {
20
+ const items = await clipboard.read();
21
+ const { imageFiles, text } = await this.processClipboardItems(items);
22
+ if (imageFiles.length > 0) {
23
+ return { type: 'image_files', files: imageFiles };
24
+ }
25
+ if (text !== null) {
26
+ const textIntent = this.determineTextPasteIntent(text);
27
+ if (textIntent) {
28
+ return textIntent;
29
+ }
30
+ }
31
+ }
32
+ catch {
33
+ // Fall back to readText below when read() is denied or unavailable at runtime.
34
+ }
35
+ }
36
+ if (clipboard.readText) {
37
+ const textIntent = this.determineTextPasteIntent(await clipboard.readText());
38
+ if (textIntent) {
39
+ return textIntent;
40
+ }
41
+ }
42
+ return { type: 'unknown' };
43
+ }
44
+ async processClipboardItems(items) {
45
+ const imageFiles = [];
46
+ let text = null;
47
+ await Promise.all(items.map(async (item) => {
48
+ const imageType = item.types.find(type => type.startsWith('image/'));
49
+ if (imageType) {
50
+ const blob = await item.getType(imageType);
51
+ imageFiles.push(new File([blob], 'pasted-image', { type: imageType }));
52
+ return;
53
+ }
54
+ if (text === null && item.types.includes('text/plain')) {
55
+ const blob = await item.getType('text/plain');
56
+ text = await blob.text();
57
+ }
58
+ }));
59
+ return { imageFiles, text };
60
+ }
61
+ parseClipboardPayload(text) {
62
+ if (!text || !text.startsWith(CLIPBOARD_PAYLOAD_PREFIX)) {
63
+ return null;
64
+ }
65
+ try {
66
+ const parsed = JSON.parse(text.slice(CLIPBOARD_PAYLOAD_PREFIX.length));
67
+ if (parsed?.version !== 1 || !Array.isArray(parsed.objects)) {
68
+ return null;
69
+ }
70
+ return parsed;
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }
76
+ determineTextPasteIntent(text) {
77
+ const payload = this.parseClipboardPayload(text);
78
+ if (payload) {
79
+ return { type: 'kritzel_objects', payload };
80
+ }
81
+ if (text && text.trim().length > 0) {
82
+ return { type: 'plain_text', text };
83
+ }
84
+ return null;
85
+ }
86
+ }
@@ -0,0 +1,26 @@
1
+ export class KritzelPasteIntentProviderRegistry {
2
+ _core;
3
+ _providers = [];
4
+ constructor(_core) {
5
+ this._core = _core;
6
+ }
7
+ register(provider) {
8
+ this._providers.push(provider);
9
+ }
10
+ async resolve(event) {
11
+ const context = {
12
+ core: this._core,
13
+ event,
14
+ };
15
+ for (const provider of this._providers) {
16
+ if (!provider.canHandle(context)) {
17
+ continue;
18
+ }
19
+ const intent = await provider.getPasteIntent(context);
20
+ if (intent.type !== 'unknown') {
21
+ return intent;
22
+ }
23
+ }
24
+ return { type: 'unknown' };
25
+ }
26
+ }
@@ -1,5 +1,6 @@
1
1
  import * as Y from "yjs";
2
2
  import { KritzelReviver } from "../core/reviver.class";
3
+ import { KritzelLine } from "../objects/line.class";
3
4
  import { KritzelSelectionBox } from "../objects/selection-box.class";
4
5
  import { KritzelSelectionGroup } from "../objects/selection-group.class";
5
6
  import { DEFAULT_SYNC_CONFIG } from "../../configs/default-sync.config";
@@ -430,10 +431,19 @@ export class KritzelObjectMap {
430
431
  const objectsToUpdate = [];
431
432
  const selectionGroupsToUpdate = [];
432
433
  const objectsToDelete = [];
434
+ // The reverse anchor index (targetObjectId -> anchored lines) is derived runtime
435
+ // state that is NOT synced via Yjs. When line anchor connections arrive through a
436
+ // remote change it must be rebuilt, otherwise getLinesAnchoredTo/updateAnchorsForObject
437
+ // return stale/empty results on this client (e.g. anchored lines fail to follow a
438
+ // locally-moved target, leaving selection UI and anchors visually offset).
439
+ let anchorIndexNeedsRebuild = false;
433
440
  changedKeys.forEach(key => {
434
441
  const change = event.changes.keys.get(key);
435
442
  if (change && change.action === 'delete') {
436
443
  objectsToDelete.push(key);
444
+ if (this._idMap.get(key) instanceof KritzelLine) {
445
+ anchorIndexNeedsRebuild = true;
446
+ }
437
447
  }
438
448
  else {
439
449
  const serialized = this._objectsMap?.get(key);
@@ -445,6 +455,9 @@ export class KritzelObjectMap {
445
455
  }
446
456
  else {
447
457
  objectsToUpdate.push(object);
458
+ if (object instanceof KritzelLine) {
459
+ anchorIndexNeedsRebuild = true;
460
+ }
448
461
  }
449
462
  }
450
463
  }
@@ -471,7 +484,7 @@ export class KritzelObjectMap {
471
484
  if (previous && typeof object.adoptTransientStateFrom === 'function') {
472
485
  object.adoptTransientStateFrom(previous);
473
486
  }
474
- this.quadtree.update(object);
487
+ this.quadtree.reindex(object);
475
488
  this._idMap.set(object.id, object);
476
489
  updatedObjects.push(object);
477
490
  }
@@ -490,7 +503,7 @@ export class KritzelObjectMap {
490
503
  if (previous && typeof object.adoptTransientStateFrom === 'function') {
491
504
  object.adoptTransientStateFrom(previous);
492
505
  }
493
- this.quadtree.update(object);
506
+ this.quadtree.reindex(object);
494
507
  this._idMap.set(object.id, object);
495
508
  }
496
509
  else {
@@ -507,7 +520,7 @@ export class KritzelObjectMap {
507
520
  object.refreshObjectDimensions(undefined, true);
508
521
  object.captureUnchangedSnapshots();
509
522
  // Update quadtree again with corrected dimensions
510
- this.quadtree.update(object);
523
+ this.quadtree.reindex(object);
511
524
  });
512
525
  // If regular objects were changed but their SelectionGroup wasn't explicitly updated,
513
526
  // we still need to refresh that SelectionGroup's dimensions
@@ -525,10 +538,16 @@ export class KritzelObjectMap {
525
538
  selectionGroup.refreshObjectDimensions(undefined, true);
526
539
  selectionGroup.captureUnchangedSnapshots();
527
540
  // Update quadtree with corrected dimensions
528
- this.quadtree.update(selectionGroup);
541
+ this.quadtree.reindex(selectionGroup);
529
542
  }
530
543
  }
531
544
  }
545
+ // Rebuild the anchor index if any line anchor connections may have changed, so that
546
+ // subsequent local operations (moving a target, refreshing selection UI) resolve the
547
+ // correct anchored lines on this client.
548
+ if (anchorIndexNeedsRebuild) {
549
+ this._core?.anchorManager.rebuildIndex();
550
+ }
532
551
  // Invalidate the store's selection cache so it fetches the updated SelectionGroup
533
552
  this._core?.store.invalidateSelectionCache();
534
553
  this._core?.rerender();
@@ -770,7 +789,12 @@ export class KritzelObjectMap {
770
789
  if (!existed) {
771
790
  return false;
772
791
  }
773
- this.quadtree.update(object);
792
+ if (this._localOnlyMode || options.skipReindex) {
793
+ this.quadtree.update(object);
794
+ }
795
+ else {
796
+ this.quadtree.reindex(object);
797
+ }
774
798
  this._idMap.set(object.id, object);
775
799
  if (!this._localOnlyMode && this._objectsMap && this.isPersistable(object)) {
776
800
  const serialized = object.serialize();
@@ -88,6 +88,20 @@ export class KritzelQuadtree {
88
88
  }
89
89
  return false;
90
90
  }
91
+ /**
92
+ * Reindexes an existing object by removing it from its current node and
93
+ * reinserting it based on its latest bounding box.
94
+ *
95
+ * @param object - The updated object to reindex
96
+ * @returns `true` if the object was found and reinserted, `false` otherwise
97
+ */
98
+ reindex(object) {
99
+ const removed = this.remove(o => o.id === object.id);
100
+ if (removed === 0) {
101
+ return false;
102
+ }
103
+ return this.insert(object);
104
+ }
91
105
  /**
92
106
  * Removes objects from the quadtree that match the given predicate.
93
107
  * Recursively searches through all nodes and removes matching objects.
@@ -3,4 +3,4 @@
3
3
  * This file is auto-generated by the version bump scripts.
4
4
  * Do not modify manually.
5
5
  */
6
- export const KRITZEL_VERSION = '0.3.30';
6
+ export const KRITZEL_VERSION = '0.3.32';
@@ -1 +1 @@
1
- export{g as getAssetPath,r as render,s as setAssetPath,a as setNonce,b as setPlatformOptions}from"./p-B43upypT.js";export{K as KritzelBaseObject,b as KritzelLine,a as KritzelPath}from"./p-BNu3Akp5.js";export{A as AssetNotFoundError,I as IndexedDBAssetProvider,g as KritzelAssetResolver,d as KritzelBaseTool,b as KritzelGroup,a as KritzelImage,f as KritzelSelectionTool,c as KritzelShape,K as KritzelText,e as KritzelTextTool,S as ShapeType}from"./p-CPnjRgkl.js";export{A as APP_STATE_MIGRATIONS,D as DE_LOCALE,E as EN_LOCALE,F as FR_LOCALE,k as KritzelAlignment,g as KritzelAnchorManager,a as KritzelBrushTool,f as KritzelCursorHelper,K as KritzelCustomElement,j as KritzelCustomElementRendererRegistry,c as KritzelEraserTool,d as KritzelImageTool,i as KritzelLicenseManager,b as KritzelLineTool,h as KritzelLocalizationManager,e as KritzelShapeTool,W as WORKSPACE_MIGRATIONS,r as runMigrations}from"./p-E4tWs1Ga.js";import*as t from"yjs";import{IndexeddbPersistence as n}from"y-indexeddb";import{WebsocketProvider as o}from"y-websocket";import{H as m,a as u}from"./kritzel-editor.js";export{d as DEFAULT_ASSET_STORAGE_CONFIG,D as DEFAULT_BRUSH_CONFIG,c as DEFAULT_LINE_TOOL_CONFIG,b as DEFAULT_TEXT_CONFIG,KritzelEditor,defineCustomElement as defineCustomElementKritzelEditor}from"./kritzel-editor.js";export{K as KritzelWorkspace,W as WORKSPACE_EXPORT_VERSION}from"./p-DhMlShij.js";export{K as KritzelThemeManager,d as darkTheme,l as lightTheme}from"./p-CpzicliK.js";export{C as CURRENT_APP_STATE_SCHEMA_VERSION,a as CURRENT_WORKSPACE_SCHEMA_VERSION}from"./p-CW-VyJgK.js";export{KritzelActiveUsers,defineCustomElement as defineCustomElementKritzelActiveUsers}from"./kritzel-active-users.js";export{KritzelAvatar,defineCustomElement as defineCustomElementKritzelAvatar}from"./kritzel-avatar.js";export{KritzelAwarenessCursors,defineCustomElement as defineCustomElementKritzelAwarenessCursors}from"./kritzel-awareness-cursors.js";export{KritzelBackToContent,defineCustomElement as defineCustomElementKritzelBackToContent}from"./kritzel-back-to-content.js";export{KritzelBrushStyle,defineCustomElement as defineCustomElementKritzelBrushStyle}from"./kritzel-brush-style.js";export{KritzelButton,defineCustomElement as defineCustomElementKritzelButton}from"./kritzel-button.js";export{KritzelColor,defineCustomElement as defineCustomElementKritzelColor}from"./kritzel-color.js";export{KritzelColorPalette,defineCustomElement as defineCustomElementKritzelColorPalette}from"./kritzel-color-palette.js";export{KritzelContextMenu,defineCustomElement as defineCustomElementKritzelContextMenu}from"./kritzel-context-menu.js";export{KritzelControls,defineCustomElement as defineCustomElementKritzelControls}from"./kritzel-controls.js";export{KritzelCurrentUser,defineCustomElement as defineCustomElementKritzelCurrentUser}from"./kritzel-current-user.js";export{KritzelCurrentUserDialog,defineCustomElement as defineCustomElementKritzelCurrentUserDialog}from"./kritzel-current-user-dialog.js";export{KritzelCursorTrail,defineCustomElement as defineCustomElementKritzelCursorTrail}from"./kritzel-cursor-trail.js";export{KritzelDialog,defineCustomElement as defineCustomElementKritzelDialog}from"./kritzel-dialog.js";export{KritzelDropdown,defineCustomElement as defineCustomElementKritzelDropdown}from"./kritzel-dropdown.js";export{KritzelEngine,defineCustomElement as defineCustomElementKritzelEngine}from"./kritzel-engine.js";export{KritzelExport,defineCustomElement as defineCustomElementKritzelExport}from"./kritzel-export.js";export{KritzelFont,defineCustomElement as defineCustomElementKritzelFont}from"./kritzel-font.js";export{KritzelFontFamily,defineCustomElement as defineCustomElementKritzelFontFamily}from"./kritzel-font-family.js";export{KritzelFontSize,defineCustomElement as defineCustomElementKritzelFontSize}from"./kritzel-font-size.js";export{KritzelIcon,defineCustomElement as defineCustomElementKritzelIcon}from"./kritzel-icon.js";export{KritzelInput,defineCustomElement as defineCustomElementKritzelInput}from"./kritzel-input.js";export{KritzelLineEndings,defineCustomElement as defineCustomElementKritzelLineEndings}from"./kritzel-line-endings.js";export{KritzelLoginDialog,defineCustomElement as defineCustomElementKritzelLoginDialog}from"./kritzel-login-dialog.js";export{KritzelMasterDetail,defineCustomElement as defineCustomElementKritzelMasterDetail}from"./kritzel-master-detail.js";export{KritzelMenu,defineCustomElement as defineCustomElementKritzelMenu}from"./kritzel-menu.js";export{KritzelMenuItem,defineCustomElement as defineCustomElementKritzelMenuItem}from"./kritzel-menu-item.js";export{KritzelMoreMenu,defineCustomElement as defineCustomElementKritzelMoreMenu}from"./kritzel-more-menu.js";export{KritzelNotificationCard,defineCustomElement as defineCustomElementKritzelNotificationCard}from"./kritzel-notification-card.js";export{KritzelNumericInput,defineCustomElement as defineCustomElementKritzelNumericInput}from"./kritzel-numeric-input.js";export{KritzelOpacitySlider,defineCustomElement as defineCustomElementKritzelOpacitySlider}from"./kritzel-opacity-slider.js";export{KritzelPillTabs,defineCustomElement as defineCustomElementKritzelPillTabs}from"./kritzel-pill-tabs.js";export{KritzelPortal,defineCustomElement as defineCustomElementKritzelPortal}from"./kritzel-portal.js";export{KritzelSettings,defineCustomElement as defineCustomElementKritzelSettings}from"./kritzel-settings.js";export{KritzelShapeFill,defineCustomElement as defineCustomElementKritzelShapeFill}from"./kritzel-shape-fill.js";export{KritzelShareDialog,defineCustomElement as defineCustomElementKritzelShareDialog}from"./kritzel-share-dialog.js";export{KritzelSlideToggle,defineCustomElement as defineCustomElementKritzelSlideToggle}from"./kritzel-slide-toggle.js";export{KritzelSplitButton,defineCustomElement as defineCustomElementKritzelSplitButton}from"./kritzel-split-button.js";export{KritzelStrokeSize,defineCustomElement as defineCustomElementKritzelStrokeSize}from"./kritzel-stroke-size.js";export{KritzelToolConfig,defineCustomElement as defineCustomElementKritzelToolConfig}from"./kritzel-tool-config.js";export{KritzelTooltip,defineCustomElement as defineCustomElementKritzelTooltip}from"./kritzel-tooltip.js";export{KritzelUtilityPanel,defineCustomElement as defineCustomElementKritzelUtilityPanel}from"./kritzel-utility-panel.js";export{KritzelWatermark,defineCustomElement as defineCustomElementKritzelWatermark}from"./kritzel-watermark.js";export{KritzelWorkspaceManager,defineCustomElement as defineCustomElementKritzelWorkspaceManager}from"./kritzel-workspace-manager.js";export{KritzelZoomPanel,defineCustomElement as defineCustomElementKritzelZoomPanel}from"./kritzel-zoom-panel.js";const z=Math.floor,p=127,y=Number.MAX_SAFE_INTEGER;class w{constructor(){this.cpos=0,this.cbuf=new Uint8Array(100),this.bufs=[]}}const x=()=>new w,T=e=>{const t=new Uint8Array((e=>{let t=e.cpos;for(let s=0;s<e.bufs.length;s++)t+=e.bufs[s].length;return t})(e));let s=0;for(let i=0;i<e.bufs.length;i++){const n=e.bufs[i];t.set(n,s),s+=n.length}return t.set(new Uint8Array(e.cbuf.buffer,0,e.cpos),s),t},P=(e,t)=>{const s=e.cbuf.length;e.cpos===s&&(e.bufs.push(e.cbuf),e.cbuf=new Uint8Array(2*s),e.cpos=0),e.cbuf[e.cpos++]=t},v=(e,t)=>{for(;t>p;)P(e,128|p&t),t=z(t/128);P(e,p&t)},M=(e,t)=>{v(e,t.byteLength),((e,t)=>{const s=e.cbuf.length,i=e.cpos,n=((e,t)=>e<t?e:t)(s-i,t.length),o=t.length-n;e.cbuf.set(t.subarray(0,n),i),e.cpos+=n,o>0&&(e.bufs.push(e.cbuf),e.cbuf=new Uint8Array(((e,t)=>e>t?e:t)(2*s,o)),e.cbuf.set(t.subarray(n)),e.cpos=o)})(e,t)},U=e=>Error(e),_=U("Unexpected end of array"),O=U("Integer out of Range");class R{constructor(e){this.arr=e,this.pos=0}}const L=e=>((e,t)=>{const s=new Uint8Array(e.arr.buffer,e.pos+e.arr.byteOffset,t);return e.pos+=t,s})(e,$(e)),$=e=>{let t=0,s=1;const i=e.arr.length;for(;e.pos<i;){const i=e.arr[e.pos++];if(t+=(i&p)*s,s*=128,i<128)return t;if(t>y)throw O}throw _};class N{type="local";doc;channel;_synced=!1;constructor(e,t,s){this.doc=t,this.channel=new BroadcastChannel(e),this.channel.onmessage=e=>{this.handleMessage(e.data)},this.doc.on("update",this.handleDocUpdate),this.broadcastSync(),setTimeout((()=>{this._synced=!0}),100),s?.quiet||console.info("BroadcastChannel Provider initialized: "+e)}handleDocUpdate=(e,t)=>{if(t!==this){const t=x();v(t,0),M(t,e),this.channel.postMessage(T(t))}};handleMessage(e){const s=(e=>new R(e))(new Uint8Array(e));switch($(s)){case 0:const e=L(s);t.applyUpdate(this.doc,e,this);break;case 1:this.broadcastSync();break;case 2:const i=L(s),n=t.encodeStateAsUpdate(this.doc,i);if(n.length>0){const e=x();v(e,0),M(e,n),this.channel.postMessage(T(e))}}}broadcastSync(){const e=x();v(e,2),M(e,t.encodeStateVector(this.doc)),this.channel.postMessage(T(e))}async connect(){if(!this._synced)return new Promise((e=>{const t=()=>{this._synced?e():setTimeout(t,50)};t()}))}disconnect(){}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleDocUpdate),this.channel.close()}}const B=new Map;class H{type="local";doc;cacheKey;isConnected=!1;constructor(e,t,s){this.doc=t,this.cacheKey=s?.name??e}handleUpdate=(e,s)=>{s!==this&&B.set(this.cacheKey,t.encodeStateAsUpdate(this.doc))};async connect(){if(this.isConnected)return;const e=B.get(this.cacheKey);e&&t.applyUpdate(this.doc,e,this),this.doc.on("update",this.handleUpdate),this.isConnected=!0}disconnect(){this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleUpdate),this.isConnected=!1}static clear(e){void 0!==e?B.delete(e):B.clear()}}class G{type="local";provider;isConnected=!1;constructor(e,t,s){this.provider=new n(s?.name||e,t)}async connect(){if(!this.isConnected)return new Promise((e=>{this.provider.on("synced",(()=>{this.isConnected=!0,e()}))}))}disconnect(){this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class V{type="network";provider;isConnected=!1;_quiet=!1;get awareness(){return this.provider.awareness}constructor(e,t,s){const i=s?.url||"ws://localhost:1234",n=s?.roomName||e;this.provider=new o(i,n,t,{params:s?.params,protocols:s?.protocols,WebSocketPolyfill:s?.WebSocketPolyfill,awareness:s?.awareness,maxBackoffTime:s?.maxBackoffTime,disableBc:!0}),this._quiet=s?.quiet??!1,this.setupEventListeners(),this._quiet||console.info(`WebSocket Provider initialized: ${i}/${n}`)}static with(e){return{create:(t,s,i)=>{const n=i?{...e,...i}:e;return new V(t,s,n)}}}setupEventListeners(){this.provider.on("status",(({status:e})=>{"connected"===e?(this.isConnected=!0,this._quiet||console.info("WebSocket connected")):"disconnected"===e&&(this.isConnected=!1,this._quiet||console.info("WebSocket disconnected"))})),this.provider.on("sync",(e=>{e&&!this._quiet&&console.info("WebSocket synced")}))}async connect(){if(!this.isConnected)return new Promise(((e,t)=>{const s=setTimeout((()=>{t(Error("WebSocket connection timeout"))}),1e4),i=({status:t})=>{"connected"===t&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,e())};this.provider.on("status",i),this.provider.wsconnected&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,e())}))}disconnect(){this.provider&&this.provider.disconnect(),this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class J{type="network";provider;isConnected=!1;isSynced=!1;usesSharedSocket=!1;isDestroyed=!1;connectTimeout=null;pendingConnectReject=null;connectionTimeoutMs;_connectionStatus="disconnected";visibilityHandler=null;onlineHandler=null;get awareness(){return this.provider.awareness}get connectionStatus(){return this._connectionStatus}static sharedWebSocketProvider=null;constructor(e,t,s){const i=s?.name||e,n=s?.url||"ws://localhost:1234";this.connectionTimeoutMs=s?.connectionTimeout??1e4;const o=s?.websocketProvider||J.sharedWebSocketProvider,r={};void 0!==s?.delay&&(r.delay=s.delay),void 0!==s?.factor&&(r.factor=s.factor),void 0!==s?.maxAttempts&&(r.maxAttempts=s.maxAttempts),void 0!==s?.minDelay&&(r.minDelay=s.minDelay),void 0!==s?.maxDelay&&(r.maxDelay=s.maxDelay);const l=()=>{this.isDestroyed||(this.isConnected=!0,this._connectionStatus="connected",s?.quiet||console.info("Hocuspocus connected: "+i),s?.onConnect&&s.onConnect())},a=()=>{this.isDestroyed||(this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected",s?.quiet||console.info("Hocuspocus disconnected: "+i),s?.onDisconnect&&s.onDisconnect())},c=()=>{this.isDestroyed||(this.isSynced=!0,this._connectionStatus="synced",s?.quiet||console.info("Hocuspocus synced: "+i),s?.onSynced&&s.onSynced())},d=e=>{this.isDestroyed||("connecting"===e.status&&(this._connectionStatus="connecting"),s?.onStatus&&s.onStatus(e))};if(o){this.usesSharedSocket=!0;const e={websocketProvider:o,name:i,document:t,token:s?.token||null,onStatus:d,onConnect:l,onDisconnect:a,onSynced:c,...r};void 0!==s?.forceSyncInterval&&(e.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(e.onAuthenticationFailed=s.onAuthenticationFailed),this.provider=new m(e),this.provider.attach(),s?.quiet||console.info("Hocuspocus Provider initialized (multiplexed): "+i)}else{this.usesSharedSocket=!1;const e={url:n,name:i,document:t,token:s?.token||null,autoConnect:!1,onStatus:d,onConnect:l,onDisconnect:a,onSynced:c,...r};void 0!==s?.forceSyncInterval&&(e.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(e.onAuthenticationFailed=s.onAuthenticationFailed),s?.WebSocketPolyfill&&(e.WebSocketPolyfill=s.WebSocketPolyfill),this.provider=new m(e),s?.quiet||console.info(`Hocuspocus Provider initialized: ${n}/${i}`)}this.setupBrowserEventListeners()}setupBrowserEventListeners(){"undefined"!=typeof document&&(this.visibilityHandler=()=>{"visible"!==document.visibilityState||this.isConnected||this.isDestroyed||this.provider.connect()},document.addEventListener("visibilitychange",this.visibilityHandler)),"undefined"!=typeof window&&(this.onlineHandler=()=>{this.isConnected||this.isDestroyed||this.provider.connect()},window.addEventListener("online",this.onlineHandler))}removeBrowserEventListeners(){this.visibilityHandler&&"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.onlineHandler&&"undefined"!=typeof window&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null)}static createSharedWebSocket(e){if(J.sharedWebSocketProvider)return console.warn("Shared WebSocket already exists. Returning existing instance."),J.sharedWebSocketProvider;const t={url:e.url};return e.WebSocketPolyfill&&(t.WebSocketPolyfill=e.WebSocketPolyfill),e.onConnect&&(t.onConnect=e.onConnect),e.onDisconnect&&(t.onDisconnect=e.onDisconnect),e.onStatus&&(t.onStatus=e.onStatus),J.sharedWebSocketProvider=new u(t),console.info("Shared Hocuspocus WebSocket created: "+e.url),J.sharedWebSocketProvider}static destroySharedWebSocket(){J.sharedWebSocketProvider&&(J.sharedWebSocketProvider.destroy(),J.sharedWebSocketProvider=null,console.info("Shared Hocuspocus WebSocket destroyed"))}static getSharedWebSocket(){return J.sharedWebSocketProvider}static with(e){return{create:(t,s,i)=>{const n=i?{...e,...i}:e;return new J(t,s,n)}}}async connect(){if(!this.isSynced&&!this.isDestroyed)return this._connectionStatus="connecting",new Promise(((e,t)=>{this.pendingConnectReject=t,this.connectTimeout=setTimeout((()=>{this.pendingConnectReject=null,this.connectTimeout=null,t(Error("Hocuspocus connection timeout"))}),this.connectionTimeoutMs);const s=()=>{this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),this.isDestroyed||e()};if(this.provider.on("synced",s),this.provider.isSynced)return this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),void e();this.isConnected||this.usesSharedSocket||this.provider.connect()}))}async reconnect(){return this.disconnect(),this.connect()}disconnect(){this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.provider&&(this.usesSharedSocket?this.provider.detach():this.provider.disconnect()),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}destroy(){this.isDestroyed=!0,this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.removeBrowserEventListeners(),this.provider&&this.provider.destroy(),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}}class X{type="remote";name="HttpAssetProvider";_options;constructor(e){this._options=e}static with(e){return{create:()=>new X(e)}}async init(){this._options.quiet||console.info("HttpAssetProvider initialized")}destroy(){}canResolve(e){return!0}async put(e,t){const s=t.id??this.generateUuid(),i={...t,id:s},n=await(this._options.headers?.())??{},o=await this._options.uploadUrl(i),r=this._options.upload??this.defaultUpload;return{id:(await r(o,e,i,n)).id??s,kind:t.kind??"file",mimeType:t.mimeType,size:e.size,createdAt:Date.now(),width:t.width,height:t.height,durationMs:t.durationMs,originalFilename:t.originalFilename}}async resolve(e){return this._options.resolveUrl(e)}async fetch(e){const t=await this._options.resolveUrl(e),s=await(this._options.headers?.())??{},i=await fetch(t,{headers:s});if(!i.ok)throw Error(`[HttpAssetProvider] Failed to fetch asset ${e}: ${i.status} ${i.statusText}`);return i.blob()}async delete(e){if(!this._options.deleteUrl)return;const t=await this._options.deleteUrl(e),s=await(this._options.headers?.())??{},i=await fetch(t,{method:"DELETE",headers:s});if(!i.ok)throw Error(`[HttpAssetProvider] Failed to delete asset ${e}: ${i.status} ${i.statusText}`);this._options.quiet||console.info("HttpAssetProvider: deleted asset "+e)}defaultUpload=async(e,t,s,i)=>{const n=new FormData;n.append("metadata",JSON.stringify(s)),n.append("file",t,s.originalFilename||`${s.id}.${this.extensionFromMime(s.mimeType)}`);const o=await fetch(e,{method:"POST",headers:i,body:n});if(!o.ok)throw Error(`[HttpAssetProvider] Upload failed: ${o.status} ${o.statusText}`);return(o.headers.get("content-type")||"").includes("application/json")?await o.json():{}};extensionFromMime(e){const t=e.indexOf("/");return t>=0?e.slice(t+1):"bin"}generateUuid(){if("undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID)return crypto.randomUUID();const e=crypto.getRandomValues(new Uint8Array(16));e[6]=15&e[6]|64,e[8]=63&e[8]|128;const t=Array.from(e,(e=>e.toString(16).padStart(2,"0"))).join("");return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}}class Z{type="remote";name="PresignedAssetProvider";_options;constructor(e){this._options=e}static with(e){return{create:()=>new Z(e)}}async init(){this._options.quiet||console.info("PresignedAssetProvider initialized")}destroy(){}canResolve(e){return!0}async put(e,t){const s=await this._options.getUploadDescriptor(t),i=s.method??"PUT",n={...s.headers??{}};let o;if(n["Content-Type"]||n["content-type"]||(n["Content-Type"]=t.mimeType),"POST"===i&&s.fields){const t=new FormData;for(const[e,i]of Object.entries(s.fields))t.append(e,i);t.append("file",e),o=t,delete n["Content-Type"],delete n["content-type"]}else o=e;const r=await fetch(s.url,{method:i,headers:n,body:o});if(!r.ok)throw Error(`[PresignedAssetProvider] Upload failed: ${r.status} ${r.statusText}`);return{id:s.id,kind:t.kind??"file",mimeType:t.mimeType,size:e.size,createdAt:Date.now(),width:t.width,height:t.height,durationMs:t.durationMs,originalFilename:t.originalFilename}}async resolve(e){return this._options.getDownloadUrl(e)}async fetch(e){const t=await this._options.getDownloadUrl(e),s=await fetch(t);if(!s.ok)throw Error(`[PresignedAssetProvider] Failed to fetch asset ${e}: ${s.status} ${s.statusText}`);return s.blob()}async delete(e){this._options.deleteAsset&&(await this._options.deleteAsset(e),this._options.quiet||console.info("PresignedAssetProvider: deleted asset "+e))}}export{N as BroadcastSyncProvider,J as HocuspocusSyncProvider,X as HttpAssetProvider,H as InMemorySyncProvider,G as IndexedDBSyncProvider,Z as PresignedAssetProvider,V as WebSocketSyncProvider}
1
+ export{g as getAssetPath,r as render,s as setAssetPath,a as setNonce,b as setPlatformOptions}from"./p-B43upypT.js";export{K as KritzelBaseObject,b as KritzelLine,a as KritzelPath}from"./p-DfIknZRl.js";export{A as AssetNotFoundError,I as IndexedDBAssetProvider,g as KritzelAssetResolver,d as KritzelBaseTool,b as KritzelGroup,a as KritzelImage,f as KritzelSelectionTool,c as KritzelShape,K as KritzelText,e as KritzelTextTool,S as ShapeType}from"./p-Dl3MwBL1.js";export{A as APP_STATE_MIGRATIONS,D as DE_LOCALE,E as EN_LOCALE,F as FR_LOCALE,k as KritzelAlignment,g as KritzelAnchorManager,a as KritzelBrushTool,f as KritzelCursorHelper,K as KritzelCustomElement,j as KritzelCustomElementRendererRegistry,c as KritzelEraserTool,d as KritzelImageTool,i as KritzelLicenseManager,b as KritzelLineTool,h as KritzelLocalizationManager,e as KritzelShapeTool,W as WORKSPACE_MIGRATIONS,r as runMigrations}from"./p-DK2ZByiU.js";import*as t from"yjs";import{IndexeddbPersistence as n}from"y-indexeddb";import{WebsocketProvider as o}from"y-websocket";import{H as m,a as u}from"./kritzel-editor.js";export{d as DEFAULT_ASSET_STORAGE_CONFIG,D as DEFAULT_BRUSH_CONFIG,c as DEFAULT_LINE_TOOL_CONFIG,b as DEFAULT_TEXT_CONFIG,KritzelEditor,defineCustomElement as defineCustomElementKritzelEditor}from"./kritzel-editor.js";export{K as KritzelWorkspace,W as WORKSPACE_EXPORT_VERSION}from"./p-DhMlShij.js";export{K as KritzelThemeManager,d as darkTheme,l as lightTheme}from"./p-CpzicliK.js";export{C as CURRENT_APP_STATE_SCHEMA_VERSION,a as CURRENT_WORKSPACE_SCHEMA_VERSION}from"./p-CW-VyJgK.js";export{KritzelActiveUsers,defineCustomElement as defineCustomElementKritzelActiveUsers}from"./kritzel-active-users.js";export{KritzelAvatar,defineCustomElement as defineCustomElementKritzelAvatar}from"./kritzel-avatar.js";export{KritzelAwarenessCursors,defineCustomElement as defineCustomElementKritzelAwarenessCursors}from"./kritzel-awareness-cursors.js";export{KritzelBackToContent,defineCustomElement as defineCustomElementKritzelBackToContent}from"./kritzel-back-to-content.js";export{KritzelBrushStyle,defineCustomElement as defineCustomElementKritzelBrushStyle}from"./kritzel-brush-style.js";export{KritzelButton,defineCustomElement as defineCustomElementKritzelButton}from"./kritzel-button.js";export{KritzelColor,defineCustomElement as defineCustomElementKritzelColor}from"./kritzel-color.js";export{KritzelColorPalette,defineCustomElement as defineCustomElementKritzelColorPalette}from"./kritzel-color-palette.js";export{KritzelContextMenu,defineCustomElement as defineCustomElementKritzelContextMenu}from"./kritzel-context-menu.js";export{KritzelControls,defineCustomElement as defineCustomElementKritzelControls}from"./kritzel-controls.js";export{KritzelCurrentUser,defineCustomElement as defineCustomElementKritzelCurrentUser}from"./kritzel-current-user.js";export{KritzelCurrentUserDialog,defineCustomElement as defineCustomElementKritzelCurrentUserDialog}from"./kritzel-current-user-dialog.js";export{KritzelCursorTrail,defineCustomElement as defineCustomElementKritzelCursorTrail}from"./kritzel-cursor-trail.js";export{KritzelDialog,defineCustomElement as defineCustomElementKritzelDialog}from"./kritzel-dialog.js";export{KritzelDropdown,defineCustomElement as defineCustomElementKritzelDropdown}from"./kritzel-dropdown.js";export{KritzelEngine,defineCustomElement as defineCustomElementKritzelEngine}from"./kritzel-engine.js";export{KritzelExport,defineCustomElement as defineCustomElementKritzelExport}from"./kritzel-export.js";export{KritzelFont,defineCustomElement as defineCustomElementKritzelFont}from"./kritzel-font.js";export{KritzelFontFamily,defineCustomElement as defineCustomElementKritzelFontFamily}from"./kritzel-font-family.js";export{KritzelFontSize,defineCustomElement as defineCustomElementKritzelFontSize}from"./kritzel-font-size.js";export{KritzelIcon,defineCustomElement as defineCustomElementKritzelIcon}from"./kritzel-icon.js";export{KritzelInput,defineCustomElement as defineCustomElementKritzelInput}from"./kritzel-input.js";export{KritzelLineEndings,defineCustomElement as defineCustomElementKritzelLineEndings}from"./kritzel-line-endings.js";export{KritzelLoginDialog,defineCustomElement as defineCustomElementKritzelLoginDialog}from"./kritzel-login-dialog.js";export{KritzelMasterDetail,defineCustomElement as defineCustomElementKritzelMasterDetail}from"./kritzel-master-detail.js";export{KritzelMenu,defineCustomElement as defineCustomElementKritzelMenu}from"./kritzel-menu.js";export{KritzelMenuItem,defineCustomElement as defineCustomElementKritzelMenuItem}from"./kritzel-menu-item.js";export{KritzelMoreMenu,defineCustomElement as defineCustomElementKritzelMoreMenu}from"./kritzel-more-menu.js";export{KritzelNotificationCard,defineCustomElement as defineCustomElementKritzelNotificationCard}from"./kritzel-notification-card.js";export{KritzelNumericInput,defineCustomElement as defineCustomElementKritzelNumericInput}from"./kritzel-numeric-input.js";export{KritzelOpacitySlider,defineCustomElement as defineCustomElementKritzelOpacitySlider}from"./kritzel-opacity-slider.js";export{KritzelPillTabs,defineCustomElement as defineCustomElementKritzelPillTabs}from"./kritzel-pill-tabs.js";export{KritzelPortal,defineCustomElement as defineCustomElementKritzelPortal}from"./kritzel-portal.js";export{KritzelSettings,defineCustomElement as defineCustomElementKritzelSettings}from"./kritzel-settings.js";export{KritzelShapeFill,defineCustomElement as defineCustomElementKritzelShapeFill}from"./kritzel-shape-fill.js";export{KritzelShareDialog,defineCustomElement as defineCustomElementKritzelShareDialog}from"./kritzel-share-dialog.js";export{KritzelSlideToggle,defineCustomElement as defineCustomElementKritzelSlideToggle}from"./kritzel-slide-toggle.js";export{KritzelSplitButton,defineCustomElement as defineCustomElementKritzelSplitButton}from"./kritzel-split-button.js";export{KritzelStrokeSize,defineCustomElement as defineCustomElementKritzelStrokeSize}from"./kritzel-stroke-size.js";export{KritzelToolConfig,defineCustomElement as defineCustomElementKritzelToolConfig}from"./kritzel-tool-config.js";export{KritzelTooltip,defineCustomElement as defineCustomElementKritzelTooltip}from"./kritzel-tooltip.js";export{KritzelUtilityPanel,defineCustomElement as defineCustomElementKritzelUtilityPanel}from"./kritzel-utility-panel.js";export{KritzelWatermark,defineCustomElement as defineCustomElementKritzelWatermark}from"./kritzel-watermark.js";export{KritzelWorkspaceManager,defineCustomElement as defineCustomElementKritzelWorkspaceManager}from"./kritzel-workspace-manager.js";export{KritzelZoomPanel,defineCustomElement as defineCustomElementKritzelZoomPanel}from"./kritzel-zoom-panel.js";const z=Math.floor,p=127,y=Number.MAX_SAFE_INTEGER;class w{constructor(){this.cpos=0,this.cbuf=new Uint8Array(100),this.bufs=[]}}const x=()=>new w,T=e=>{const t=new Uint8Array((e=>{let t=e.cpos;for(let s=0;s<e.bufs.length;s++)t+=e.bufs[s].length;return t})(e));let s=0;for(let i=0;i<e.bufs.length;i++){const n=e.bufs[i];t.set(n,s),s+=n.length}return t.set(new Uint8Array(e.cbuf.buffer,0,e.cpos),s),t},P=(e,t)=>{const s=e.cbuf.length;e.cpos===s&&(e.bufs.push(e.cbuf),e.cbuf=new Uint8Array(2*s),e.cpos=0),e.cbuf[e.cpos++]=t},v=(e,t)=>{for(;t>p;)P(e,128|p&t),t=z(t/128);P(e,p&t)},M=(e,t)=>{v(e,t.byteLength),((e,t)=>{const s=e.cbuf.length,i=e.cpos,n=((e,t)=>e<t?e:t)(s-i,t.length),o=t.length-n;e.cbuf.set(t.subarray(0,n),i),e.cpos+=n,o>0&&(e.bufs.push(e.cbuf),e.cbuf=new Uint8Array(((e,t)=>e>t?e:t)(2*s,o)),e.cbuf.set(t.subarray(n)),e.cpos=o)})(e,t)},U=e=>Error(e),_=U("Unexpected end of array"),O=U("Integer out of Range");class L{constructor(e){this.arr=e,this.pos=0}}const R=e=>((e,t)=>{const s=new Uint8Array(e.arr.buffer,e.pos+e.arr.byteOffset,t);return e.pos+=t,s})(e,$(e)),$=e=>{let t=0,s=1;const i=e.arr.length;for(;e.pos<i;){const i=e.arr[e.pos++];if(t+=(i&p)*s,s*=128,i<128)return t;if(t>y)throw O}throw _};class B{type="local";doc;channel;_synced=!1;constructor(e,t,s){this.doc=t,this.channel=new BroadcastChannel(e),this.channel.onmessage=e=>{this.handleMessage(e.data)},this.doc.on("update",this.handleDocUpdate),this.broadcastSync(),setTimeout((()=>{this._synced=!0}),100),s?.quiet||console.info("BroadcastChannel Provider initialized: "+e)}handleDocUpdate=(e,t)=>{if(t!==this){const t=x();v(t,0),M(t,e),this.channel.postMessage(T(t))}};handleMessage(e){const s=(e=>new L(e))(new Uint8Array(e));switch($(s)){case 0:const e=R(s);t.applyUpdate(this.doc,e,this);break;case 1:this.broadcastSync();break;case 2:const i=R(s),n=t.encodeStateAsUpdate(this.doc,i);if(n.length>0){const e=x();v(e,0),M(e,n),this.channel.postMessage(T(e))}}}broadcastSync(){const e=x();v(e,2),M(e,t.encodeStateVector(this.doc)),this.channel.postMessage(T(e))}async connect(){if(!this._synced)return new Promise((e=>{const t=()=>{this._synced?e():setTimeout(t,50)};t()}))}disconnect(){}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleDocUpdate),this.channel.close()}}const H=new Map;class N{type="local";doc;cacheKey;isConnected=!1;constructor(e,t,s){this.doc=t,this.cacheKey=s?.name??e}handleUpdate=(e,s)=>{s!==this&&H.set(this.cacheKey,t.encodeStateAsUpdate(this.doc))};async connect(){if(this.isConnected)return;const e=H.get(this.cacheKey);e&&t.applyUpdate(this.doc,e,this),this.doc.on("update",this.handleUpdate),this.isConnected=!0}disconnect(){this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.doc.off("update",this.handleUpdate),this.isConnected=!1}static clear(e){void 0!==e?H.delete(e):H.clear()}}class G{type="local";provider;isConnected=!1;constructor(e,t,s){this.provider=new n(s?.name||e,t)}async connect(){if(!this.isConnected)return new Promise((e=>{this.provider.on("synced",(()=>{this.isConnected=!0,e()}))}))}disconnect(){this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class V{type="network";provider;isConnected=!1;_quiet=!1;get awareness(){return this.provider.awareness}constructor(e,t,s){const i=s?.url||"ws://localhost:1234",n=s?.roomName||e;this.provider=new o(i,n,t,{params:s?.params,protocols:s?.protocols,WebSocketPolyfill:s?.WebSocketPolyfill,awareness:s?.awareness,maxBackoffTime:s?.maxBackoffTime,disableBc:!0}),this._quiet=s?.quiet??!1,this.setupEventListeners(),this._quiet||console.info(`WebSocket Provider initialized: ${i}/${n}`)}static with(e){return{create:(t,s,i)=>{const n=i?{...e,...i}:e;return new V(t,s,n)}}}setupEventListeners(){this.provider.on("status",(({status:e})=>{"connected"===e?(this.isConnected=!0,this._quiet||console.info("WebSocket connected")):"disconnected"===e&&(this.isConnected=!1,this._quiet||console.info("WebSocket disconnected"))})),this.provider.on("sync",(e=>{e&&!this._quiet&&console.info("WebSocket synced")}))}async connect(){if(!this.isConnected)return new Promise(((e,t)=>{const s=setTimeout((()=>{t(Error("WebSocket connection timeout"))}),1e4),i=({status:t})=>{"connected"===t&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,e())};this.provider.on("status",i),this.provider.wsconnected&&(clearTimeout(s),this.provider.off("status",i),this.isConnected=!0,e())}))}disconnect(){this.provider&&this.provider.disconnect(),this.isConnected=!1}async reconnect(){return this.disconnect(),this.connect()}destroy(){this.provider&&this.provider.destroy(),this.isConnected=!1}}class Z{type="network";provider;isConnected=!1;isSynced=!1;usesSharedSocket=!1;isDestroyed=!1;connectTimeout=null;pendingConnectReject=null;connectionTimeoutMs;_connectionStatus="disconnected";visibilityHandler=null;onlineHandler=null;get awareness(){return this.provider.awareness}get connectionStatus(){return this._connectionStatus}static sharedWebSocketProvider=null;constructor(e,t,s){const i=s?.name||e,n=s?.url||"ws://localhost:1234";this.connectionTimeoutMs=s?.connectionTimeout??1e4;const o=s?.websocketProvider||Z.sharedWebSocketProvider,r={};void 0!==s?.delay&&(r.delay=s.delay),void 0!==s?.factor&&(r.factor=s.factor),void 0!==s?.maxAttempts&&(r.maxAttempts=s.maxAttempts),void 0!==s?.minDelay&&(r.minDelay=s.minDelay),void 0!==s?.maxDelay&&(r.maxDelay=s.maxDelay);const l=()=>{this.isDestroyed||(this.isConnected=!0,this._connectionStatus="connected",s?.quiet||console.info("Hocuspocus connected: "+i),s?.onConnect&&s.onConnect())},a=()=>{this.isDestroyed||(this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected",s?.quiet||console.info("Hocuspocus disconnected: "+i),s?.onDisconnect&&s.onDisconnect())},c=()=>{this.isDestroyed||(this.isSynced=!0,this._connectionStatus="synced",s?.quiet||console.info("Hocuspocus synced: "+i),s?.onSynced&&s.onSynced())},d=e=>{this.isDestroyed||("connecting"===e.status&&(this._connectionStatus="connecting"),s?.onStatus&&s.onStatus(e))};if(o){this.usesSharedSocket=!0;const e={websocketProvider:o,name:i,document:t,token:s?.token||null,onStatus:d,onConnect:l,onDisconnect:a,onSynced:c,...r};void 0!==s?.forceSyncInterval&&(e.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(e.onAuthenticationFailed=s.onAuthenticationFailed),this.provider=new m(e),this.provider.attach(),s?.quiet||console.info("Hocuspocus Provider initialized (multiplexed): "+i)}else{this.usesSharedSocket=!1;const e={url:n,name:i,document:t,token:s?.token||null,autoConnect:!1,onStatus:d,onConnect:l,onDisconnect:a,onSynced:c,...r};void 0!==s?.forceSyncInterval&&(e.forceSyncInterval=s.forceSyncInterval),s?.onAuthenticationFailed&&(e.onAuthenticationFailed=s.onAuthenticationFailed),s?.WebSocketPolyfill&&(e.WebSocketPolyfill=s.WebSocketPolyfill),this.provider=new m(e),s?.quiet||console.info(`Hocuspocus Provider initialized: ${n}/${i}`)}this.setupBrowserEventListeners()}setupBrowserEventListeners(){"undefined"!=typeof document&&(this.visibilityHandler=()=>{"visible"!==document.visibilityState||this.isConnected||this.isDestroyed||this.provider.connect()},document.addEventListener("visibilitychange",this.visibilityHandler)),"undefined"!=typeof window&&(this.onlineHandler=()=>{this.isConnected||this.isDestroyed||this.provider.connect()},window.addEventListener("online",this.onlineHandler))}removeBrowserEventListeners(){this.visibilityHandler&&"undefined"!=typeof document&&(document.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=null),this.onlineHandler&&"undefined"!=typeof window&&(window.removeEventListener("online",this.onlineHandler),this.onlineHandler=null)}static createSharedWebSocket(e){if(Z.sharedWebSocketProvider)return console.warn("Shared WebSocket already exists. Returning existing instance."),Z.sharedWebSocketProvider;const t={url:e.url};return e.WebSocketPolyfill&&(t.WebSocketPolyfill=e.WebSocketPolyfill),e.onConnect&&(t.onConnect=e.onConnect),e.onDisconnect&&(t.onDisconnect=e.onDisconnect),e.onStatus&&(t.onStatus=e.onStatus),Z.sharedWebSocketProvider=new u(t),console.info("Shared Hocuspocus WebSocket created: "+e.url),Z.sharedWebSocketProvider}static destroySharedWebSocket(){Z.sharedWebSocketProvider&&(Z.sharedWebSocketProvider.destroy(),Z.sharedWebSocketProvider=null,console.info("Shared Hocuspocus WebSocket destroyed"))}static getSharedWebSocket(){return Z.sharedWebSocketProvider}static with(e){return{create:(t,s,i)=>{const n=i?{...e,...i}:e;return new Z(t,s,n)}}}async connect(){if(!this.isSynced&&!this.isDestroyed)return this._connectionStatus="connecting",new Promise(((e,t)=>{this.pendingConnectReject=t,this.connectTimeout=setTimeout((()=>{this.pendingConnectReject=null,this.connectTimeout=null,t(Error("Hocuspocus connection timeout"))}),this.connectionTimeoutMs);const s=()=>{this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),this.isDestroyed||e()};if(this.provider.on("synced",s),this.provider.isSynced)return this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject=null,this.provider.off("synced",s),void e();this.isConnected||this.usesSharedSocket||this.provider.connect()}))}async reconnect(){return this.disconnect(),this.connect()}disconnect(){this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.provider&&(this.usesSharedSocket?this.provider.detach():this.provider.disconnect()),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}destroy(){this.isDestroyed=!0,this.connectTimeout&&(clearTimeout(this.connectTimeout),this.connectTimeout=null),this.pendingConnectReject&&(this.pendingConnectReject=null),this.removeBrowserEventListeners(),this.provider&&this.provider.destroy(),this.isConnected=!1,this.isSynced=!1,this._connectionStatus="disconnected"}}class J{type="remote";name="HttpAssetProvider";_options;constructor(e){this._options=e}static with(e){return{create:()=>new J(e)}}async init(){this._options.quiet||console.info("HttpAssetProvider initialized")}destroy(){}canResolve(e){return!0}async put(e,t){const s=t.id??this.generateUuid(),i={...t,id:s},n=await(this._options.headers?.())??{},o=await this._options.uploadUrl(i),r=this._options.upload??this.defaultUpload;return{id:(await r(o,e,i,n)).id??s,kind:t.kind??"file",mimeType:t.mimeType,size:e.size,createdAt:Date.now(),width:t.width,height:t.height,durationMs:t.durationMs,originalFilename:t.originalFilename}}async resolve(e){return this._options.resolveUrl(e)}async fetch(e){const t=await this._options.resolveUrl(e),s=await(this._options.headers?.())??{},i=await fetch(t,{headers:s});if(!i.ok)throw Error(`[HttpAssetProvider] Failed to fetch asset ${e}: ${i.status} ${i.statusText}`);return i.blob()}async delete(e){if(!this._options.deleteUrl)return;const t=await this._options.deleteUrl(e),s=await(this._options.headers?.())??{},i=await fetch(t,{method:"DELETE",headers:s});if(!i.ok)throw Error(`[HttpAssetProvider] Failed to delete asset ${e}: ${i.status} ${i.statusText}`);this._options.quiet||console.info("HttpAssetProvider: deleted asset "+e)}defaultUpload=async(e,t,s,i)=>{const n=new FormData;n.append("metadata",JSON.stringify(s)),n.append("file",t,s.originalFilename||`${s.id}.${this.extensionFromMime(s.mimeType)}`);const o=await fetch(e,{method:"POST",headers:i,body:n});if(!o.ok)throw Error(`[HttpAssetProvider] Upload failed: ${o.status} ${o.statusText}`);return(o.headers.get("content-type")||"").includes("application/json")?await o.json():{}};extensionFromMime(e){const t=e.indexOf("/");return t>=0?e.slice(t+1):"bin"}generateUuid(){if("undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID)return crypto.randomUUID();const e=crypto.getRandomValues(new Uint8Array(16));e[6]=15&e[6]|64,e[8]=63&e[8]|128;const t=Array.from(e,(e=>e.toString(16).padStart(2,"0"))).join("");return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}}class X{type="remote";name="PresignedAssetProvider";_options;constructor(e){this._options=e}static with(e){return{create:()=>new X(e)}}async init(){this._options.quiet||console.info("PresignedAssetProvider initialized")}destroy(){}canResolve(e){return!0}async put(e,t){const s=await this._options.getUploadDescriptor(t),i=s.method??"PUT",n={...s.headers??{}};let o;if(n["Content-Type"]||n["content-type"]||(n["Content-Type"]=t.mimeType),"POST"===i&&s.fields){const t=new FormData;for(const[e,i]of Object.entries(s.fields))t.append(e,i);t.append("file",e),o=t,delete n["Content-Type"],delete n["content-type"]}else o=e;const r=await fetch(s.url,{method:i,headers:n,body:o});if(!r.ok)throw Error(`[PresignedAssetProvider] Upload failed: ${r.status} ${r.statusText}`);return{id:s.id,kind:t.kind??"file",mimeType:t.mimeType,size:e.size,createdAt:Date.now(),width:t.width,height:t.height,durationMs:t.durationMs,originalFilename:t.originalFilename}}async resolve(e){return this._options.getDownloadUrl(e)}async fetch(e){const t=await this._options.getDownloadUrl(e),s=await fetch(t);if(!s.ok)throw Error(`[PresignedAssetProvider] Failed to fetch asset ${e}: ${s.status} ${s.statusText}`);return s.blob()}async delete(e){this._options.deleteAsset&&(await this._options.deleteAsset(e),this._options.quiet||console.info("PresignedAssetProvider: deleted asset "+e))}}export{B as BroadcastSyncProvider,Z as HocuspocusSyncProvider,J as HttpAssetProvider,N as InMemorySyncProvider,G as IndexedDBSyncProvider,X as PresignedAssetProvider,V as WebSocketSyncProvider}
@@ -1 +1 @@
1
- import{K as o,d as s}from"./p-B4WfPDj0.js";const p=o,r=s;export{p as KritzelAwarenessCursors,r as defineCustomElement}
1
+ import{K as o,d as s}from"./p-BmX2Vb4Z.js";const m=o,p=s;export{m as KritzelAwarenessCursors,p as defineCustomElement}
@@ -1 +1 @@
1
- import{K as o,d as s}from"./p-BC7jWR_G.js";const p=o,r=s;export{p as KritzelControls,r as defineCustomElement}
1
+ import{K as o,d as s}from"./p-14NPx-_P.js";const p=o,r=s;export{p as KritzelControls,r as defineCustomElement}