@ibodr/schema 0.0.0

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.
package/dist/index.mjs ADDED
@@ -0,0 +1,4084 @@
1
+ import { structuredClone, sortByIndex, objectMapFromEntries, getIndices, objectMapEntries, registerDrawLibraryVersion, mapObjectMapValues, uniqueId, safeParseUrl, assert, filterEntries, objectMapValues, annotateError } from '@ibodr/utils';
2
+ import { T } from '@ibodr/validate';
3
+ import { createMigrationIds, createRecordMigrationSequence, createMigrationSequence, createRecordType, StoreSchema, RecordType } from '@ibodr/store';
4
+ import { computed } from '@ibodr/state';
5
+
6
+ // src/index.ts
7
+ function idValidator(prefix) {
8
+ return T.string.refine((id) => {
9
+ if (!id.startsWith(`${prefix}:`)) {
10
+ throw new Error(`${prefix} ID must start with "${prefix}:"`);
11
+ }
12
+ return id;
13
+ });
14
+ }
15
+
16
+ // src/assets/DrBaseAsset.ts
17
+ var assetIdValidator = idValidator("asset");
18
+ function createAssetValidator(type, props, meta) {
19
+ const propsValidator = props instanceof T.Validator ? props : props ? T.object(props) : T.jsonValue;
20
+ return T.object({
21
+ id: assetIdValidator,
22
+ typeName: T.literal("asset"),
23
+ type: T.literal(type),
24
+ props: propsValidator,
25
+ meta: meta ? T.object(meta) : T.jsonValue
26
+ });
27
+ }
28
+ var bookmarkAssetProps = {
29
+ title: T.string,
30
+ description: T.string,
31
+ image: T.string,
32
+ favicon: T.string,
33
+ src: T.srcUrl.nullable()
34
+ };
35
+ createAssetValidator(
36
+ "bookmark",
37
+ T.object(bookmarkAssetProps)
38
+ );
39
+ var Versions = createMigrationIds("com.draw.asset.bookmark", {
40
+ MakeUrlsValid: 1,
41
+ AddFavicon: 2
42
+ });
43
+ var bookmarkAssetMigrations = createRecordMigrationSequence({
44
+ sequenceId: "com.draw.asset.bookmark",
45
+ recordType: "asset",
46
+ filter: (asset) => asset.type === "bookmark",
47
+ sequence: [
48
+ {
49
+ id: Versions.MakeUrlsValid,
50
+ up: (asset) => {
51
+ if (!T.srcUrl.isValid(asset.props.src)) {
52
+ asset.props.src = "";
53
+ }
54
+ },
55
+ down: (_asset) => {
56
+ }
57
+ },
58
+ {
59
+ id: Versions.AddFavicon,
60
+ up: (asset) => {
61
+ if (!T.srcUrl.isValid(asset.props.favicon)) {
62
+ asset.props.favicon = "";
63
+ }
64
+ },
65
+ down: (asset) => {
66
+ delete asset.props.favicon;
67
+ }
68
+ }
69
+ ]
70
+ });
71
+ var imageAssetProps = {
72
+ w: T.number,
73
+ h: T.number,
74
+ name: T.string,
75
+ isAnimated: T.boolean,
76
+ mimeType: T.string.nullable(),
77
+ src: T.srcUrl.nullable(),
78
+ fileSize: T.nonZeroNumber.optional(),
79
+ pixelRatio: T.positiveNumber.optional()
80
+ };
81
+ createAssetValidator(
82
+ "image",
83
+ T.object(imageAssetProps)
84
+ );
85
+ var Versions2 = createMigrationIds("com.draw.asset.image", {
86
+ AddIsAnimated: 1,
87
+ RenameWidthHeight: 2,
88
+ MakeUrlsValid: 3,
89
+ AddFileSize: 4,
90
+ MakeFileSizeOptional: 5,
91
+ AddPixelRatio: 6
92
+ });
93
+ var imageAssetMigrations = createRecordMigrationSequence({
94
+ sequenceId: "com.draw.asset.image",
95
+ recordType: "asset",
96
+ filter: (asset) => asset.type === "image",
97
+ sequence: [
98
+ {
99
+ id: Versions2.AddIsAnimated,
100
+ up: (asset) => {
101
+ asset.props.isAnimated = false;
102
+ },
103
+ down: (asset) => {
104
+ delete asset.props.isAnimated;
105
+ }
106
+ },
107
+ {
108
+ id: Versions2.RenameWidthHeight,
109
+ up: (asset) => {
110
+ asset.props.w = asset.props.width;
111
+ asset.props.h = asset.props.height;
112
+ delete asset.props.width;
113
+ delete asset.props.height;
114
+ },
115
+ down: (asset) => {
116
+ asset.props.width = asset.props.w;
117
+ asset.props.height = asset.props.h;
118
+ delete asset.props.w;
119
+ delete asset.props.h;
120
+ }
121
+ },
122
+ {
123
+ id: Versions2.MakeUrlsValid,
124
+ up: (asset) => {
125
+ if (!T.srcUrl.isValid(asset.props.src)) {
126
+ asset.props.src = "";
127
+ }
128
+ },
129
+ down: (_asset) => {
130
+ }
131
+ },
132
+ {
133
+ id: Versions2.AddFileSize,
134
+ up: (asset) => {
135
+ asset.props.fileSize = -1;
136
+ },
137
+ down: (asset) => {
138
+ delete asset.props.fileSize;
139
+ }
140
+ },
141
+ {
142
+ id: Versions2.MakeFileSizeOptional,
143
+ up: (asset) => {
144
+ if (asset.props.fileSize === -1) {
145
+ asset.props.fileSize = void 0;
146
+ }
147
+ },
148
+ down: (asset) => {
149
+ if (asset.props.fileSize === void 0) {
150
+ asset.props.fileSize = -1;
151
+ }
152
+ }
153
+ },
154
+ {
155
+ id: Versions2.AddPixelRatio,
156
+ up: (_asset) => {
157
+ },
158
+ down: (asset) => {
159
+ delete asset.props.pixelRatio;
160
+ }
161
+ }
162
+ ]
163
+ });
164
+ var videoAssetProps = {
165
+ w: T.number,
166
+ h: T.number,
167
+ name: T.string,
168
+ isAnimated: T.boolean,
169
+ mimeType: T.string.nullable(),
170
+ src: T.srcUrl.nullable(),
171
+ fileSize: T.number.optional()
172
+ };
173
+ createAssetValidator(
174
+ "video",
175
+ T.object(videoAssetProps)
176
+ );
177
+ var Versions3 = createMigrationIds("com.draw.asset.video", {
178
+ AddIsAnimated: 1,
179
+ RenameWidthHeight: 2,
180
+ MakeUrlsValid: 3,
181
+ AddFileSize: 4,
182
+ MakeFileSizeOptional: 5
183
+ });
184
+ var videoAssetMigrations = createRecordMigrationSequence({
185
+ sequenceId: "com.draw.asset.video",
186
+ recordType: "asset",
187
+ filter: (asset) => asset.type === "video",
188
+ sequence: [
189
+ {
190
+ id: Versions3.AddIsAnimated,
191
+ up: (asset) => {
192
+ asset.props.isAnimated = false;
193
+ },
194
+ down: (asset) => {
195
+ delete asset.props.isAnimated;
196
+ }
197
+ },
198
+ {
199
+ id: Versions3.RenameWidthHeight,
200
+ up: (asset) => {
201
+ asset.props.w = asset.props.width;
202
+ asset.props.h = asset.props.height;
203
+ delete asset.props.width;
204
+ delete asset.props.height;
205
+ },
206
+ down: (asset) => {
207
+ asset.props.width = asset.props.w;
208
+ asset.props.height = asset.props.h;
209
+ delete asset.props.w;
210
+ delete asset.props.h;
211
+ }
212
+ },
213
+ {
214
+ id: Versions3.MakeUrlsValid,
215
+ up: (asset) => {
216
+ if (!T.srcUrl.isValid(asset.props.src)) {
217
+ asset.props.src = "";
218
+ }
219
+ },
220
+ down: (_asset) => {
221
+ }
222
+ },
223
+ {
224
+ id: Versions3.AddFileSize,
225
+ up: (asset) => {
226
+ asset.props.fileSize = -1;
227
+ },
228
+ down: (asset) => {
229
+ delete asset.props.fileSize;
230
+ }
231
+ },
232
+ {
233
+ id: Versions3.MakeFileSizeOptional,
234
+ up: (asset) => {
235
+ if (asset.props.fileSize === -1) {
236
+ asset.props.fileSize = void 0;
237
+ }
238
+ },
239
+ down: (asset) => {
240
+ if (asset.props.fileSize === void 0) {
241
+ asset.props.fileSize = -1;
242
+ }
243
+ }
244
+ }
245
+ ]
246
+ });
247
+ var vecModelValidator = T.object({
248
+ x: T.number,
249
+ y: T.number,
250
+ z: T.number.optional()
251
+ });
252
+ var boxModelValidator = T.object({
253
+ x: T.number,
254
+ y: T.number,
255
+ w: T.number,
256
+ h: T.number
257
+ });
258
+ var opacityValidator = T.unitInterval;
259
+
260
+ // src/shapes/DrBaseShape.ts
261
+ var parentIdValidator = T.string.refine((id) => {
262
+ if (!id.startsWith("page:") && !id.startsWith("shape:")) {
263
+ throw new Error('Parent ID must start with "page:" or "shape:"');
264
+ }
265
+ return id;
266
+ });
267
+ var shapeIdValidator = idValidator("shape");
268
+ function createShapeValidator(type, props, meta) {
269
+ return T.object({
270
+ id: shapeIdValidator,
271
+ typeName: T.literal("shape"),
272
+ x: T.number,
273
+ y: T.number,
274
+ rotation: T.number,
275
+ index: T.indexKey,
276
+ parentId: parentIdValidator,
277
+ type: T.literal(type),
278
+ isLocked: T.boolean,
279
+ opacity: opacityValidator,
280
+ props: props ? T.object(props) : T.jsonValue,
281
+ meta: meta ? T.object(meta) : T.jsonValue
282
+ });
283
+ }
284
+
285
+ // src/bindings/DrBaseBinding.ts
286
+ var bindingIdValidator = idValidator("binding");
287
+ function createBindingValidator(type, props, meta) {
288
+ return T.object({
289
+ id: bindingIdValidator,
290
+ typeName: T.literal("binding"),
291
+ type: T.literal(type),
292
+ fromId: shapeIdValidator,
293
+ toId: shapeIdValidator,
294
+ props: props ? T.object(props) : T.jsonValue,
295
+ meta: meta ? T.object(meta) : T.jsonValue
296
+ });
297
+ }
298
+
299
+ // src/records/DrBinding.ts
300
+ createMigrationIds("com.draw.binding", {});
301
+ var rootBindingMigrations = createRecordMigrationSequence({
302
+ sequenceId: "com.draw.binding",
303
+ recordType: "binding",
304
+ sequence: []
305
+ });
306
+ function isBinding(record) {
307
+ if (!record) return false;
308
+ return record.typeName === "binding";
309
+ }
310
+ function isBindingId(id) {
311
+ if (!id) return false;
312
+ return id.startsWith("binding:");
313
+ }
314
+ function createBindingId(id) {
315
+ return `binding:${id ?? uniqueId()}`;
316
+ }
317
+ function createBindingPropsMigrationSequence(migrations) {
318
+ return migrations;
319
+ }
320
+ function createBindingPropsMigrationIds(bindingType, ids) {
321
+ return mapObjectMapValues(ids, (_k, v) => `com.draw.binding.${bindingType}/${v}`);
322
+ }
323
+ function createBindingRecordType(bindings) {
324
+ return createRecordType("binding", {
325
+ scope: "document",
326
+ validator: T.model(
327
+ "binding",
328
+ T.union(
329
+ "type",
330
+ mapObjectMapValues(
331
+ bindings,
332
+ (type, { props, meta }) => createBindingValidator(type, props, meta)
333
+ )
334
+ )
335
+ )
336
+ }).withDefaultProperties(() => ({
337
+ meta: {}
338
+ }));
339
+ }
340
+ var richTextValidator = T.object({
341
+ type: T.string,
342
+ content: T.arrayOf(T.unknown),
343
+ attrs: T.any.optional()
344
+ });
345
+ function toRichText(text) {
346
+ const lines = text.split("\n");
347
+ const content = lines.map((text2) => {
348
+ if (!text2) {
349
+ return {
350
+ type: "paragraph"
351
+ };
352
+ }
353
+ return {
354
+ type: "paragraph",
355
+ content: [{ type: "text", text: text2 }]
356
+ };
357
+ });
358
+ return {
359
+ type: "doc",
360
+ content
361
+ };
362
+ }
363
+ var StyleProp = class _StyleProp {
364
+ /** @internal */
365
+ constructor(id, defaultValue, type) {
366
+ this.id = id;
367
+ this.defaultValue = defaultValue;
368
+ this.type = type;
369
+ }
370
+ id;
371
+ defaultValue;
372
+ type;
373
+ /**
374
+ * Define a new {@link StyleProp}.
375
+ *
376
+ * @param uniqueId - Each StyleProp must have a unique ID. We recommend you prefix this with
377
+ * your app/library name.
378
+ * @param options -
379
+ * - `defaultValue`: The default value for this style prop.
380
+ *
381
+ * - `type`: Optionally, describe what type of data you expect for this style prop.
382
+ *
383
+ * @example
384
+ * ```ts
385
+ * import {T} from '@ibodr/validate'
386
+ * import {StyleProp} from '@ibodr/schema'
387
+ *
388
+ * const MyLineWidthProp = StyleProp.define('myApp:lineWidth', {
389
+ * defaultValue: 1,
390
+ * type: T.number,
391
+ * })
392
+ * ```
393
+ * @public
394
+ */
395
+ static define(uniqueId4, options) {
396
+ const { defaultValue, type = T.any } = options;
397
+ return new _StyleProp(uniqueId4, defaultValue, type);
398
+ }
399
+ /**
400
+ * Define a new {@link StyleProp} as a list of possible values.
401
+ *
402
+ * @param uniqueId - Each StyleProp must have a unique ID. We recommend you prefix this with
403
+ * your app/library name.
404
+ * @param options -
405
+ * - `defaultValue`: The default value for this style prop.
406
+ *
407
+ * - `values`: An array of possible values of this style prop.
408
+ *
409
+ * @example
410
+ * ```ts
411
+ * import {StyleProp} from '@ibodr/schema'
412
+ *
413
+ * const MySizeProp = StyleProp.defineEnum('myApp:size', {
414
+ * defaultValue: 'medium',
415
+ * values: ['small', 'medium', 'large'],
416
+ * })
417
+ * ```
418
+ */
419
+ static defineEnum(uniqueId4, options) {
420
+ const { defaultValue, values } = options;
421
+ return new EnumStyleProp(uniqueId4, defaultValue, values);
422
+ }
423
+ setDefaultValue(value) {
424
+ this.defaultValue = value;
425
+ }
426
+ validate(value) {
427
+ return this.type.validate(value);
428
+ }
429
+ validateUsingKnownGoodVersion(prevValue, newValue) {
430
+ if (this.type.validateUsingKnownGoodVersion) {
431
+ return this.type.validateUsingKnownGoodVersion(prevValue, newValue);
432
+ } else {
433
+ return this.validate(newValue);
434
+ }
435
+ }
436
+ };
437
+ var EnumStyleProp = class extends StyleProp {
438
+ /** @internal */
439
+ constructor(id, defaultValue, values) {
440
+ super(id, defaultValue, T.literalEnum(...values));
441
+ this.values = [...values];
442
+ }
443
+ values;
444
+ /**
445
+ * Add new values to this enum style prop at runtime. This is useful for extending
446
+ * the built-in styles with custom values (e.g. adding custom colors). Be sure to
447
+ * also modify the associated types.
448
+ *
449
+ * @param newValues - The new values to add.
450
+ *
451
+ * @public
452
+ */
453
+ addValues(...newValues) {
454
+ for (const v of newValues) {
455
+ if (!this.values.includes(v)) {
456
+ this.values.push(v);
457
+ }
458
+ }
459
+ this.type = T.literalEnum(...this.values);
460
+ }
461
+ /**
462
+ * Remove values from this enum style prop at runtime. This is useful for narrowing
463
+ * the built-in styles with custom values (e.g. adding custom colors). Be sure to
464
+ * also modify the associated types.
465
+ *
466
+ * @param valuesToRemove - The values to remove.
467
+ *
468
+ * @public
469
+ */
470
+ removeValues(...valuesToRemove) {
471
+ for (const v of valuesToRemove) {
472
+ if (this.values.includes(v)) {
473
+ this.values.splice(this.values.indexOf(v), 1);
474
+ }
475
+ }
476
+ this.type = T.literalEnum(...this.values);
477
+ }
478
+ };
479
+
480
+ // src/records/DrShape.ts
481
+ var rootShapeVersions = createMigrationIds("com.draw.shape", {
482
+ AddIsLocked: 1,
483
+ HoistOpacity: 2,
484
+ AddMeta: 3,
485
+ AddWhite: 4
486
+ });
487
+ var rootShapeMigrations = createRecordMigrationSequence({
488
+ sequenceId: "com.draw.shape",
489
+ recordType: "shape",
490
+ sequence: [
491
+ {
492
+ id: rootShapeVersions.AddIsLocked,
493
+ up: (record) => {
494
+ record.isLocked = false;
495
+ },
496
+ down: (record) => {
497
+ delete record.isLocked;
498
+ }
499
+ },
500
+ {
501
+ id: rootShapeVersions.HoistOpacity,
502
+ up: (record) => {
503
+ record.opacity = Number(record.props.opacity ?? "1");
504
+ delete record.props.opacity;
505
+ },
506
+ down: (record) => {
507
+ const opacity = record.opacity;
508
+ delete record.opacity;
509
+ record.props.opacity = opacity < 0.175 ? "0.1" : opacity < 0.375 ? "0.25" : opacity < 0.625 ? "0.5" : opacity < 0.875 ? "0.75" : "1";
510
+ }
511
+ },
512
+ {
513
+ id: rootShapeVersions.AddMeta,
514
+ up: (record) => {
515
+ record.meta = {};
516
+ }
517
+ },
518
+ {
519
+ id: rootShapeVersions.AddWhite,
520
+ up: (_record) => {
521
+ },
522
+ down: (record) => {
523
+ if (record.props.color === "white") {
524
+ record.props.color = "black";
525
+ }
526
+ }
527
+ }
528
+ ]
529
+ });
530
+ function isShape(record) {
531
+ if (!record) return false;
532
+ return record.typeName === "shape";
533
+ }
534
+ function isShapeId(id) {
535
+ if (!id) return false;
536
+ return id.startsWith("shape:");
537
+ }
538
+ function createShapeId(id) {
539
+ return `shape:${id ?? uniqueId()}`;
540
+ }
541
+ function getShapePropKeysByStyle(props) {
542
+ const propKeysByStyle = /* @__PURE__ */ new Map();
543
+ for (const [key, prop] of Object.entries(props)) {
544
+ if (prop instanceof StyleProp) {
545
+ if (propKeysByStyle.has(prop)) {
546
+ throw new Error(
547
+ `Duplicate style prop ${prop.id}. Each style prop can only be used once within a shape.`
548
+ );
549
+ }
550
+ propKeysByStyle.set(prop, key);
551
+ }
552
+ }
553
+ return propKeysByStyle;
554
+ }
555
+ function createShapePropsMigrationSequence(migrations) {
556
+ return migrations;
557
+ }
558
+ function createShapePropsMigrationIds(shapeType, ids) {
559
+ return mapObjectMapValues(ids, (_k, v) => `com.draw.shape.${shapeType}/${v}`);
560
+ }
561
+ function createShapeRecordType(shapes) {
562
+ return createRecordType("shape", {
563
+ scope: "document",
564
+ validator: T.model(
565
+ "shape",
566
+ T.union(
567
+ "type",
568
+ mapObjectMapValues(
569
+ shapes,
570
+ (type, { props, meta }) => createShapeValidator(type, props, meta)
571
+ )
572
+ )
573
+ )
574
+ }).withDefaultProperties(() => ({
575
+ x: 0,
576
+ y: 0,
577
+ rotation: 0,
578
+ isLocked: false,
579
+ opacity: 1,
580
+ meta: {}
581
+ }));
582
+ }
583
+ function processPropsMigrations(typeName, records) {
584
+ const result = [];
585
+ for (const [subType, { migrations }] of Object.entries(records)) {
586
+ const sequenceId = `com.draw.${typeName}.${subType}`;
587
+ if (!migrations) {
588
+ result.push(
589
+ createMigrationSequence({
590
+ sequenceId,
591
+ retroactive: true,
592
+ sequence: []
593
+ })
594
+ );
595
+ } else if ("sequenceId" in migrations) {
596
+ assert(
597
+ sequenceId === migrations.sequenceId,
598
+ `sequenceId mismatch for ${subType} ${RecordType} migrations. Expected '${sequenceId}', got '${migrations.sequenceId}'`
599
+ );
600
+ result.push(migrations);
601
+ } else if ("sequence" in migrations) {
602
+ result.push(
603
+ createMigrationSequence({
604
+ sequenceId,
605
+ retroactive: true,
606
+ sequence: migrations.sequence.map(
607
+ (m) => "id" in m ? createPropsMigration(typeName, subType, m) : m
608
+ )
609
+ })
610
+ );
611
+ } else {
612
+ result.push(
613
+ createMigrationSequence({
614
+ sequenceId,
615
+ retroactive: true,
616
+ sequence: Object.keys(migrations.migrators).map((k) => Number(k)).sort((a, b) => a - b).map(
617
+ (version) => ({
618
+ id: `${sequenceId}/${version}`,
619
+ scope: "record",
620
+ filter: (r) => r.typeName === typeName && r.type === subType,
621
+ up: (record) => {
622
+ const result2 = migrations.migrators[version].up(record);
623
+ if (result2) {
624
+ return result2;
625
+ }
626
+ },
627
+ down: (record) => {
628
+ const result2 = migrations.migrators[version].down(record);
629
+ if (result2) {
630
+ return result2;
631
+ }
632
+ }
633
+ })
634
+ )
635
+ })
636
+ );
637
+ }
638
+ }
639
+ return result;
640
+ }
641
+ function createPropsMigration(typeName, subType, m) {
642
+ return {
643
+ id: m.id,
644
+ dependsOn: m.dependsOn,
645
+ scope: "record",
646
+ filter: (r) => r.typeName === typeName && r.type === subType,
647
+ up: (record) => {
648
+ const result = m.up(record.props);
649
+ if (result) {
650
+ record.props = result;
651
+ }
652
+ },
653
+ down: typeof m.down === "function" ? (record) => {
654
+ const result = m.down(record.props);
655
+ if (result) {
656
+ record.props = result;
657
+ }
658
+ } : void 0
659
+ };
660
+ }
661
+
662
+ // src/styles/DrColorStyle.ts
663
+ var defaultColorNames = [
664
+ "black",
665
+ "grey",
666
+ "light-violet",
667
+ "violet",
668
+ "blue",
669
+ "light-blue",
670
+ "yellow",
671
+ "orange",
672
+ "green",
673
+ "light-green",
674
+ "light-red",
675
+ "red",
676
+ "white"
677
+ ];
678
+ var DefaultColorStyle = StyleProp.defineEnum("draw:color", {
679
+ defaultValue: "black",
680
+ values: defaultColorNames
681
+ });
682
+ var DefaultLabelColorStyle = StyleProp.defineEnum("draw:labelColor", {
683
+ defaultValue: "black",
684
+ values: defaultColorNames
685
+ });
686
+ function registerColorsFromThemes(definitions) {
687
+ const colorNames = /* @__PURE__ */ new Set();
688
+ for (const def of Object.values(definitions)) {
689
+ for (const colorPalette of [def.colors.light, def.colors.dark]) {
690
+ for (const [key, value] of Object.entries(colorPalette)) {
691
+ if (typeof value === "object" && value !== null) {
692
+ colorNames.add(key);
693
+ }
694
+ }
695
+ }
696
+ }
697
+ if (colorNames.size > 0) {
698
+ DefaultColorStyle.addValues(...colorNames);
699
+ DefaultLabelColorStyle.addValues(...colorNames);
700
+ }
701
+ const toRemove = DefaultColorStyle.values.filter((v) => !colorNames.has(v));
702
+ if (toRemove.length > 0) {
703
+ DefaultColorStyle.removeValues(...toRemove);
704
+ DefaultLabelColorStyle.removeValues(...toRemove);
705
+ }
706
+ {
707
+ for (const def of Object.values(definitions)) {
708
+ for (const color of colorNames) {
709
+ if (!(color in def.colors.light)) {
710
+ console.warn(
711
+ `Theme '${def.id}' light palette is missing color '${color}'. Shapes using this color won't render correctly.`
712
+ );
713
+ }
714
+ if (!(color in def.colors.dark)) {
715
+ console.warn(
716
+ `Theme '${def.id}' dark palette is missing color '${color}'. Shapes using this color won't render correctly.`
717
+ );
718
+ }
719
+ }
720
+ }
721
+ }
722
+ }
723
+
724
+ // src/styles/DrDashStyle.ts
725
+ var DefaultDashStyle = StyleProp.defineEnum("draw:dash", {
726
+ defaultValue: "draw",
727
+ values: ["draw", "solid", "dashed", "dotted", "none"]
728
+ });
729
+
730
+ // src/styles/DrFillStyle.ts
731
+ var DefaultFillStyle = StyleProp.defineEnum("draw:fill", {
732
+ defaultValue: "none",
733
+ values: ["none", "semi", "solid", "pattern", "fill", "lined-fill"]
734
+ });
735
+
736
+ // src/styles/DrFontStyle.ts
737
+ var DefaultFontStyle = StyleProp.defineEnum("draw:font", {
738
+ defaultValue: "draw",
739
+ values: ["draw", "sans", "serif", "mono"]
740
+ });
741
+ var DefaultFontFamilies = {
742
+ draw: "'draw_draw', sans-serif",
743
+ sans: "'draw_sans', sans-serif",
744
+ serif: "'draw_serif', serif",
745
+ mono: "'draw_mono', monospace"
746
+ };
747
+ function isFontEntry(value) {
748
+ return typeof value === "object" && value !== null && "fontFamily" in value;
749
+ }
750
+ function registerFontsFromThemes(definitions) {
751
+ const fontNames = /* @__PURE__ */ new Set();
752
+ for (const def of Object.values(definitions)) {
753
+ if (!def.fonts) continue;
754
+ for (const [key, value] of Object.entries(def.fonts)) {
755
+ if (isFontEntry(value)) {
756
+ fontNames.add(key);
757
+ }
758
+ }
759
+ }
760
+ const toAdd = [...fontNames].filter((v) => !DefaultFontStyle.values.includes(v));
761
+ if (toAdd.length > 0) {
762
+ DefaultFontStyle.addValues(...toAdd);
763
+ }
764
+ const toRemove = DefaultFontStyle.values.filter((v) => !fontNames.has(v));
765
+ if (toRemove.length > 0) {
766
+ DefaultFontStyle.removeValues(...toRemove);
767
+ }
768
+ {
769
+ for (const def of Object.values(definitions)) {
770
+ for (const font of fontNames) {
771
+ if (!def.fonts || !(font in def.fonts)) {
772
+ console.warn(
773
+ `Theme '${def.id}' is missing font '${font}'. Shapes using this font won't render correctly in this theme.`
774
+ );
775
+ }
776
+ }
777
+ }
778
+ }
779
+ }
780
+
781
+ // src/styles/DrSizeStyle.ts
782
+ var DefaultSizeStyle = StyleProp.defineEnum("draw:size", {
783
+ defaultValue: "m",
784
+ values: ["s", "m", "l", "xl"]
785
+ });
786
+
787
+ // src/shapes/DrArrowShape.ts
788
+ var arrowKinds = ["arc", "elbow"];
789
+ var ArrowShapeKindStyle = StyleProp.defineEnum("draw:arrowKind", {
790
+ defaultValue: "arc",
791
+ values: arrowKinds
792
+ });
793
+ var arrowheadTypes = [
794
+ "arrow",
795
+ "triangle",
796
+ "square",
797
+ "dot",
798
+ "pipe",
799
+ "diamond",
800
+ "inverted",
801
+ "bar",
802
+ "none"
803
+ ];
804
+ var ArrowShapeArrowheadStartStyle = StyleProp.defineEnum("draw:arrowheadStart", {
805
+ defaultValue: "none",
806
+ values: arrowheadTypes
807
+ });
808
+ var ArrowShapeArrowheadEndStyle = StyleProp.defineEnum("draw:arrowheadEnd", {
809
+ defaultValue: "arrow",
810
+ values: arrowheadTypes
811
+ });
812
+ var arrowShapeProps = {
813
+ kind: ArrowShapeKindStyle,
814
+ labelColor: DefaultLabelColorStyle,
815
+ color: DefaultColorStyle,
816
+ fill: DefaultFillStyle,
817
+ dash: DefaultDashStyle,
818
+ size: DefaultSizeStyle,
819
+ arrowheadStart: ArrowShapeArrowheadStartStyle,
820
+ arrowheadEnd: ArrowShapeArrowheadEndStyle,
821
+ font: DefaultFontStyle,
822
+ start: vecModelValidator,
823
+ end: vecModelValidator,
824
+ bend: T.number,
825
+ richText: richTextValidator,
826
+ labelPosition: T.number,
827
+ scale: T.nonZeroNumber,
828
+ elbowMidPoint: T.number
829
+ };
830
+ var arrowShapeVersions = createShapePropsMigrationIds("arrow", {
831
+ AddLabelColor: 1,
832
+ AddIsPrecise: 2,
833
+ AddLabelPosition: 3,
834
+ ExtractBindings: 4,
835
+ AddScale: 5,
836
+ AddElbow: 6,
837
+ AddRichText: 7,
838
+ AddRichTextAttrs: 8
839
+ });
840
+ function propsMigration(migration) {
841
+ return createPropsMigration("shape", "arrow", migration);
842
+ }
843
+ var arrowShapeMigrations = createMigrationSequence({
844
+ sequenceId: "com.draw.shape.arrow",
845
+ retroactive: false,
846
+ sequence: [
847
+ propsMigration({
848
+ id: arrowShapeVersions.AddLabelColor,
849
+ up: (props) => {
850
+ props.labelColor = "black";
851
+ },
852
+ down: "retired"
853
+ }),
854
+ propsMigration({
855
+ id: arrowShapeVersions.AddIsPrecise,
856
+ up: ({ start, end }) => {
857
+ if (start.type === "binding") {
858
+ start.isPrecise = !(start.normalizedAnchor.x === 0.5 && start.normalizedAnchor.y === 0.5);
859
+ }
860
+ if (end.type === "binding") {
861
+ end.isPrecise = !(end.normalizedAnchor.x === 0.5 && end.normalizedAnchor.y === 0.5);
862
+ }
863
+ },
864
+ down: ({ start, end }) => {
865
+ if (start.type === "binding") {
866
+ if (!start.isPrecise) {
867
+ start.normalizedAnchor = { x: 0.5, y: 0.5 };
868
+ }
869
+ delete start.isPrecise;
870
+ }
871
+ if (end.type === "binding") {
872
+ if (!end.isPrecise) {
873
+ end.normalizedAnchor = { x: 0.5, y: 0.5 };
874
+ }
875
+ delete end.isPrecise;
876
+ }
877
+ }
878
+ }),
879
+ propsMigration({
880
+ id: arrowShapeVersions.AddLabelPosition,
881
+ up: (props) => {
882
+ props.labelPosition = 0.5;
883
+ },
884
+ down: (props) => {
885
+ delete props.labelPosition;
886
+ }
887
+ }),
888
+ {
889
+ id: arrowShapeVersions.ExtractBindings,
890
+ scope: "storage",
891
+ up: (storage) => {
892
+ const updates = [];
893
+ for (const record of storage.values()) {
894
+ if (record.typeName !== "shape" || record.type !== "arrow") continue;
895
+ const arrow = record;
896
+ const newArrow = structuredClone(arrow);
897
+ const { start, end } = arrow.props;
898
+ if (start.type === "binding") {
899
+ const id = createBindingId();
900
+ const binding = {
901
+ typeName: "binding",
902
+ id,
903
+ type: "arrow",
904
+ fromId: arrow.id,
905
+ toId: start.boundShapeId,
906
+ meta: {},
907
+ props: {
908
+ terminal: "start",
909
+ normalizedAnchor: start.normalizedAnchor,
910
+ isExact: start.isExact,
911
+ isPrecise: start.isPrecise
912
+ }
913
+ };
914
+ updates.push([id, binding]);
915
+ newArrow.props.start = { x: 0, y: 0 };
916
+ } else {
917
+ delete newArrow.props.start.type;
918
+ }
919
+ if (end.type === "binding") {
920
+ const id = createBindingId();
921
+ const binding = {
922
+ typeName: "binding",
923
+ id,
924
+ type: "arrow",
925
+ fromId: arrow.id,
926
+ toId: end.boundShapeId,
927
+ meta: {},
928
+ props: {
929
+ terminal: "end",
930
+ normalizedAnchor: end.normalizedAnchor,
931
+ isExact: end.isExact,
932
+ isPrecise: end.isPrecise
933
+ }
934
+ };
935
+ updates.push([id, binding]);
936
+ newArrow.props.end = { x: 0, y: 0 };
937
+ } else {
938
+ delete newArrow.props.end.type;
939
+ }
940
+ updates.push([arrow.id, newArrow]);
941
+ }
942
+ for (const [id, record] of updates) {
943
+ storage.set(id, record);
944
+ }
945
+ }
946
+ },
947
+ propsMigration({
948
+ id: arrowShapeVersions.AddScale,
949
+ up: (props) => {
950
+ props.scale = 1;
951
+ },
952
+ down: (props) => {
953
+ delete props.scale;
954
+ }
955
+ }),
956
+ propsMigration({
957
+ id: arrowShapeVersions.AddElbow,
958
+ up: (props) => {
959
+ props.kind = "arc";
960
+ props.elbowMidPoint = 0.5;
961
+ },
962
+ down: (props) => {
963
+ delete props.kind;
964
+ delete props.elbowMidPoint;
965
+ }
966
+ }),
967
+ propsMigration({
968
+ id: arrowShapeVersions.AddRichText,
969
+ up: (props) => {
970
+ props.richText = toRichText(props.text);
971
+ delete props.text;
972
+ }
973
+ // N.B. Explicitly no down state so that we force clients to update.
974
+ // down: (props) => {
975
+ // delete props.richText
976
+ // },
977
+ }),
978
+ propsMigration({
979
+ id: arrowShapeVersions.AddRichTextAttrs,
980
+ up: (_props) => {
981
+ },
982
+ down: (props) => {
983
+ if (props.richText && "attrs" in props.richText) {
984
+ delete props.richText.attrs;
985
+ }
986
+ }
987
+ })
988
+ ]
989
+ });
990
+
991
+ // src/bindings/DrArrowBinding.ts
992
+ var ElbowArrowSnap = T.literalEnum("center", "edge-point", "edge", "none");
993
+ var arrowBindingProps = {
994
+ terminal: T.literalEnum("start", "end"),
995
+ normalizedAnchor: vecModelValidator,
996
+ isExact: T.boolean,
997
+ isPrecise: T.boolean,
998
+ snap: ElbowArrowSnap
999
+ };
1000
+ var arrowBindingVersions = createBindingPropsMigrationIds("arrow", {
1001
+ AddSnap: 1
1002
+ });
1003
+ var arrowBindingMigrations = createBindingPropsMigrationSequence({
1004
+ sequence: [
1005
+ { dependsOn: [arrowShapeVersions.ExtractBindings] },
1006
+ {
1007
+ id: arrowBindingVersions.AddSnap,
1008
+ up: (props) => {
1009
+ props.snap = "none";
1010
+ },
1011
+ down: (props) => {
1012
+ delete props.snap;
1013
+ }
1014
+ }
1015
+ ]
1016
+ });
1017
+ var cameraValidator = T.model(
1018
+ "camera",
1019
+ T.object({
1020
+ typeName: T.literal("camera"),
1021
+ id: idValidator("camera"),
1022
+ x: T.number,
1023
+ y: T.number,
1024
+ z: T.number,
1025
+ meta: T.jsonValue
1026
+ })
1027
+ );
1028
+ var cameraVersions = createMigrationIds("com.draw.camera", {
1029
+ AddMeta: 1
1030
+ });
1031
+ var cameraMigrations = createRecordMigrationSequence({
1032
+ sequenceId: "com.draw.camera",
1033
+ recordType: "camera",
1034
+ sequence: [
1035
+ {
1036
+ id: cameraVersions.AddMeta,
1037
+ up: (record) => {
1038
+ record.meta = {};
1039
+ }
1040
+ }
1041
+ ]
1042
+ });
1043
+ var CameraRecordType = createRecordType("camera", {
1044
+ validator: cameraValidator,
1045
+ scope: "session"
1046
+ }).withDefaultProperties(
1047
+ () => ({
1048
+ x: 0,
1049
+ y: 0,
1050
+ z: 1,
1051
+ meta: {}
1052
+ })
1053
+ );
1054
+ var TL_CURSOR_TYPES = /* @__PURE__ */ new Set([
1055
+ "none",
1056
+ "default",
1057
+ "pointer",
1058
+ "cross",
1059
+ "grab",
1060
+ "rotate",
1061
+ "grabbing",
1062
+ "resize-edge",
1063
+ "resize-corner",
1064
+ "text",
1065
+ "move",
1066
+ "ew-resize",
1067
+ "ns-resize",
1068
+ "nesw-resize",
1069
+ "nwse-resize",
1070
+ "nesw-rotate",
1071
+ "nwse-rotate",
1072
+ "swne-rotate",
1073
+ "senw-rotate",
1074
+ "zoom-in",
1075
+ "zoom-out"
1076
+ ]);
1077
+ var cursorTypeValidator = T.setEnum(TL_CURSOR_TYPES);
1078
+ var cursorValidator = T.object({
1079
+ type: cursorTypeValidator,
1080
+ rotation: T.number
1081
+ });
1082
+ var TL_CANVAS_UI_COLOR_TYPES = /* @__PURE__ */ new Set([
1083
+ "accent",
1084
+ "white",
1085
+ "black",
1086
+ "selection-stroke",
1087
+ "selection-fill",
1088
+ "laser",
1089
+ "muted-1"
1090
+ ]);
1091
+ var canvasUiColorTypeValidator = T.setEnum(TL_CANVAS_UI_COLOR_TYPES);
1092
+
1093
+ // src/misc/DrScribble.ts
1094
+ var TL_SCRIBBLE_STATES = /* @__PURE__ */ new Set([
1095
+ "starting",
1096
+ "paused",
1097
+ "active",
1098
+ "complete",
1099
+ "stopping"
1100
+ ]);
1101
+ var scribbleValidator = T.object({
1102
+ id: T.string,
1103
+ points: T.arrayOf(vecModelValidator),
1104
+ size: T.positiveNumber,
1105
+ color: canvasUiColorTypeValidator,
1106
+ opacity: T.number,
1107
+ state: T.setEnum(TL_SCRIBBLE_STATES),
1108
+ delay: T.number,
1109
+ shrink: T.number,
1110
+ taper: T.boolean
1111
+ });
1112
+ var pageIdValidator = idValidator("page");
1113
+ var pageValidator = T.model(
1114
+ "page",
1115
+ T.object({
1116
+ typeName: T.literal("page"),
1117
+ id: pageIdValidator,
1118
+ name: T.string,
1119
+ index: T.indexKey,
1120
+ meta: T.jsonValue
1121
+ })
1122
+ );
1123
+ var pageVersions = createMigrationIds("com.draw.page", {
1124
+ AddMeta: 1
1125
+ });
1126
+ var pageMigrations = createRecordMigrationSequence({
1127
+ sequenceId: "com.draw.page",
1128
+ recordType: "page",
1129
+ sequence: [
1130
+ {
1131
+ id: pageVersions.AddMeta,
1132
+ up: (record) => {
1133
+ record.meta = {};
1134
+ }
1135
+ }
1136
+ ]
1137
+ });
1138
+ var PageRecordType = createRecordType("page", {
1139
+ validator: pageValidator,
1140
+ scope: "document"
1141
+ }).withDefaultProperties(() => ({
1142
+ meta: {}
1143
+ }));
1144
+ function isPageId(id) {
1145
+ return PageRecordType.isId(id);
1146
+ }
1147
+
1148
+ // src/records/DrInstance.ts
1149
+ var shouldKeyBePreservedBetweenSessions = {
1150
+ // This object defines keys that should be preserved across calls to loadSnapshot()
1151
+ id: false,
1152
+ // meta
1153
+ typeName: false,
1154
+ // meta
1155
+ currentPageId: false,
1156
+ // does not preserve because who knows if the page still exists
1157
+ opacityForNextShape: false,
1158
+ // does not preserve because it's a temporary state
1159
+ stylesForNextShape: false,
1160
+ // does not preserve because it's a temporary state
1161
+ followingUserId: false,
1162
+ // does not preserve because it's a temporary state
1163
+ highlightedUserIds: false,
1164
+ // does not preserve because it's a temporary state
1165
+ brush: false,
1166
+ // does not preserve because it's a temporary state
1167
+ cursor: false,
1168
+ // does not preserve because it's a temporary state
1169
+ scribbles: false,
1170
+ // does not preserve because it's a temporary state
1171
+ isFocusMode: true,
1172
+ // preserves because it's a user preference
1173
+ isDebugMode: true,
1174
+ // preserves because it's a user preference
1175
+ isToolLocked: true,
1176
+ // preserves because it's a user preference
1177
+ exportBackground: true,
1178
+ // preserves because it's a user preference
1179
+ screenBounds: true,
1180
+ // preserves because it's capturing the user's screen state
1181
+ insets: true,
1182
+ // preserves because it's capturing the user's screen state
1183
+ zoomBrush: false,
1184
+ // does not preserve because it's a temporary state
1185
+ chatMessage: false,
1186
+ // does not preserve because it's a temporary state
1187
+ isChatting: false,
1188
+ // does not preserve because it's a temporary state
1189
+ isPenMode: false,
1190
+ // does not preserve because it's a temporary state
1191
+ isGridMode: true,
1192
+ // preserves because it's a user preference
1193
+ isFocused: true,
1194
+ // preserves because obviously
1195
+ devicePixelRatio: true,
1196
+ // preserves because it captures the user's screen state
1197
+ isCoarsePointer: true,
1198
+ // preserves because it captures the user's screen state
1199
+ isHoveringCanvas: false,
1200
+ // does not preserve because it's a temporary state
1201
+ openMenus: false,
1202
+ // does not preserve because it's a temporary state
1203
+ isChangingStyle: false,
1204
+ // does not preserve because it's a temporary state
1205
+ isReadonly: true,
1206
+ // preserves because it's a config option
1207
+ meta: false,
1208
+ // does not preserve because who knows what's in there, leave it up to sdk users to save and reinstate
1209
+ duplicateProps: false,
1210
+ //
1211
+ cameraState: false
1212
+ // does not preserve because it's a temporary state
1213
+ };
1214
+ function pluckPreservingValues(val) {
1215
+ return val ? filterEntries(val, (key) => {
1216
+ return shouldKeyBePreservedBetweenSessions[key];
1217
+ }) : null;
1218
+ }
1219
+ idValidator("instance");
1220
+ function createInstanceRecordType(stylesById) {
1221
+ const stylesForNextShapeValidators = {};
1222
+ for (const [id, style] of stylesById) {
1223
+ stylesForNextShapeValidators[id] = T.optional(style);
1224
+ }
1225
+ const instanceTypeValidator = T.model(
1226
+ "instance",
1227
+ T.object({
1228
+ typeName: T.literal("instance"),
1229
+ id: idValidator("instance"),
1230
+ currentPageId: pageIdValidator,
1231
+ followingUserId: T.string.nullable(),
1232
+ brush: boxModelValidator.nullable(),
1233
+ opacityForNextShape: opacityValidator,
1234
+ stylesForNextShape: T.object(stylesForNextShapeValidators),
1235
+ cursor: cursorValidator,
1236
+ scribbles: T.arrayOf(scribbleValidator),
1237
+ isFocusMode: T.boolean,
1238
+ isDebugMode: T.boolean,
1239
+ isToolLocked: T.boolean,
1240
+ exportBackground: T.boolean,
1241
+ screenBounds: boxModelValidator,
1242
+ insets: T.arrayOf(T.boolean),
1243
+ zoomBrush: boxModelValidator.nullable(),
1244
+ isPenMode: T.boolean,
1245
+ isGridMode: T.boolean,
1246
+ chatMessage: T.string,
1247
+ isChatting: T.boolean,
1248
+ highlightedUserIds: T.arrayOf(T.string),
1249
+ isFocused: T.boolean,
1250
+ devicePixelRatio: T.number,
1251
+ isCoarsePointer: T.boolean,
1252
+ isHoveringCanvas: T.boolean.nullable(),
1253
+ openMenus: T.arrayOf(T.string),
1254
+ isChangingStyle: T.boolean,
1255
+ isReadonly: T.boolean,
1256
+ meta: T.jsonValue,
1257
+ duplicateProps: T.object({
1258
+ shapeIds: T.arrayOf(idValidator("shape")),
1259
+ offset: T.object({
1260
+ x: T.number,
1261
+ y: T.number
1262
+ })
1263
+ }).nullable(),
1264
+ cameraState: T.literalEnum("idle", "moving")
1265
+ })
1266
+ );
1267
+ return createRecordType("instance", {
1268
+ validator: instanceTypeValidator,
1269
+ scope: "session",
1270
+ ephemeralKeys: {
1271
+ currentPageId: false,
1272
+ meta: false,
1273
+ followingUserId: true,
1274
+ opacityForNextShape: true,
1275
+ stylesForNextShape: true,
1276
+ brush: true,
1277
+ cursor: true,
1278
+ scribbles: true,
1279
+ isFocusMode: true,
1280
+ isDebugMode: true,
1281
+ isToolLocked: true,
1282
+ exportBackground: true,
1283
+ screenBounds: true,
1284
+ insets: true,
1285
+ zoomBrush: true,
1286
+ isPenMode: true,
1287
+ isGridMode: true,
1288
+ chatMessage: true,
1289
+ isChatting: true,
1290
+ highlightedUserIds: true,
1291
+ isFocused: true,
1292
+ devicePixelRatio: true,
1293
+ isCoarsePointer: true,
1294
+ isHoveringCanvas: true,
1295
+ openMenus: true,
1296
+ isChangingStyle: true,
1297
+ isReadonly: true,
1298
+ duplicateProps: true,
1299
+ cameraState: true
1300
+ }
1301
+ }).withDefaultProperties(
1302
+ () => ({
1303
+ followingUserId: null,
1304
+ opacityForNextShape: 1,
1305
+ stylesForNextShape: {},
1306
+ brush: null,
1307
+ scribbles: [],
1308
+ cursor: {
1309
+ type: "default",
1310
+ rotation: 0
1311
+ },
1312
+ isFocusMode: false,
1313
+ exportBackground: false,
1314
+ isDebugMode: false,
1315
+ isToolLocked: false,
1316
+ screenBounds: { x: 0, y: 0, w: 1080, h: 720 },
1317
+ insets: [false, false, false, false],
1318
+ zoomBrush: null,
1319
+ isGridMode: false,
1320
+ isPenMode: false,
1321
+ chatMessage: "",
1322
+ isChatting: false,
1323
+ highlightedUserIds: [],
1324
+ isFocused: false,
1325
+ devicePixelRatio: typeof window === "undefined" ? 1 : window.devicePixelRatio,
1326
+ isCoarsePointer: false,
1327
+ isHoveringCanvas: null,
1328
+ openMenus: [],
1329
+ isChangingStyle: false,
1330
+ isReadonly: false,
1331
+ meta: {},
1332
+ duplicateProps: null,
1333
+ cameraState: "idle"
1334
+ })
1335
+ );
1336
+ }
1337
+ var instanceVersions = createMigrationIds("com.draw.instance", {
1338
+ AddTransparentExportBgs: 1,
1339
+ RemoveDialog: 2,
1340
+ AddToolLockMode: 3,
1341
+ RemoveExtraPropsForNextShape: 4,
1342
+ AddLabelColor: 5,
1343
+ AddFollowingUserId: 6,
1344
+ RemoveAlignJustify: 7,
1345
+ AddZoom: 8,
1346
+ AddVerticalAlign: 9,
1347
+ AddScribbleDelay: 10,
1348
+ RemoveUserId: 11,
1349
+ AddIsPenModeAndIsGridMode: 12,
1350
+ HoistOpacity: 13,
1351
+ AddChat: 14,
1352
+ AddHighlightedUserIds: 15,
1353
+ ReplacePropsForNextShapeWithStylesForNextShape: 16,
1354
+ AddMeta: 17,
1355
+ RemoveCursorColor: 18,
1356
+ AddLonelyProperties: 19,
1357
+ ReadOnlyReadonly: 20,
1358
+ AddHoveringCanvas: 21,
1359
+ AddScribbles: 22,
1360
+ AddInset: 23,
1361
+ AddDuplicateProps: 24,
1362
+ RemoveCanMoveCamera: 25,
1363
+ AddCameraState: 26
1364
+ });
1365
+ var instanceMigrations = createRecordMigrationSequence({
1366
+ sequenceId: "com.draw.instance",
1367
+ recordType: "instance",
1368
+ sequence: [
1369
+ {
1370
+ id: instanceVersions.AddTransparentExportBgs,
1371
+ up: (instance) => {
1372
+ return { ...instance, exportBackground: true };
1373
+ }
1374
+ },
1375
+ {
1376
+ id: instanceVersions.RemoveDialog,
1377
+ up: ({ dialog: _, ...instance }) => {
1378
+ return instance;
1379
+ }
1380
+ },
1381
+ {
1382
+ id: instanceVersions.AddToolLockMode,
1383
+ up: (instance) => {
1384
+ return { ...instance, isToolLocked: false };
1385
+ }
1386
+ },
1387
+ {
1388
+ id: instanceVersions.RemoveExtraPropsForNextShape,
1389
+ up: ({ propsForNextShape, ...instance }) => {
1390
+ return {
1391
+ ...instance,
1392
+ propsForNextShape: Object.fromEntries(
1393
+ Object.entries(propsForNextShape).filter(
1394
+ ([key]) => [
1395
+ "color",
1396
+ "labelColor",
1397
+ "dash",
1398
+ "fill",
1399
+ "size",
1400
+ "font",
1401
+ "align",
1402
+ "verticalAlign",
1403
+ "icon",
1404
+ "geo",
1405
+ "arrowheadStart",
1406
+ "arrowheadEnd",
1407
+ "spline"
1408
+ ].includes(key)
1409
+ )
1410
+ )
1411
+ };
1412
+ }
1413
+ },
1414
+ {
1415
+ id: instanceVersions.AddLabelColor,
1416
+ up: ({ propsForNextShape, ...instance }) => {
1417
+ return {
1418
+ ...instance,
1419
+ propsForNextShape: {
1420
+ ...propsForNextShape,
1421
+ labelColor: "black"
1422
+ }
1423
+ };
1424
+ }
1425
+ },
1426
+ {
1427
+ id: instanceVersions.AddFollowingUserId,
1428
+ up: (instance) => {
1429
+ return { ...instance, followingUserId: null };
1430
+ }
1431
+ },
1432
+ {
1433
+ id: instanceVersions.RemoveAlignJustify,
1434
+ up: (instance) => {
1435
+ let newAlign = instance.propsForNextShape.align;
1436
+ if (newAlign === "justify") {
1437
+ newAlign = "start";
1438
+ }
1439
+ return {
1440
+ ...instance,
1441
+ propsForNextShape: {
1442
+ ...instance.propsForNextShape,
1443
+ align: newAlign
1444
+ }
1445
+ };
1446
+ }
1447
+ },
1448
+ {
1449
+ id: instanceVersions.AddZoom,
1450
+ up: (instance) => {
1451
+ return { ...instance, zoomBrush: null };
1452
+ }
1453
+ },
1454
+ {
1455
+ id: instanceVersions.AddVerticalAlign,
1456
+ up: (instance) => {
1457
+ return {
1458
+ ...instance,
1459
+ propsForNextShape: {
1460
+ ...instance.propsForNextShape,
1461
+ verticalAlign: "middle"
1462
+ }
1463
+ };
1464
+ }
1465
+ },
1466
+ {
1467
+ id: instanceVersions.AddScribbleDelay,
1468
+ up: (instance) => {
1469
+ if (instance.scribble !== null) {
1470
+ return { ...instance, scribble: { ...instance.scribble, delay: 0 } };
1471
+ }
1472
+ return { ...instance };
1473
+ }
1474
+ },
1475
+ {
1476
+ id: instanceVersions.RemoveUserId,
1477
+ up: ({ userId: _, ...instance }) => {
1478
+ return instance;
1479
+ }
1480
+ },
1481
+ {
1482
+ id: instanceVersions.AddIsPenModeAndIsGridMode,
1483
+ up: (instance) => {
1484
+ return { ...instance, isPenMode: false, isGridMode: false };
1485
+ }
1486
+ },
1487
+ {
1488
+ id: instanceVersions.HoistOpacity,
1489
+ up: ({ propsForNextShape: { opacity, ...propsForNextShape }, ...instance }) => {
1490
+ return { ...instance, opacityForNextShape: Number(opacity ?? "1"), propsForNextShape };
1491
+ }
1492
+ },
1493
+ {
1494
+ id: instanceVersions.AddChat,
1495
+ up: (instance) => {
1496
+ return { ...instance, chatMessage: "", isChatting: false };
1497
+ }
1498
+ },
1499
+ {
1500
+ id: instanceVersions.AddHighlightedUserIds,
1501
+ up: (instance) => {
1502
+ return { ...instance, highlightedUserIds: [] };
1503
+ }
1504
+ },
1505
+ {
1506
+ id: instanceVersions.ReplacePropsForNextShapeWithStylesForNextShape,
1507
+ up: ({ propsForNextShape: _, ...instance }) => {
1508
+ return { ...instance, stylesForNextShape: {} };
1509
+ }
1510
+ },
1511
+ {
1512
+ id: instanceVersions.AddMeta,
1513
+ up: (record) => {
1514
+ return {
1515
+ ...record,
1516
+ meta: {}
1517
+ };
1518
+ }
1519
+ },
1520
+ {
1521
+ id: instanceVersions.RemoveCursorColor,
1522
+ up: (record) => {
1523
+ const { color: _, ...cursor } = record.cursor;
1524
+ return {
1525
+ ...record,
1526
+ cursor
1527
+ };
1528
+ }
1529
+ },
1530
+ {
1531
+ id: instanceVersions.AddLonelyProperties,
1532
+ up: (record) => {
1533
+ return {
1534
+ ...record,
1535
+ canMoveCamera: true,
1536
+ isFocused: false,
1537
+ devicePixelRatio: 1,
1538
+ isCoarsePointer: false,
1539
+ openMenus: [],
1540
+ isChangingStyle: false,
1541
+ isReadOnly: false
1542
+ };
1543
+ }
1544
+ },
1545
+ {
1546
+ id: instanceVersions.ReadOnlyReadonly,
1547
+ up: ({ isReadOnly: _isReadOnly, ...record }) => {
1548
+ return {
1549
+ ...record,
1550
+ isReadonly: _isReadOnly
1551
+ };
1552
+ }
1553
+ },
1554
+ {
1555
+ id: instanceVersions.AddHoveringCanvas,
1556
+ up: (record) => {
1557
+ return {
1558
+ ...record,
1559
+ isHoveringCanvas: null
1560
+ };
1561
+ }
1562
+ },
1563
+ {
1564
+ id: instanceVersions.AddScribbles,
1565
+ up: ({ scribble: _, ...record }) => {
1566
+ return {
1567
+ ...record,
1568
+ scribbles: []
1569
+ };
1570
+ }
1571
+ },
1572
+ {
1573
+ id: instanceVersions.AddInset,
1574
+ up: (record) => {
1575
+ return {
1576
+ ...record,
1577
+ insets: [false, false, false, false]
1578
+ };
1579
+ },
1580
+ down: ({ insets: _, ...record }) => {
1581
+ return {
1582
+ ...record
1583
+ };
1584
+ }
1585
+ },
1586
+ {
1587
+ id: instanceVersions.AddDuplicateProps,
1588
+ up: (record) => {
1589
+ return {
1590
+ ...record,
1591
+ duplicateProps: null
1592
+ };
1593
+ },
1594
+ down: ({ duplicateProps: _, ...record }) => {
1595
+ return {
1596
+ ...record
1597
+ };
1598
+ }
1599
+ },
1600
+ {
1601
+ id: instanceVersions.RemoveCanMoveCamera,
1602
+ up: ({ canMoveCamera: _, ...record }) => {
1603
+ return {
1604
+ ...record
1605
+ };
1606
+ },
1607
+ down: (instance) => {
1608
+ return { ...instance, canMoveCamera: true };
1609
+ }
1610
+ },
1611
+ {
1612
+ id: instanceVersions.AddCameraState,
1613
+ up: (record) => {
1614
+ return { ...record, cameraState: "idle" };
1615
+ },
1616
+ down: ({ cameraState: _, ...record }) => {
1617
+ return record;
1618
+ }
1619
+ }
1620
+ ]
1621
+ });
1622
+ var DrINSTANCE_ID = "instance:instance";
1623
+ var instancePageStateValidator = T.model(
1624
+ "instance_page_state",
1625
+ T.object({
1626
+ typeName: T.literal("instance_page_state"),
1627
+ id: idValidator("instance_page_state"),
1628
+ pageId: pageIdValidator,
1629
+ selectedShapeIds: T.arrayOf(shapeIdValidator),
1630
+ hintingShapeIds: T.arrayOf(shapeIdValidator),
1631
+ erasingShapeIds: T.arrayOf(shapeIdValidator),
1632
+ hoveredShapeId: shapeIdValidator.nullable(),
1633
+ editingShapeId: shapeIdValidator.nullable(),
1634
+ croppingShapeId: shapeIdValidator.nullable(),
1635
+ focusedGroupId: shapeIdValidator.nullable(),
1636
+ meta: T.jsonValue
1637
+ })
1638
+ );
1639
+ var instancePageStateVersions = createMigrationIds("com.draw.instance_page_state", {
1640
+ AddCroppingId: 1,
1641
+ RemoveInstanceIdAndCameraId: 2,
1642
+ AddMeta: 3,
1643
+ RenameProperties: 4,
1644
+ RenamePropertiesAgain: 5
1645
+ });
1646
+ var instancePageStateMigrations = createRecordMigrationSequence({
1647
+ sequenceId: "com.draw.instance_page_state",
1648
+ recordType: "instance_page_state",
1649
+ sequence: [
1650
+ {
1651
+ id: instancePageStateVersions.AddCroppingId,
1652
+ up(instance) {
1653
+ instance.croppingShapeId = null;
1654
+ }
1655
+ },
1656
+ {
1657
+ id: instancePageStateVersions.RemoveInstanceIdAndCameraId,
1658
+ up(instance) {
1659
+ delete instance.instanceId;
1660
+ delete instance.cameraId;
1661
+ }
1662
+ },
1663
+ {
1664
+ id: instancePageStateVersions.AddMeta,
1665
+ up: (record) => {
1666
+ record.meta = {};
1667
+ }
1668
+ },
1669
+ {
1670
+ id: instancePageStateVersions.RenameProperties,
1671
+ // this migration is cursed: it was written wrong and doesn't do anything.
1672
+ // rather than replace it, I've added another migration below that fixes it.
1673
+ up: (_record) => {
1674
+ },
1675
+ down: (_record) => {
1676
+ }
1677
+ },
1678
+ {
1679
+ id: instancePageStateVersions.RenamePropertiesAgain,
1680
+ up: (record) => {
1681
+ record.selectedShapeIds = record.selectedIds;
1682
+ delete record.selectedIds;
1683
+ record.hintingShapeIds = record.hintingIds;
1684
+ delete record.hintingIds;
1685
+ record.erasingShapeIds = record.erasingIds;
1686
+ delete record.erasingIds;
1687
+ record.hoveredShapeId = record.hoveredId;
1688
+ delete record.hoveredId;
1689
+ record.editingShapeId = record.editingId;
1690
+ delete record.editingId;
1691
+ record.croppingShapeId = record.croppingShapeId ?? record.croppingId ?? null;
1692
+ delete record.croppingId;
1693
+ record.focusedGroupId = record.focusLayerId;
1694
+ delete record.focusLayerId;
1695
+ },
1696
+ down: (record) => {
1697
+ record.selectedIds = record.selectedShapeIds;
1698
+ delete record.selectedShapeIds;
1699
+ record.hintingIds = record.hintingShapeIds;
1700
+ delete record.hintingShapeIds;
1701
+ record.erasingIds = record.erasingShapeIds;
1702
+ delete record.erasingShapeIds;
1703
+ record.hoveredId = record.hoveredShapeId;
1704
+ delete record.hoveredShapeId;
1705
+ record.editingId = record.editingShapeId;
1706
+ delete record.editingShapeId;
1707
+ record.croppingId = record.croppingShapeId;
1708
+ delete record.croppingShapeId;
1709
+ record.focusLayerId = record.focusedGroupId;
1710
+ delete record.focusedGroupId;
1711
+ }
1712
+ }
1713
+ ]
1714
+ });
1715
+ var InstancePageStateRecordType = createRecordType(
1716
+ "instance_page_state",
1717
+ {
1718
+ validator: instancePageStateValidator,
1719
+ scope: "session",
1720
+ ephemeralKeys: {
1721
+ pageId: false,
1722
+ selectedShapeIds: false,
1723
+ editingShapeId: false,
1724
+ croppingShapeId: false,
1725
+ meta: false,
1726
+ hintingShapeIds: true,
1727
+ erasingShapeIds: true,
1728
+ hoveredShapeId: true,
1729
+ focusedGroupId: true
1730
+ }
1731
+ }
1732
+ ).withDefaultProperties(
1733
+ () => ({
1734
+ editingShapeId: null,
1735
+ croppingShapeId: null,
1736
+ selectedShapeIds: [],
1737
+ hoveredShapeId: null,
1738
+ erasingShapeIds: [],
1739
+ hintingShapeIds: [],
1740
+ focusedGroupId: null,
1741
+ meta: {}
1742
+ })
1743
+ );
1744
+ var pointerValidator = T.model(
1745
+ "pointer",
1746
+ T.object({
1747
+ typeName: T.literal("pointer"),
1748
+ id: idValidator("pointer"),
1749
+ x: T.number,
1750
+ y: T.number,
1751
+ lastActivityTimestamp: T.number,
1752
+ meta: T.jsonValue
1753
+ })
1754
+ );
1755
+ var pointerVersions = createMigrationIds("com.draw.pointer", {
1756
+ AddMeta: 1
1757
+ });
1758
+ var pointerMigrations = createRecordMigrationSequence({
1759
+ sequenceId: "com.draw.pointer",
1760
+ recordType: "pointer",
1761
+ sequence: [
1762
+ {
1763
+ id: pointerVersions.AddMeta,
1764
+ up: (record) => {
1765
+ record.meta = {};
1766
+ }
1767
+ }
1768
+ ]
1769
+ });
1770
+ var PointerRecordType = createRecordType("pointer", {
1771
+ validator: pointerValidator,
1772
+ scope: "session"
1773
+ }).withDefaultProperties(
1774
+ () => ({
1775
+ x: 0,
1776
+ y: 0,
1777
+ lastActivityTimestamp: 0,
1778
+ meta: {}
1779
+ })
1780
+ );
1781
+ var DrPOINTER_ID = PointerRecordType.createId("pointer");
1782
+ var instancePresenceValidator = T.model(
1783
+ "instance_presence",
1784
+ T.object({
1785
+ typeName: T.literal("instance_presence"),
1786
+ id: idValidator("instance_presence"),
1787
+ userId: T.string,
1788
+ userName: T.string,
1789
+ lastActivityTimestamp: T.number.nullable(),
1790
+ followingUserId: T.string.nullable(),
1791
+ cursor: T.object({
1792
+ x: T.number,
1793
+ y: T.number,
1794
+ type: cursorTypeValidator,
1795
+ rotation: T.number
1796
+ }).nullable(),
1797
+ color: T.string,
1798
+ camera: T.object({
1799
+ x: T.number,
1800
+ y: T.number,
1801
+ z: T.number
1802
+ }).nullable(),
1803
+ screenBounds: boxModelValidator.nullable(),
1804
+ selectedShapeIds: T.arrayOf(idValidator("shape")),
1805
+ currentPageId: idValidator("page"),
1806
+ brush: boxModelValidator.nullable(),
1807
+ scribbles: T.arrayOf(scribbleValidator),
1808
+ chatMessage: T.string,
1809
+ meta: T.jsonValue
1810
+ })
1811
+ );
1812
+ var instancePresenceVersions = createMigrationIds("com.draw.instance_presence", {
1813
+ AddScribbleDelay: 1,
1814
+ RemoveInstanceId: 2,
1815
+ AddChatMessage: 3,
1816
+ AddMeta: 4,
1817
+ RenameSelectedShapeIds: 5,
1818
+ NullableCameraCursor: 6
1819
+ });
1820
+ var instancePresenceMigrations = createRecordMigrationSequence({
1821
+ sequenceId: "com.draw.instance_presence",
1822
+ recordType: "instance_presence",
1823
+ sequence: [
1824
+ {
1825
+ id: instancePresenceVersions.AddScribbleDelay,
1826
+ up: (instance) => {
1827
+ if (instance.scribble !== null) {
1828
+ instance.scribble.delay = 0;
1829
+ }
1830
+ }
1831
+ },
1832
+ {
1833
+ id: instancePresenceVersions.RemoveInstanceId,
1834
+ up: (instance) => {
1835
+ delete instance.instanceId;
1836
+ }
1837
+ },
1838
+ {
1839
+ id: instancePresenceVersions.AddChatMessage,
1840
+ up: (instance) => {
1841
+ instance.chatMessage = "";
1842
+ }
1843
+ },
1844
+ {
1845
+ id: instancePresenceVersions.AddMeta,
1846
+ up: (record) => {
1847
+ record.meta = {};
1848
+ }
1849
+ },
1850
+ {
1851
+ id: instancePresenceVersions.RenameSelectedShapeIds,
1852
+ up: (_record) => {
1853
+ }
1854
+ },
1855
+ {
1856
+ id: instancePresenceVersions.NullableCameraCursor,
1857
+ up: (_record) => {
1858
+ },
1859
+ down: (record) => {
1860
+ if (record.camera === null) {
1861
+ record.camera = { x: 0, y: 0, z: 1 };
1862
+ }
1863
+ if (record.lastActivityTimestamp === null) {
1864
+ record.lastActivityTimestamp = 0;
1865
+ }
1866
+ if (record.cursor === null) {
1867
+ record.cursor = { type: "default", x: 0, y: 0, rotation: 0 };
1868
+ }
1869
+ if (record.screenBounds === null) {
1870
+ record.screenBounds = { x: 0, y: 0, w: 1, h: 1 };
1871
+ }
1872
+ }
1873
+ }
1874
+ ]
1875
+ });
1876
+ var InstancePresenceRecordType = createRecordType(
1877
+ "instance_presence",
1878
+ {
1879
+ validator: instancePresenceValidator,
1880
+ scope: "presence"
1881
+ }
1882
+ ).withDefaultProperties(() => ({
1883
+ lastActivityTimestamp: null,
1884
+ followingUserId: null,
1885
+ color: "#FF0000",
1886
+ camera: null,
1887
+ cursor: null,
1888
+ screenBounds: null,
1889
+ selectedShapeIds: [],
1890
+ brush: null,
1891
+ scribbles: [],
1892
+ chatMessage: "",
1893
+ meta: {}
1894
+ }));
1895
+
1896
+ // src/createPresenceStateDerivation.ts
1897
+ function createPresenceStateDerivation($user, opts) {
1898
+ const { instanceId, getUserPresence: _getUserPresence } = opts ?? {};
1899
+ const getUserPresence = _getUserPresence ?? getDefaultUserPresence;
1900
+ return (store) => {
1901
+ return computed("instancePresence", () => {
1902
+ const user = $user.get();
1903
+ if (!user) return null;
1904
+ const state = getUserPresence(store, user);
1905
+ if (!state) return null;
1906
+ return InstancePresenceRecordType.create({
1907
+ ...state,
1908
+ id: instanceId ?? InstancePresenceRecordType.createId(store.id)
1909
+ });
1910
+ });
1911
+ };
1912
+ }
1913
+ function getDefaultUserPresence(store, user) {
1914
+ const instance = store.get(DrINSTANCE_ID);
1915
+ const pageState = store.get(InstancePageStateRecordType.createId(instance?.currentPageId));
1916
+ const camera = store.get(CameraRecordType.createId(instance?.currentPageId));
1917
+ const pointer = store.get(DrPOINTER_ID);
1918
+ if (!pageState || !instance || !camera || !pointer) {
1919
+ return null;
1920
+ }
1921
+ return {
1922
+ selectedShapeIds: pageState.selectedShapeIds,
1923
+ brush: instance.brush,
1924
+ scribbles: instance.scribbles,
1925
+ userId: user.id,
1926
+ userName: user.name,
1927
+ followingUserId: instance.followingUserId,
1928
+ camera: {
1929
+ x: camera.x,
1930
+ y: camera.y,
1931
+ z: camera.z
1932
+ },
1933
+ color: user.color || "#FF0000",
1934
+ currentPageId: instance.currentPageId,
1935
+ cursor: {
1936
+ x: pointer.x,
1937
+ y: pointer.y,
1938
+ rotation: instance.cursor.rotation,
1939
+ type: instance.cursor.type
1940
+ },
1941
+ lastActivityTimestamp: pointer.lastActivityTimestamp,
1942
+ screenBounds: instance.screenBounds,
1943
+ chatMessage: instance.chatMessage,
1944
+ meta: {}
1945
+ };
1946
+ }
1947
+ var assetVersions = createMigrationIds("com.draw.asset", {
1948
+ AddMeta: 1
1949
+ });
1950
+ var assetMigrations = createRecordMigrationSequence({
1951
+ sequenceId: "com.draw.asset",
1952
+ recordType: "asset",
1953
+ sequence: [
1954
+ {
1955
+ id: assetVersions.AddMeta,
1956
+ up: (record) => {
1957
+ record.meta = {};
1958
+ }
1959
+ }
1960
+ ]
1961
+ });
1962
+ function createAssetRecordType(assets) {
1963
+ return createRecordType("asset", {
1964
+ scope: "document",
1965
+ validator: T.model(
1966
+ "asset",
1967
+ T.union(
1968
+ "type",
1969
+ mapObjectMapValues(
1970
+ assets,
1971
+ (type, { props, meta }) => createAssetValidator(type, props, meta)
1972
+ )
1973
+ )
1974
+ )
1975
+ }).withDefaultProperties(() => ({
1976
+ meta: {}
1977
+ }));
1978
+ }
1979
+ var AssetRecordType = createRecordType("asset", {
1980
+ scope: "document"
1981
+ }).withDefaultProperties(() => ({
1982
+ meta: {}
1983
+ }));
1984
+ function createAssetPropsMigrationSequence(migrations) {
1985
+ return migrations;
1986
+ }
1987
+ function createAssetPropsMigrationIds(assetType, ids) {
1988
+ return mapObjectMapValues(ids, (_k, v) => `com.draw.asset.${assetType}/${v}`);
1989
+ }
1990
+ function createCustomRecordType(typeName, config) {
1991
+ return createRecordType(typeName, {
1992
+ scope: config.scope,
1993
+ validator: config.validator
1994
+ }).withDefaultProperties(config.createDefaultProperties ?? (() => ({})));
1995
+ }
1996
+ function processCustomRecordMigrations(records) {
1997
+ const result = [];
1998
+ for (const [typeName, config] of Object.entries(records)) {
1999
+ const sequenceId = `com.draw.${typeName}`;
2000
+ const { migrations } = config;
2001
+ if (!migrations) {
2002
+ result.push(
2003
+ createMigrationSequence({
2004
+ sequenceId,
2005
+ retroactive: true,
2006
+ sequence: []
2007
+ })
2008
+ );
2009
+ } else if ("sequenceId" in migrations) {
2010
+ assert(
2011
+ sequenceId === migrations.sequenceId,
2012
+ `sequenceId mismatch for ${typeName} custom record migrations. Expected '${sequenceId}', got '${migrations.sequenceId}'`
2013
+ );
2014
+ result.push(migrations);
2015
+ } else if ("sequence" in migrations) {
2016
+ result.push(
2017
+ createMigrationSequence({
2018
+ sequenceId,
2019
+ retroactive: true,
2020
+ sequence: migrations.sequence.map((m) => {
2021
+ if (!("id" in m)) return m;
2022
+ return {
2023
+ id: m.id,
2024
+ dependsOn: m.dependsOn,
2025
+ scope: "record",
2026
+ filter: (r) => r.typeName === typeName,
2027
+ up: (record) => {
2028
+ const result2 = m.up(record);
2029
+ if (result2) return result2;
2030
+ },
2031
+ down: typeof m.down === "function" ? (record) => {
2032
+ const result2 = m.down(record);
2033
+ if (result2) return result2;
2034
+ } : void 0
2035
+ };
2036
+ })
2037
+ })
2038
+ );
2039
+ }
2040
+ }
2041
+ return result;
2042
+ }
2043
+ function createCustomRecordMigrationIds(recordType, ids) {
2044
+ return mapObjectMapValues(ids, (_k, v) => `com.draw.${recordType}/${v}`);
2045
+ }
2046
+ function createCustomRecordMigrationSequence(migrations) {
2047
+ return migrations;
2048
+ }
2049
+ function createCustomRecordId(typeName, id) {
2050
+ return `${typeName}:${id ?? uniqueId()}`;
2051
+ }
2052
+ function isCustomRecordId(typeName, id) {
2053
+ if (!id) return false;
2054
+ return id.startsWith(`${typeName}:`);
2055
+ }
2056
+ function isCustomRecord(typeName, record) {
2057
+ if (!record) return false;
2058
+ return record.typeName === typeName;
2059
+ }
2060
+ var documentValidator = T.model(
2061
+ "document",
2062
+ T.object({
2063
+ typeName: T.literal("document"),
2064
+ id: T.literal("document:document"),
2065
+ gridSize: T.number,
2066
+ name: T.string,
2067
+ meta: T.jsonValue
2068
+ })
2069
+ );
2070
+ function isDocument(record) {
2071
+ if (!record) return false;
2072
+ return record.typeName === "document";
2073
+ }
2074
+ var documentVersions = createMigrationIds("com.draw.document", {
2075
+ AddName: 1,
2076
+ AddMeta: 2
2077
+ });
2078
+ var documentMigrations = createRecordMigrationSequence({
2079
+ sequenceId: "com.draw.document",
2080
+ recordType: "document",
2081
+ sequence: [
2082
+ {
2083
+ id: documentVersions.AddName,
2084
+ up: (document) => {
2085
+ document.name = "";
2086
+ },
2087
+ down: (document) => {
2088
+ delete document.name;
2089
+ }
2090
+ },
2091
+ {
2092
+ id: documentVersions.AddMeta,
2093
+ up: (record) => {
2094
+ record.meta = {};
2095
+ }
2096
+ }
2097
+ ]
2098
+ });
2099
+ var DocumentRecordType = createRecordType("document", {
2100
+ validator: documentValidator,
2101
+ scope: "document"
2102
+ }).withDefaultProperties(
2103
+ () => ({
2104
+ gridSize: 10,
2105
+ name: "",
2106
+ meta: {}
2107
+ })
2108
+ );
2109
+ var DrDOCUMENT_ID = DocumentRecordType.createId("document");
2110
+ var userIdValidator = idValidator("user");
2111
+ var userVersions = createMigrationIds("com.draw.user", {
2112
+ Initial: 1
2113
+ });
2114
+ var userMigrations = createRecordMigrationSequence({
2115
+ sequenceId: "com.draw.user",
2116
+ recordType: "user",
2117
+ sequence: [
2118
+ {
2119
+ id: userVersions.Initial,
2120
+ up: (_record) => {
2121
+ }
2122
+ }
2123
+ ]
2124
+ });
2125
+ function createUserRecordType(config) {
2126
+ const metaConfig = config?.meta;
2127
+ const metaValidator = metaConfig ? T.object(
2128
+ Object.fromEntries(
2129
+ Object.entries(metaConfig).map(([key, v]) => [
2130
+ key,
2131
+ new T.Validator((value) => {
2132
+ if (value === void 0) return void 0;
2133
+ return v.validate(value);
2134
+ })
2135
+ ])
2136
+ )
2137
+ ).allowUnknownProperties() : T.jsonValue;
2138
+ const validator = T.model(
2139
+ "user",
2140
+ T.object({
2141
+ typeName: T.literal("user"),
2142
+ id: userIdValidator,
2143
+ name: T.string,
2144
+ color: T.string,
2145
+ imageUrl: T.string,
2146
+ meta: metaValidator
2147
+ })
2148
+ );
2149
+ return createRecordType("user", {
2150
+ validator,
2151
+ scope: "document"
2152
+ }).withDefaultProperties(() => ({
2153
+ name: "",
2154
+ color: "",
2155
+ imageUrl: "",
2156
+ meta: {}
2157
+ }));
2158
+ }
2159
+ T.model(
2160
+ "user",
2161
+ T.object({
2162
+ typeName: T.literal("user"),
2163
+ id: userIdValidator,
2164
+ name: T.string,
2165
+ color: T.string,
2166
+ imageUrl: T.string,
2167
+ meta: T.jsonValue
2168
+ })
2169
+ );
2170
+ var UserRecordType = createUserRecordType();
2171
+ function isUserId(id) {
2172
+ return UserRecordType.isId(id);
2173
+ }
2174
+ function createUserId(id) {
2175
+ return UserRecordType.createId(id);
2176
+ }
2177
+ var bookmarkShapeProps = {
2178
+ w: T.nonZeroNumber,
2179
+ h: T.nonZeroNumber,
2180
+ assetId: assetIdValidator.nullable(),
2181
+ url: T.linkUrl
2182
+ };
2183
+ var Versions4 = createShapePropsMigrationIds("bookmark", {
2184
+ NullAssetId: 1,
2185
+ MakeUrlsValid: 2
2186
+ });
2187
+ var bookmarkShapeMigrations = createShapePropsMigrationSequence({
2188
+ sequence: [
2189
+ {
2190
+ id: Versions4.NullAssetId,
2191
+ up: (props) => {
2192
+ if (props.assetId === void 0) {
2193
+ props.assetId = null;
2194
+ }
2195
+ },
2196
+ down: "retired"
2197
+ },
2198
+ {
2199
+ id: Versions4.MakeUrlsValid,
2200
+ up: (props) => {
2201
+ if (!T.linkUrl.isValid(props.url)) {
2202
+ props.url = "";
2203
+ }
2204
+ },
2205
+ down: (_props) => {
2206
+ }
2207
+ }
2208
+ ]
2209
+ });
2210
+ var FIRST_POINT_B64_LENGTH = 16;
2211
+ var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2212
+ var B64_LOOKUP = new Uint8Array(128);
2213
+ for (let i = 0; i < 64; i++) {
2214
+ B64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i;
2215
+ }
2216
+ var POW2 = new Float64Array(31);
2217
+ for (let i = 0; i < 31; i++) {
2218
+ POW2[i] = Math.pow(2, i - 15);
2219
+ }
2220
+ var POW2_SUBNORMAL = Math.pow(2, -14) / 1024;
2221
+ var MANTISSA = new Float64Array(1024);
2222
+ for (let i = 0; i < 1024; i++) {
2223
+ MANTISSA[i] = 1 + i / 1024;
2224
+ }
2225
+ function nativeGetFloat16(dataView, offset) {
2226
+ return dataView.getFloat16(offset, true);
2227
+ }
2228
+ function fallbackGetFloat16(dataView, offset) {
2229
+ return float16BitsToNumber(dataView.getUint16(offset, true));
2230
+ }
2231
+ var getFloat16 = typeof DataView.prototype.getFloat16 === "function" ? nativeGetFloat16 : fallbackGetFloat16;
2232
+ function nativeSetFloat16(dataView, offset, value) {
2233
+ dataView.setFloat16(offset, value, true);
2234
+ }
2235
+ function fallbackSetFloat16(dataView, offset, value) {
2236
+ dataView.setUint16(offset, numberToFloat16Bits(value), true);
2237
+ }
2238
+ var setFloat16 = typeof DataView.prototype.setFloat16 === "function" ? nativeSetFloat16 : fallbackSetFloat16;
2239
+ function nativeBase64ToUint8Array(base64) {
2240
+ return Uint8Array.fromBase64(base64);
2241
+ }
2242
+ function fallbackBase64ToUint8Array(base64) {
2243
+ const numBytes = Math.floor(base64.length * 3 / 4);
2244
+ const bytes = new Uint8Array(numBytes);
2245
+ let byteIndex = 0;
2246
+ for (let i = 0; i < base64.length; i += 4) {
2247
+ const c0 = B64_LOOKUP[base64.charCodeAt(i)];
2248
+ const c1 = B64_LOOKUP[base64.charCodeAt(i + 1)];
2249
+ const c2 = B64_LOOKUP[base64.charCodeAt(i + 2)];
2250
+ const c3 = B64_LOOKUP[base64.charCodeAt(i + 3)];
2251
+ const bitmap = c0 << 18 | c1 << 12 | c2 << 6 | c3;
2252
+ bytes[byteIndex++] = bitmap >> 16 & 255;
2253
+ bytes[byteIndex++] = bitmap >> 8 & 255;
2254
+ bytes[byteIndex++] = bitmap & 255;
2255
+ }
2256
+ return bytes;
2257
+ }
2258
+ function nativeUint8ArrayToBase64(uint8Array) {
2259
+ return uint8Array.toBase64();
2260
+ }
2261
+ function fallbackUint8ArrayToBase64(uint8Array) {
2262
+ assert(uint8Array.length % 3 === 0, "Uint8Array length must be a multiple of 3");
2263
+ let result = "";
2264
+ for (let i = 0; i < uint8Array.length; i += 3) {
2265
+ const byte1 = uint8Array[i];
2266
+ const byte2 = uint8Array[i + 1];
2267
+ const byte3 = uint8Array[i + 2];
2268
+ const bitmap = byte1 << 16 | byte2 << 8 | byte3;
2269
+ result += BASE64_CHARS[bitmap >> 18 & 63] + BASE64_CHARS[bitmap >> 12 & 63] + BASE64_CHARS[bitmap >> 6 & 63] + BASE64_CHARS[bitmap & 63];
2270
+ }
2271
+ return result;
2272
+ }
2273
+ var uint8ArrayToBase64 = typeof Uint8Array.prototype.toBase64 === "function" ? nativeUint8ArrayToBase64 : fallbackUint8ArrayToBase64;
2274
+ var base64ToUint8Array = typeof Uint8Array.fromBase64 === "function" ? nativeBase64ToUint8Array : fallbackBase64ToUint8Array;
2275
+ function float16BitsToNumber(bits) {
2276
+ const sign = bits >> 15;
2277
+ const exp = bits >> 10 & 31;
2278
+ const frac = bits & 1023;
2279
+ if (exp === 0) {
2280
+ return sign ? -frac * POW2_SUBNORMAL : frac * POW2_SUBNORMAL;
2281
+ }
2282
+ if (exp === 31) {
2283
+ return frac ? NaN : sign ? -Infinity : Infinity;
2284
+ }
2285
+ const magnitude = POW2[exp] * MANTISSA[frac];
2286
+ return sign ? -magnitude : magnitude;
2287
+ }
2288
+ function numberToFloat16Bits(value) {
2289
+ if (value === 0) return Object.is(value, -0) ? 32768 : 0;
2290
+ if (!Number.isFinite(value)) {
2291
+ if (Number.isNaN(value)) return 32256;
2292
+ return value > 0 ? 31744 : 64512;
2293
+ }
2294
+ const sign = value < 0 ? 1 : 0;
2295
+ value = Math.abs(value);
2296
+ const exp = Math.floor(Math.log2(value));
2297
+ let expBiased = exp + 15;
2298
+ if (expBiased >= 31) {
2299
+ return sign << 15 | 31744;
2300
+ }
2301
+ if (expBiased <= 0) {
2302
+ const frac2 = Math.round(value * Math.pow(2, 14) * 1024);
2303
+ return sign << 15 | frac2 & 1023;
2304
+ }
2305
+ const mantissa = value / Math.pow(2, exp) - 1;
2306
+ let frac = Math.round(mantissa * 1024);
2307
+ if (frac >= 1024) {
2308
+ frac = 0;
2309
+ expBiased++;
2310
+ if (expBiased >= 31) {
2311
+ return sign << 15 | 31744;
2312
+ }
2313
+ }
2314
+ return sign << 15 | expBiased << 10 | frac;
2315
+ }
2316
+ var b64Vecs = class {
2317
+ /**
2318
+ * Encode a single point (x, y, z) to 8 base64 characters using legacy Float16 encoding.
2319
+ * Each coordinate is encoded as a Float16 value, resulting in 6 bytes total.
2320
+ *
2321
+ * @param x - The x coordinate
2322
+ * @param y - The y coordinate
2323
+ * @param z - The z coordinate
2324
+ * @returns An 8-character base64 string representing the point
2325
+ * @internal
2326
+ */
2327
+ static _legacyEncodePoint(x, y, z) {
2328
+ const buffer = new Uint8Array(6);
2329
+ const dataView = new DataView(buffer.buffer);
2330
+ setFloat16(dataView, 0, x);
2331
+ setFloat16(dataView, 2, y);
2332
+ setFloat16(dataView, 4, z);
2333
+ return uint8ArrayToBase64(buffer);
2334
+ }
2335
+ /**
2336
+ * Convert an array of VecModels to a base64 string using legacy Float16 encoding.
2337
+ * Uses Float16 encoding for each coordinate (x, y, z). If a point's z value is
2338
+ * undefined, it defaults to 0.5.
2339
+ *
2340
+ * @param points - An array of VecModel objects to encode
2341
+ * @returns A base64-encoded string containing all points
2342
+ * @internal Used only for migrations from legacy format
2343
+ */
2344
+ static _legacyEncodePoints(points) {
2345
+ if (points.length === 0) return "";
2346
+ const buffer = new Uint8Array(points.length * 6);
2347
+ const dataView = new DataView(buffer.buffer);
2348
+ for (let i = 0; i < points.length; i++) {
2349
+ const p = points[i];
2350
+ const offset = i * 6;
2351
+ setFloat16(dataView, offset, p.x);
2352
+ setFloat16(dataView, offset + 2, p.y);
2353
+ setFloat16(dataView, offset + 4, p.z ?? 0.5);
2354
+ }
2355
+ return uint8ArrayToBase64(buffer);
2356
+ }
2357
+ /**
2358
+ * Convert a legacy base64 string back to an array of VecModels.
2359
+ * Decodes Float16-encoded coordinates (x, y, z) from the base64 string.
2360
+ *
2361
+ * @param base64 - The base64-encoded string containing point data
2362
+ * @returns An array of VecModel objects decoded from the string
2363
+ * @internal Used only for migrations from legacy format
2364
+ */
2365
+ static _legacyDecodePoints(base64) {
2366
+ const bytes = base64ToUint8Array(base64);
2367
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
2368
+ const result = [];
2369
+ for (let offset = 0; offset < bytes.length; offset += 6) {
2370
+ result.push({
2371
+ x: getFloat16(dataView, offset),
2372
+ y: getFloat16(dataView, offset + 2),
2373
+ z: getFloat16(dataView, offset + 4)
2374
+ });
2375
+ }
2376
+ return result;
2377
+ }
2378
+ /**
2379
+ * Encode an array of VecModels using delta encoding for improved precision.
2380
+ * The first point is stored as Float32 (high precision for absolute position),
2381
+ * subsequent points are stored as Float16 deltas from the previous point.
2382
+ * This provides full precision for the starting position and excellent precision
2383
+ * for deltas between consecutive points (which are typically small values).
2384
+ *
2385
+ * Format:
2386
+ * - First point: 3 Float32 values = 12 bytes = 16 base64 chars
2387
+ * - Delta points: 3 Float16 values each = 6 bytes = 8 base64 chars each
2388
+ *
2389
+ * @param points - An array of VecModel objects to encode
2390
+ * @returns A base64-encoded string containing delta-encoded points
2391
+ * @public
2392
+ */
2393
+ static encodePoints(points) {
2394
+ if (points.length === 0) return "";
2395
+ const firstPointBytes = 12;
2396
+ const deltaBytes = (points.length - 1) * 6;
2397
+ const totalBytes = firstPointBytes + deltaBytes;
2398
+ const buffer = new Uint8Array(totalBytes);
2399
+ const dataView = new DataView(buffer.buffer);
2400
+ const first = points[0];
2401
+ dataView.setFloat32(0, first.x, true);
2402
+ dataView.setFloat32(4, first.y, true);
2403
+ dataView.setFloat32(8, first.z ?? 0.5, true);
2404
+ let prevX = first.x;
2405
+ let prevY = first.y;
2406
+ let prevZ = first.z ?? 0.5;
2407
+ for (let i = 1; i < points.length; i++) {
2408
+ const p = points[i];
2409
+ const z = p.z ?? 0.5;
2410
+ const offset = firstPointBytes + (i - 1) * 6;
2411
+ setFloat16(dataView, offset, p.x - prevX);
2412
+ setFloat16(dataView, offset + 2, p.y - prevY);
2413
+ setFloat16(dataView, offset + 4, z - prevZ);
2414
+ prevX = p.x;
2415
+ prevY = p.y;
2416
+ prevZ = z;
2417
+ }
2418
+ return uint8ArrayToBase64(buffer);
2419
+ }
2420
+ /**
2421
+ * Decode a delta-encoded base64 string back to an array of absolute VecModels.
2422
+ * The first point is stored as Float32 (high precision), subsequent points are
2423
+ * Float16 deltas that are accumulated to reconstruct absolute positions.
2424
+ *
2425
+ * @param base64 - The base64-encoded string containing delta-encoded point data
2426
+ * @returns An array of VecModel objects with absolute coordinates
2427
+ * @public
2428
+ */
2429
+ static decodePoints(base64) {
2430
+ if (base64.length === 0) return [];
2431
+ const bytes = base64ToUint8Array(base64);
2432
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
2433
+ const result = [];
2434
+ let x = dataView.getFloat32(0, true);
2435
+ let y = dataView.getFloat32(4, true);
2436
+ let z = dataView.getFloat32(8, true);
2437
+ result.push({ x, y, z });
2438
+ const firstPointBytes = 12;
2439
+ for (let offset = firstPointBytes; offset < bytes.length; offset += 6) {
2440
+ x += getFloat16(dataView, offset);
2441
+ y += getFloat16(dataView, offset + 2);
2442
+ z += getFloat16(dataView, offset + 4);
2443
+ result.push({ x, y, z });
2444
+ }
2445
+ return result;
2446
+ }
2447
+ /**
2448
+ * Get the first point from a delta-encoded base64 string.
2449
+ * The first point is stored as Float32 for full precision.
2450
+ *
2451
+ * @param b64Points - The delta-encoded base64 string
2452
+ * @returns The first point as a VecModel, or null if the string is too short
2453
+ * @public
2454
+ */
2455
+ static decodeFirstPoint(b64Points) {
2456
+ if (b64Points.length < FIRST_POINT_B64_LENGTH) return null;
2457
+ const bytes = base64ToUint8Array(b64Points.slice(0, FIRST_POINT_B64_LENGTH));
2458
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
2459
+ return {
2460
+ x: dataView.getFloat32(0, true),
2461
+ y: dataView.getFloat32(4, true),
2462
+ z: dataView.getFloat32(8, true)
2463
+ };
2464
+ }
2465
+ /**
2466
+ * Get the last point from a delta-encoded base64 string.
2467
+ * Requires decoding all points to accumulate deltas.
2468
+ *
2469
+ * @param b64Points - The delta-encoded base64 string
2470
+ * @returns The last point as a VecModel, or null if the string is too short
2471
+ * @public
2472
+ */
2473
+ static decodeLastPoint(b64Points) {
2474
+ if (b64Points.length < FIRST_POINT_B64_LENGTH) return null;
2475
+ const bytes = base64ToUint8Array(b64Points);
2476
+ const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
2477
+ let x = dataView.getFloat32(0, true);
2478
+ let y = dataView.getFloat32(4, true);
2479
+ let z = dataView.getFloat32(8, true);
2480
+ const firstPointBytes = 12;
2481
+ for (let offset = firstPointBytes; offset < bytes.length; offset += 6) {
2482
+ x += getFloat16(dataView, offset);
2483
+ y += getFloat16(dataView, offset + 2);
2484
+ z += getFloat16(dataView, offset + 4);
2485
+ }
2486
+ return { x, y, z };
2487
+ }
2488
+ };
2489
+
2490
+ // src/shapes/DrDrawShape.ts
2491
+ var DrawShapeSegment = T.object({
2492
+ type: T.literalEnum("free", "straight"),
2493
+ path: T.string
2494
+ });
2495
+ var drawShapeProps = {
2496
+ color: DefaultColorStyle,
2497
+ fill: DefaultFillStyle,
2498
+ dash: DefaultDashStyle,
2499
+ size: DefaultSizeStyle,
2500
+ segments: T.arrayOf(DrawShapeSegment),
2501
+ isComplete: T.boolean,
2502
+ isClosed: T.boolean,
2503
+ isPen: T.boolean,
2504
+ scale: T.nonZeroNumber,
2505
+ scaleX: T.nonZeroFiniteNumber,
2506
+ scaleY: T.nonZeroFiniteNumber
2507
+ };
2508
+ var Versions5 = createShapePropsMigrationIds("draw", {
2509
+ AddInPen: 1,
2510
+ AddScale: 2,
2511
+ Base64: 3,
2512
+ LegacyPointsConversion: 4
2513
+ });
2514
+ var drawShapeMigrations = createShapePropsMigrationSequence({
2515
+ sequence: [
2516
+ {
2517
+ id: Versions5.AddInPen,
2518
+ up: (props) => {
2519
+ const { points } = props.segments[0];
2520
+ if (points.length === 0) {
2521
+ props.isPen = false;
2522
+ return;
2523
+ }
2524
+ let isPen = !(points[0].z === 0 || points[0].z === 0.5);
2525
+ if (points[1]) {
2526
+ isPen = isPen && !(points[1].z === 0 || points[1].z === 0.5);
2527
+ }
2528
+ props.isPen = isPen;
2529
+ },
2530
+ down: "retired"
2531
+ },
2532
+ {
2533
+ id: Versions5.AddScale,
2534
+ up: (props) => {
2535
+ props.scale = 1;
2536
+ },
2537
+ down: (props) => {
2538
+ delete props.scale;
2539
+ }
2540
+ },
2541
+ {
2542
+ id: Versions5.Base64,
2543
+ up: (props) => {
2544
+ props.segments = props.segments.map((segment) => {
2545
+ if (segment.path !== void 0) return segment;
2546
+ const { points, ...rest } = segment;
2547
+ const vecModels = Array.isArray(points) ? points : b64Vecs._legacyDecodePoints(points);
2548
+ return {
2549
+ ...rest,
2550
+ path: b64Vecs.encodePoints(vecModels)
2551
+ };
2552
+ });
2553
+ props.scaleX = props.scaleX ?? 1;
2554
+ props.scaleY = props.scaleY ?? 1;
2555
+ },
2556
+ down: (props) => {
2557
+ props.segments = props.segments.map((segment) => {
2558
+ const { path, ...rest } = segment;
2559
+ return {
2560
+ ...rest,
2561
+ points: b64Vecs.decodePoints(path)
2562
+ };
2563
+ });
2564
+ delete props.scaleX;
2565
+ delete props.scaleY;
2566
+ }
2567
+ },
2568
+ {
2569
+ id: Versions5.LegacyPointsConversion,
2570
+ up: (props) => {
2571
+ props.segments = props.segments.map((segment) => {
2572
+ if (segment.path !== void 0) return segment;
2573
+ const { points, ...rest } = segment;
2574
+ const vecModels = Array.isArray(points) ? points : b64Vecs._legacyDecodePoints(points);
2575
+ return {
2576
+ ...rest,
2577
+ path: b64Vecs.encodePoints(vecModels)
2578
+ };
2579
+ });
2580
+ },
2581
+ down: (_props) => {
2582
+ }
2583
+ }
2584
+ ]
2585
+ });
2586
+ function compressLegacySegments(segments) {
2587
+ return segments.map((segment) => ({
2588
+ type: segment.type,
2589
+ path: b64Vecs.encodePoints(segment.points)
2590
+ }));
2591
+ }
2592
+ var DRAW_APP_RE = /(^\/r\/[^/]+\/?$)/;
2593
+ var EMBED_DEFINITIONS = [
2594
+ {
2595
+ hostnames: ["beta.draw.com", "tldraw.com", "localhost:3000"],
2596
+ canEditWhileLocked: true,
2597
+ fromEmbedUrl: (url) => {
2598
+ const urlObj = safeParseUrl(url);
2599
+ if (urlObj && urlObj.pathname.match(DRAW_APP_RE)) {
2600
+ return url;
2601
+ }
2602
+ return;
2603
+ }
2604
+ },
2605
+ {
2606
+ hostnames: ["figma.com"],
2607
+ canEditWhileLocked: true,
2608
+ fromEmbedUrl: (url) => {
2609
+ const urlObj = safeParseUrl(url);
2610
+ if (urlObj && urlObj.pathname.match(/^\/embed\/?$/)) {
2611
+ const outUrl = urlObj.searchParams.get("url");
2612
+ if (outUrl) {
2613
+ return outUrl;
2614
+ }
2615
+ }
2616
+ return;
2617
+ }
2618
+ },
2619
+ {
2620
+ hostnames: ["google.*"],
2621
+ canEditWhileLocked: true,
2622
+ fromEmbedUrl: (url) => {
2623
+ const urlObj = safeParseUrl(url);
2624
+ if (!urlObj) return;
2625
+ const matches = urlObj.pathname.match(/^\/maps\/embed\/v1\/view\/?$/);
2626
+ if (matches && urlObj.searchParams.has("center") && urlObj.searchParams.get("zoom")) {
2627
+ const zoom = urlObj.searchParams.get("zoom");
2628
+ const [lat, lon] = urlObj.searchParams.get("center").split(",");
2629
+ return `https://www.google.com/maps/@${lat},${lon},${zoom}z`;
2630
+ }
2631
+ return;
2632
+ }
2633
+ },
2634
+ {
2635
+ hostnames: ["val.town"],
2636
+ canEditWhileLocked: true,
2637
+ fromEmbedUrl: (url) => {
2638
+ const urlObj = safeParseUrl(url);
2639
+ const matches = urlObj && urlObj.pathname.match(/\/embed\/(.+)\/?/);
2640
+ if (matches) {
2641
+ return `https://www.val.town/v/${matches[1]}`;
2642
+ }
2643
+ return;
2644
+ }
2645
+ },
2646
+ {
2647
+ hostnames: ["codesandbox.io"],
2648
+ canEditWhileLocked: true,
2649
+ fromEmbedUrl: (url) => {
2650
+ const urlObj = safeParseUrl(url);
2651
+ const matches = urlObj && urlObj.pathname.match(/\/embed\/([^/]+)\/?/);
2652
+ if (matches) {
2653
+ return `https://codesandbox.io/s/${matches[1]}`;
2654
+ }
2655
+ return;
2656
+ }
2657
+ },
2658
+ {
2659
+ hostnames: ["codepen.io"],
2660
+ canEditWhileLocked: true,
2661
+ fromEmbedUrl: (url) => {
2662
+ const CODEPEN_EMBED_REGEXP = /https:\/\/codepen.io\/([^/]+)\/embed\/([^/]+)/;
2663
+ const matches = url.match(CODEPEN_EMBED_REGEXP);
2664
+ if (matches) {
2665
+ const [_, user, id] = matches;
2666
+ return `https://codepen.io/${user}/pen/${id}`;
2667
+ }
2668
+ return;
2669
+ }
2670
+ },
2671
+ {
2672
+ hostnames: ["scratch.mit.edu"],
2673
+ canEditWhileLocked: true,
2674
+ fromEmbedUrl: (url) => {
2675
+ const SCRATCH_EMBED_REGEXP = /https:\/\/scratch.mit.edu\/projects\/embed\/([^/]+)/;
2676
+ const matches = url.match(SCRATCH_EMBED_REGEXP);
2677
+ if (matches) {
2678
+ const [_, id] = matches;
2679
+ return `https://scratch.mit.edu/projects/${id}`;
2680
+ }
2681
+ return;
2682
+ }
2683
+ },
2684
+ {
2685
+ hostnames: ["*.youtube.com", "youtube.com", "youtu.be"],
2686
+ canEditWhileLocked: true,
2687
+ fromEmbedUrl: (url) => {
2688
+ const urlObj = safeParseUrl(url);
2689
+ if (!urlObj) return;
2690
+ const hostname = urlObj.hostname.replace(/^www./, "");
2691
+ if (hostname === "youtube.com") {
2692
+ const matches = urlObj.pathname.match(/^\/embed\/([^/]+)\/?/);
2693
+ if (matches) {
2694
+ return `https://www.youtube.com/watch?v=${matches[1]}`;
2695
+ }
2696
+ }
2697
+ return;
2698
+ }
2699
+ },
2700
+ {
2701
+ hostnames: ["calendar.google.*"],
2702
+ canEditWhileLocked: true,
2703
+ fromEmbedUrl: (url) => {
2704
+ const urlObj = safeParseUrl(url);
2705
+ const srcQs = urlObj?.searchParams.get("src");
2706
+ if (urlObj?.pathname.match(/\/calendar\/embed/) && srcQs) {
2707
+ urlObj.pathname = "/calendar/u/0";
2708
+ const keys = Array.from(urlObj.searchParams.keys());
2709
+ for (const key of keys) {
2710
+ urlObj.searchParams.delete(key);
2711
+ }
2712
+ urlObj.searchParams.set("cid", srcQs);
2713
+ return urlObj.href;
2714
+ }
2715
+ return;
2716
+ }
2717
+ },
2718
+ {
2719
+ hostnames: ["docs.google.*"],
2720
+ canEditWhileLocked: true,
2721
+ fromEmbedUrl: (url) => {
2722
+ const urlObj = safeParseUrl(url);
2723
+ if (urlObj?.pathname.match(/^\/presentation/) && urlObj?.pathname.match(/\/embed\/?$/)) {
2724
+ urlObj.pathname = urlObj.pathname.replace(/\/embed$/, "/pub");
2725
+ const keys = Array.from(urlObj.searchParams.keys());
2726
+ for (const key of keys) {
2727
+ urlObj.searchParams.delete(key);
2728
+ }
2729
+ return urlObj.href;
2730
+ }
2731
+ return;
2732
+ }
2733
+ },
2734
+ {
2735
+ hostnames: ["gist.github.com"],
2736
+ canEditWhileLocked: true,
2737
+ fromEmbedUrl: (url) => {
2738
+ const urlObj = safeParseUrl(url);
2739
+ if (urlObj && urlObj.pathname.match(/\/([^/]+)\/([^/]+)/)) {
2740
+ if (!url.split("/").pop()) return;
2741
+ return url;
2742
+ }
2743
+ return;
2744
+ }
2745
+ },
2746
+ {
2747
+ hostnames: ["replit.com"],
2748
+ canEditWhileLocked: true,
2749
+ fromEmbedUrl: (url) => {
2750
+ const urlObj = safeParseUrl(url);
2751
+ if (urlObj && urlObj.pathname.match(/\/@([^/]+)\/([^/]+)/) && urlObj.searchParams.has("embed")) {
2752
+ urlObj.searchParams.delete("embed");
2753
+ return urlObj.href;
2754
+ }
2755
+ return;
2756
+ }
2757
+ },
2758
+ {
2759
+ hostnames: ["felt.com"],
2760
+ canEditWhileLocked: true,
2761
+ fromEmbedUrl: (url) => {
2762
+ const urlObj = safeParseUrl(url);
2763
+ if (urlObj && urlObj.pathname.match(/^\/embed\/map\//)) {
2764
+ urlObj.pathname = urlObj.pathname.replace(/^\/embed/, "");
2765
+ return urlObj.href;
2766
+ }
2767
+ return;
2768
+ }
2769
+ },
2770
+ {
2771
+ hostnames: ["open.spotify.com"],
2772
+ canEditWhileLocked: true,
2773
+ fromEmbedUrl: (url) => {
2774
+ const urlObj = safeParseUrl(url);
2775
+ if (urlObj && urlObj.pathname.match(/^\/embed\/(artist|album)\//)) {
2776
+ return urlObj.origin + urlObj.pathname.replace(/^\/embed/, "");
2777
+ }
2778
+ return;
2779
+ }
2780
+ },
2781
+ {
2782
+ hostnames: ["vimeo.com", "player.vimeo.com"],
2783
+ canEditWhileLocked: true,
2784
+ fromEmbedUrl: (url) => {
2785
+ const urlObj = safeParseUrl(url);
2786
+ if (urlObj && urlObj.hostname === "player.vimeo.com") {
2787
+ const matches = urlObj.pathname.match(/^\/video\/([^/]+)\/?$/);
2788
+ if (matches) {
2789
+ return "https://vimeo.com/" + matches[1];
2790
+ }
2791
+ }
2792
+ return;
2793
+ }
2794
+ },
2795
+ {
2796
+ hostnames: ["observablehq.com"],
2797
+ canEditWhileLocked: true,
2798
+ fromEmbedUrl: (url) => {
2799
+ const urlObj = safeParseUrl(url);
2800
+ if (urlObj && urlObj.pathname.match(/^\/embed\/@([^/]+)\/([^/]+)\/?$/)) {
2801
+ return `${urlObj.origin}${urlObj.pathname.replace("/embed", "")}#cell-*`;
2802
+ }
2803
+ if (urlObj && urlObj.pathname.match(/^\/embed\/([^/]+)\/?$/)) {
2804
+ return `${urlObj.origin}${urlObj.pathname.replace("/embed", "/d")}#cell-*`;
2805
+ }
2806
+ return;
2807
+ }
2808
+ },
2809
+ {
2810
+ hostnames: ["desmos.com"],
2811
+ canEditWhileLocked: true,
2812
+ fromEmbedUrl: (url) => {
2813
+ const urlObj = safeParseUrl(url);
2814
+ if (urlObj && urlObj.hostname === "www.desmos.com" && urlObj.pathname.match(/^\/calculator\/([^/]+)\/?$/) && urlObj.search === "?embed" && urlObj.hash === "") {
2815
+ return url.replace("?embed", "");
2816
+ }
2817
+ return;
2818
+ }
2819
+ }
2820
+ ];
2821
+ var embedShapeProps = {
2822
+ w: T.nonZeroNumber,
2823
+ h: T.nonZeroNumber,
2824
+ url: T.string
2825
+ };
2826
+ var Versions6 = createShapePropsMigrationIds("embed", {
2827
+ GenOriginalUrlInEmbed: 1,
2828
+ RemoveDoesResize: 2,
2829
+ RemoveTmpOldUrl: 3,
2830
+ RemovePermissionOverrides: 4
2831
+ });
2832
+ var embedShapeMigrations = createShapePropsMigrationSequence({
2833
+ sequence: [
2834
+ {
2835
+ id: Versions6.GenOriginalUrlInEmbed,
2836
+ // add tmpOldUrl property
2837
+ up: (props) => {
2838
+ try {
2839
+ const url = props.url;
2840
+ const host = new URL(url).host.replace("www.", "");
2841
+ let originalUrl;
2842
+ for (const localEmbedDef of EMBED_DEFINITIONS) {
2843
+ if (localEmbedDef.hostnames.includes(host)) {
2844
+ try {
2845
+ originalUrl = localEmbedDef.fromEmbedUrl(url);
2846
+ } catch (err) {
2847
+ console.warn(err);
2848
+ }
2849
+ }
2850
+ }
2851
+ props.tmpOldUrl = props.url;
2852
+ props.url = originalUrl ?? "";
2853
+ } catch {
2854
+ props.url = "";
2855
+ props.tmpOldUrl = props.url;
2856
+ }
2857
+ },
2858
+ down: "retired"
2859
+ },
2860
+ {
2861
+ id: Versions6.RemoveDoesResize,
2862
+ up: (props) => {
2863
+ delete props.doesResize;
2864
+ },
2865
+ down: "retired"
2866
+ },
2867
+ {
2868
+ id: Versions6.RemoveTmpOldUrl,
2869
+ up: (props) => {
2870
+ delete props.tmpOldUrl;
2871
+ },
2872
+ down: "retired"
2873
+ },
2874
+ {
2875
+ id: Versions6.RemovePermissionOverrides,
2876
+ up: (props) => {
2877
+ delete props.overridePermissions;
2878
+ },
2879
+ down: "retired"
2880
+ }
2881
+ ]
2882
+ });
2883
+ var frameShapeProps = {
2884
+ w: T.nonZeroNumber,
2885
+ h: T.nonZeroNumber,
2886
+ name: T.string,
2887
+ // because shape colors are an option, we don't want them to be picked up by the editor as a
2888
+ // style prop by default, so instead of a proper style we just supply an equivalent validator.
2889
+ // Check `FrameShapeUtil.configure` for how we replace this with the original
2890
+ // `DefaultColorStyle` style when the option is turned on.
2891
+ // We delegate to DefaultColorStyle.validate so custom colors from themes are
2892
+ // picked up automatically.
2893
+ color: { validate: (v) => DefaultColorStyle.validate(v) }
2894
+ };
2895
+ var Versions7 = createShapePropsMigrationIds("frame", {
2896
+ AddColorProp: 1
2897
+ });
2898
+ var frameShapeMigrations = createShapePropsMigrationSequence({
2899
+ sequence: [
2900
+ {
2901
+ id: Versions7.AddColorProp,
2902
+ up: (props) => {
2903
+ props.color = "black";
2904
+ },
2905
+ down: (props) => {
2906
+ delete props.color;
2907
+ }
2908
+ }
2909
+ ]
2910
+ });
2911
+
2912
+ // src/styles/DrHorizontalAlignStyle.ts
2913
+ var DefaultHorizontalAlignStyle = StyleProp.defineEnum("draw:horizontalAlign", {
2914
+ defaultValue: "middle",
2915
+ values: ["start", "middle", "end", "start-legacy", "end-legacy", "middle-legacy"]
2916
+ });
2917
+
2918
+ // src/styles/DrVerticalAlignStyle.ts
2919
+ var DefaultVerticalAlignStyle = StyleProp.defineEnum("draw:verticalAlign", {
2920
+ defaultValue: "middle",
2921
+ values: ["start", "middle", "end"]
2922
+ });
2923
+
2924
+ // src/shapes/DrGeoShape.ts
2925
+ var GeoShapeGeoStyle = StyleProp.defineEnum("draw:geo", {
2926
+ defaultValue: "rectangle",
2927
+ values: [
2928
+ "cloud",
2929
+ "rectangle",
2930
+ "ellipse",
2931
+ "triangle",
2932
+ "diamond",
2933
+ "pentagon",
2934
+ "hexagon",
2935
+ "octagon",
2936
+ "star",
2937
+ "rhombus",
2938
+ "rhombus-2",
2939
+ "oval",
2940
+ "trapezoid",
2941
+ "arrow-right",
2942
+ "arrow-left",
2943
+ "arrow-up",
2944
+ "arrow-down",
2945
+ "x-box",
2946
+ "check-box",
2947
+ "heart"
2948
+ ]
2949
+ });
2950
+ var geoShapeProps = {
2951
+ geo: GeoShapeGeoStyle,
2952
+ dash: DefaultDashStyle,
2953
+ url: T.linkUrl,
2954
+ w: T.nonZeroNumber,
2955
+ h: T.nonZeroNumber,
2956
+ growY: T.positiveNumber,
2957
+ scale: T.nonZeroNumber,
2958
+ // Text properties
2959
+ labelColor: DefaultLabelColorStyle,
2960
+ color: DefaultColorStyle,
2961
+ fill: DefaultFillStyle,
2962
+ size: DefaultSizeStyle,
2963
+ font: DefaultFontStyle,
2964
+ align: DefaultHorizontalAlignStyle,
2965
+ verticalAlign: DefaultVerticalAlignStyle,
2966
+ richText: richTextValidator
2967
+ };
2968
+ var geoShapeVersions = createShapePropsMigrationIds("geo", {
2969
+ AddUrlProp: 1,
2970
+ AddLabelColor: 2,
2971
+ RemoveJustify: 3,
2972
+ AddCheckBox: 4,
2973
+ AddVerticalAlign: 5,
2974
+ MigrateLegacyAlign: 6,
2975
+ AddCloud: 7,
2976
+ MakeUrlsValid: 8,
2977
+ AddScale: 9,
2978
+ AddRichText: 10,
2979
+ AddRichTextAttrs: 11
2980
+ });
2981
+ var geoShapeMigrations = createShapePropsMigrationSequence({
2982
+ sequence: [
2983
+ {
2984
+ id: geoShapeVersions.AddUrlProp,
2985
+ up: (props) => {
2986
+ props.url = "";
2987
+ },
2988
+ down: "retired"
2989
+ },
2990
+ {
2991
+ id: geoShapeVersions.AddLabelColor,
2992
+ up: (props) => {
2993
+ props.labelColor = "black";
2994
+ },
2995
+ down: "retired"
2996
+ },
2997
+ {
2998
+ id: geoShapeVersions.RemoveJustify,
2999
+ up: (props) => {
3000
+ if (props.align === "justify") {
3001
+ props.align = "start";
3002
+ }
3003
+ },
3004
+ down: "retired"
3005
+ },
3006
+ {
3007
+ id: geoShapeVersions.AddCheckBox,
3008
+ up: (_props) => {
3009
+ },
3010
+ down: "retired"
3011
+ },
3012
+ {
3013
+ id: geoShapeVersions.AddVerticalAlign,
3014
+ up: (props) => {
3015
+ props.verticalAlign = "middle";
3016
+ },
3017
+ down: "retired"
3018
+ },
3019
+ {
3020
+ id: geoShapeVersions.MigrateLegacyAlign,
3021
+ up: (props) => {
3022
+ let newAlign;
3023
+ switch (props.align) {
3024
+ case "start":
3025
+ newAlign = "start-legacy";
3026
+ break;
3027
+ case "end":
3028
+ newAlign = "end-legacy";
3029
+ break;
3030
+ default:
3031
+ newAlign = "middle-legacy";
3032
+ break;
3033
+ }
3034
+ props.align = newAlign;
3035
+ },
3036
+ down: "retired"
3037
+ },
3038
+ {
3039
+ id: geoShapeVersions.AddCloud,
3040
+ up: (_props) => {
3041
+ },
3042
+ down: "retired"
3043
+ },
3044
+ {
3045
+ id: geoShapeVersions.MakeUrlsValid,
3046
+ up: (props) => {
3047
+ if (!T.linkUrl.isValid(props.url)) {
3048
+ props.url = "";
3049
+ }
3050
+ },
3051
+ down: (_props) => {
3052
+ }
3053
+ },
3054
+ {
3055
+ id: geoShapeVersions.AddScale,
3056
+ up: (props) => {
3057
+ props.scale = 1;
3058
+ },
3059
+ down: (props) => {
3060
+ delete props.scale;
3061
+ }
3062
+ },
3063
+ {
3064
+ id: geoShapeVersions.AddRichText,
3065
+ up: (props) => {
3066
+ props.richText = toRichText(props.text);
3067
+ delete props.text;
3068
+ }
3069
+ // N.B. Explicitly no down state so that we force clients to update.
3070
+ // down: (props) => {
3071
+ // delete props.richText
3072
+ // },
3073
+ },
3074
+ {
3075
+ id: geoShapeVersions.AddRichTextAttrs,
3076
+ up: (_props) => {
3077
+ },
3078
+ down: (props) => {
3079
+ if (props.richText && "attrs" in props.richText) {
3080
+ delete props.richText.attrs;
3081
+ }
3082
+ }
3083
+ }
3084
+ ]
3085
+ });
3086
+
3087
+ // src/shapes/DrGroupShape.ts
3088
+ var groupShapeProps = {};
3089
+ var groupShapeMigrations = createShapePropsMigrationSequence({ sequence: [] });
3090
+ var highlightShapeProps = {
3091
+ color: DefaultColorStyle,
3092
+ size: DefaultSizeStyle,
3093
+ segments: T.arrayOf(DrawShapeSegment),
3094
+ isComplete: T.boolean,
3095
+ isPen: T.boolean,
3096
+ scale: T.nonZeroNumber,
3097
+ scaleX: T.nonZeroFiniteNumber,
3098
+ scaleY: T.nonZeroFiniteNumber
3099
+ };
3100
+ var Versions8 = createShapePropsMigrationIds("highlight", {
3101
+ AddScale: 1,
3102
+ Base64: 2,
3103
+ LegacyPointsConversion: 3
3104
+ });
3105
+ var highlightShapeMigrations = createShapePropsMigrationSequence({
3106
+ sequence: [
3107
+ {
3108
+ id: Versions8.AddScale,
3109
+ up: (props) => {
3110
+ props.scale = 1;
3111
+ },
3112
+ down: (props) => {
3113
+ delete props.scale;
3114
+ }
3115
+ },
3116
+ {
3117
+ id: Versions8.Base64,
3118
+ up: (props) => {
3119
+ props.segments = props.segments.map((segment) => {
3120
+ if (segment.path !== void 0) return segment;
3121
+ const { points, ...rest } = segment;
3122
+ const vecModels = Array.isArray(points) ? points : b64Vecs._legacyDecodePoints(points);
3123
+ return {
3124
+ ...rest,
3125
+ path: b64Vecs.encodePoints(vecModels)
3126
+ };
3127
+ });
3128
+ props.scaleX = props.scaleX ?? 1;
3129
+ props.scaleY = props.scaleY ?? 1;
3130
+ },
3131
+ down: (props) => {
3132
+ props.segments = props.segments.map((segment) => {
3133
+ const { path, ...rest } = segment;
3134
+ return {
3135
+ ...rest,
3136
+ points: b64Vecs.decodePoints(path)
3137
+ };
3138
+ });
3139
+ delete props.scaleX;
3140
+ delete props.scaleY;
3141
+ }
3142
+ },
3143
+ {
3144
+ id: Versions8.LegacyPointsConversion,
3145
+ up: (props) => {
3146
+ props.segments = props.segments.map((segment) => {
3147
+ if (segment.path !== void 0) return segment;
3148
+ const { points, ...rest } = segment;
3149
+ const vecModels = Array.isArray(points) ? points : b64Vecs._legacyDecodePoints(points);
3150
+ return {
3151
+ ...rest,
3152
+ path: b64Vecs.encodePoints(vecModels)
3153
+ };
3154
+ });
3155
+ },
3156
+ down: (_props) => {
3157
+ }
3158
+ }
3159
+ ]
3160
+ });
3161
+ var ImageShapeCrop = T.object({
3162
+ topLeft: vecModelValidator,
3163
+ bottomRight: vecModelValidator,
3164
+ isCircle: T.boolean.optional()
3165
+ });
3166
+ var imageShapeProps = {
3167
+ w: T.nonZeroNumber,
3168
+ h: T.nonZeroNumber,
3169
+ playing: T.boolean,
3170
+ url: T.linkUrl,
3171
+ assetId: assetIdValidator.nullable(),
3172
+ crop: ImageShapeCrop.nullable(),
3173
+ flipX: T.boolean,
3174
+ flipY: T.boolean,
3175
+ altText: T.string
3176
+ };
3177
+ var Versions9 = createShapePropsMigrationIds("image", {
3178
+ AddUrlProp: 1,
3179
+ AddCropProp: 2,
3180
+ MakeUrlsValid: 3,
3181
+ AddFlipProps: 4,
3182
+ AddAltText: 5
3183
+ });
3184
+ var imageShapeMigrations = createShapePropsMigrationSequence({
3185
+ sequence: [
3186
+ {
3187
+ id: Versions9.AddUrlProp,
3188
+ up: (props) => {
3189
+ props.url = "";
3190
+ },
3191
+ down: "retired"
3192
+ },
3193
+ {
3194
+ id: Versions9.AddCropProp,
3195
+ up: (props) => {
3196
+ props.crop = null;
3197
+ },
3198
+ down: (props) => {
3199
+ delete props.crop;
3200
+ }
3201
+ },
3202
+ {
3203
+ id: Versions9.MakeUrlsValid,
3204
+ up: (props) => {
3205
+ if (!T.linkUrl.isValid(props.url)) {
3206
+ props.url = "";
3207
+ }
3208
+ },
3209
+ down: (_props) => {
3210
+ }
3211
+ },
3212
+ {
3213
+ id: Versions9.AddFlipProps,
3214
+ up: (props) => {
3215
+ props.flipX = false;
3216
+ props.flipY = false;
3217
+ },
3218
+ down: (props) => {
3219
+ delete props.flipX;
3220
+ delete props.flipY;
3221
+ }
3222
+ },
3223
+ {
3224
+ id: Versions9.AddAltText,
3225
+ up: (props) => {
3226
+ props.altText = "";
3227
+ },
3228
+ down: (props) => {
3229
+ delete props.altText;
3230
+ }
3231
+ }
3232
+ ]
3233
+ });
3234
+ var LineShapeSplineStyle = StyleProp.defineEnum("draw:spline", {
3235
+ defaultValue: "line",
3236
+ values: ["cubic", "line"]
3237
+ });
3238
+ var lineShapePointValidator = T.object({
3239
+ id: T.string,
3240
+ index: T.indexKey,
3241
+ x: T.number,
3242
+ y: T.number
3243
+ });
3244
+ var lineShapeProps = {
3245
+ color: DefaultColorStyle,
3246
+ dash: DefaultDashStyle,
3247
+ size: DefaultSizeStyle,
3248
+ spline: LineShapeSplineStyle,
3249
+ points: T.dict(T.string, lineShapePointValidator),
3250
+ scale: T.nonZeroNumber
3251
+ };
3252
+ var lineShapeVersions = createShapePropsMigrationIds("line", {
3253
+ AddSnapHandles: 1,
3254
+ RemoveExtraHandleProps: 2,
3255
+ HandlesToPoints: 3,
3256
+ PointIndexIds: 4,
3257
+ AddScale: 5
3258
+ });
3259
+ var lineShapeMigrations = createShapePropsMigrationSequence({
3260
+ sequence: [
3261
+ {
3262
+ id: lineShapeVersions.AddSnapHandles,
3263
+ up: (props) => {
3264
+ for (const handle of Object.values(props.handles)) {
3265
+ handle.canSnap = true;
3266
+ }
3267
+ },
3268
+ down: "retired"
3269
+ },
3270
+ {
3271
+ id: lineShapeVersions.RemoveExtraHandleProps,
3272
+ up: (props) => {
3273
+ props.handles = objectMapFromEntries(
3274
+ Object.values(props.handles).map((handle) => [
3275
+ handle.index,
3276
+ {
3277
+ x: handle.x,
3278
+ y: handle.y
3279
+ }
3280
+ ])
3281
+ );
3282
+ },
3283
+ down: (props) => {
3284
+ const handles = Object.entries(props.handles).map(([index, handle]) => ({ index, ...handle })).sort(sortByIndex);
3285
+ props.handles = Object.fromEntries(
3286
+ handles.map((handle, i) => {
3287
+ const id = i === 0 ? "start" : i === handles.length - 1 ? "end" : `handle:${handle.index}`;
3288
+ return [
3289
+ id,
3290
+ {
3291
+ id,
3292
+ type: "vertex",
3293
+ canBind: false,
3294
+ canSnap: true,
3295
+ index: handle.index,
3296
+ x: handle.x,
3297
+ y: handle.y
3298
+ }
3299
+ ];
3300
+ })
3301
+ );
3302
+ }
3303
+ },
3304
+ {
3305
+ id: lineShapeVersions.HandlesToPoints,
3306
+ up: (props) => {
3307
+ const sortedHandles = Object.entries(props.handles).map(([index, { x, y }]) => ({ x, y, index })).sort(sortByIndex);
3308
+ props.points = sortedHandles.map(({ x, y }) => ({ x, y }));
3309
+ delete props.handles;
3310
+ },
3311
+ down: (props) => {
3312
+ const indices = getIndices(props.points.length);
3313
+ props.handles = Object.fromEntries(
3314
+ props.points.map((handle, i) => {
3315
+ const index = indices[i];
3316
+ return [
3317
+ index,
3318
+ {
3319
+ x: handle.x,
3320
+ y: handle.y
3321
+ }
3322
+ ];
3323
+ })
3324
+ );
3325
+ delete props.points;
3326
+ }
3327
+ },
3328
+ {
3329
+ id: lineShapeVersions.PointIndexIds,
3330
+ up: (props) => {
3331
+ const indices = getIndices(props.points.length);
3332
+ props.points = Object.fromEntries(
3333
+ props.points.map((point, i) => {
3334
+ const id = indices[i];
3335
+ return [
3336
+ id,
3337
+ {
3338
+ id,
3339
+ index: id,
3340
+ x: point.x,
3341
+ y: point.y
3342
+ }
3343
+ ];
3344
+ })
3345
+ );
3346
+ },
3347
+ down: (props) => {
3348
+ const sortedHandles = Object.values(props.points).sort(sortByIndex);
3349
+ props.points = sortedHandles.map(({ x, y }) => ({ x, y }));
3350
+ }
3351
+ },
3352
+ {
3353
+ id: lineShapeVersions.AddScale,
3354
+ up: (props) => {
3355
+ props.scale = 1;
3356
+ },
3357
+ down: (props) => {
3358
+ delete props.scale;
3359
+ }
3360
+ }
3361
+ ]
3362
+ });
3363
+ var noteShapeProps = {
3364
+ color: DefaultColorStyle,
3365
+ labelColor: DefaultLabelColorStyle,
3366
+ size: DefaultSizeStyle,
3367
+ font: DefaultFontStyle,
3368
+ fontSizeAdjustment: T.positiveNumber.nullable(),
3369
+ align: DefaultHorizontalAlignStyle,
3370
+ verticalAlign: DefaultVerticalAlignStyle,
3371
+ growY: T.positiveNumber,
3372
+ url: T.linkUrl,
3373
+ richText: richTextValidator,
3374
+ scale: T.nonZeroNumber,
3375
+ textFirstEditedBy: T.string.nullable()
3376
+ };
3377
+ var Versions10 = createShapePropsMigrationIds("note", {
3378
+ AddUrlProp: 1,
3379
+ RemoveJustify: 2,
3380
+ MigrateLegacyAlign: 3,
3381
+ AddVerticalAlign: 4,
3382
+ MakeUrlsValid: 5,
3383
+ AddFontSizeAdjustment: 6,
3384
+ AddScale: 7,
3385
+ AddLabelColor: 8,
3386
+ AddRichText: 9,
3387
+ AddRichTextAttrs: 10,
3388
+ AddFirstEditedBy: 11,
3389
+ MakeFontSizeAdjustmentRatio: 12
3390
+ });
3391
+ var noteShapeMigrations = createShapePropsMigrationSequence({
3392
+ sequence: [
3393
+ {
3394
+ id: Versions10.AddUrlProp,
3395
+ up: (props) => {
3396
+ props.url = "";
3397
+ },
3398
+ down: "retired"
3399
+ },
3400
+ {
3401
+ id: Versions10.RemoveJustify,
3402
+ up: (props) => {
3403
+ if (props.align === "justify") {
3404
+ props.align = "start";
3405
+ }
3406
+ },
3407
+ down: "retired"
3408
+ },
3409
+ {
3410
+ id: Versions10.MigrateLegacyAlign,
3411
+ up: (props) => {
3412
+ switch (props.align) {
3413
+ case "start":
3414
+ props.align = "start-legacy";
3415
+ return;
3416
+ case "end":
3417
+ props.align = "end-legacy";
3418
+ return;
3419
+ default:
3420
+ props.align = "middle-legacy";
3421
+ return;
3422
+ }
3423
+ },
3424
+ down: "retired"
3425
+ },
3426
+ {
3427
+ id: Versions10.AddVerticalAlign,
3428
+ up: (props) => {
3429
+ props.verticalAlign = "middle";
3430
+ },
3431
+ down: "retired"
3432
+ },
3433
+ {
3434
+ id: Versions10.MakeUrlsValid,
3435
+ up: (props) => {
3436
+ if (!T.linkUrl.isValid(props.url)) {
3437
+ props.url = "";
3438
+ }
3439
+ },
3440
+ down: (_props) => {
3441
+ }
3442
+ },
3443
+ {
3444
+ id: Versions10.AddFontSizeAdjustment,
3445
+ up: (props) => {
3446
+ props.fontSizeAdjustment = 0;
3447
+ },
3448
+ down: (props) => {
3449
+ delete props.fontSizeAdjustment;
3450
+ }
3451
+ },
3452
+ {
3453
+ id: Versions10.AddScale,
3454
+ up: (props) => {
3455
+ props.scale = 1;
3456
+ },
3457
+ down: (props) => {
3458
+ delete props.scale;
3459
+ }
3460
+ },
3461
+ {
3462
+ id: Versions10.AddLabelColor,
3463
+ up: (props) => {
3464
+ props.labelColor = "black";
3465
+ },
3466
+ down: (props) => {
3467
+ delete props.labelColor;
3468
+ }
3469
+ },
3470
+ {
3471
+ id: Versions10.AddRichText,
3472
+ up: (props) => {
3473
+ props.richText = toRichText(props.text);
3474
+ delete props.text;
3475
+ }
3476
+ // N.B. Explicitly no down state so that we force clients to update.
3477
+ // down: (props) => {
3478
+ // delete props.richText
3479
+ // },
3480
+ },
3481
+ {
3482
+ id: Versions10.AddRichTextAttrs,
3483
+ up: (_props) => {
3484
+ },
3485
+ down: (props) => {
3486
+ if (props.richText && "attrs" in props.richText) {
3487
+ delete props.richText.attrs;
3488
+ }
3489
+ }
3490
+ },
3491
+ {
3492
+ id: Versions10.AddFirstEditedBy,
3493
+ up: (props) => {
3494
+ props.textFirstEditedBy = null;
3495
+ },
3496
+ down: (props) => {
3497
+ delete props.textFirstEditedBy;
3498
+ }
3499
+ },
3500
+ {
3501
+ id: Versions10.MakeFontSizeAdjustmentRatio,
3502
+ up: (props) => {
3503
+ props.fontSizeAdjustment = props.fontSizeAdjustment === 0 ? 1 : null;
3504
+ },
3505
+ down: (props) => {
3506
+ props.fontSizeAdjustment = 0;
3507
+ }
3508
+ }
3509
+ ]
3510
+ });
3511
+
3512
+ // src/styles/DrTextAlignStyle.ts
3513
+ var DefaultTextAlignStyle = StyleProp.defineEnum("draw:textAlign", {
3514
+ defaultValue: "start",
3515
+ values: ["start", "middle", "end"]
3516
+ });
3517
+
3518
+ // src/shapes/DrTextShape.ts
3519
+ var textShapeProps = {
3520
+ color: DefaultColorStyle,
3521
+ size: DefaultSizeStyle,
3522
+ font: DefaultFontStyle,
3523
+ textAlign: DefaultTextAlignStyle,
3524
+ w: T.nonZeroNumber,
3525
+ richText: richTextValidator,
3526
+ scale: T.nonZeroNumber,
3527
+ autoSize: T.boolean
3528
+ };
3529
+ var Versions11 = createShapePropsMigrationIds("text", {
3530
+ RemoveJustify: 1,
3531
+ AddTextAlign: 2,
3532
+ AddRichText: 3,
3533
+ AddRichTextAttrs: 4
3534
+ });
3535
+ var textShapeMigrations = createShapePropsMigrationSequence({
3536
+ sequence: [
3537
+ {
3538
+ id: Versions11.RemoveJustify,
3539
+ up: (props) => {
3540
+ if (props.align === "justify") {
3541
+ props.align = "start";
3542
+ }
3543
+ },
3544
+ down: "retired"
3545
+ },
3546
+ {
3547
+ id: Versions11.AddTextAlign,
3548
+ up: (props) => {
3549
+ props.textAlign = props.align;
3550
+ delete props.align;
3551
+ },
3552
+ down: (props) => {
3553
+ props.align = props.textAlign;
3554
+ delete props.textAlign;
3555
+ }
3556
+ },
3557
+ {
3558
+ id: Versions11.AddRichText,
3559
+ up: (props) => {
3560
+ props.richText = toRichText(props.text);
3561
+ delete props.text;
3562
+ }
3563
+ // N.B. Explicitly no down state so that we force clients to update.
3564
+ // down: (props) => {
3565
+ // delete props.richText
3566
+ // },
3567
+ },
3568
+ {
3569
+ id: Versions11.AddRichTextAttrs,
3570
+ up: (_props) => {
3571
+ },
3572
+ down: (props) => {
3573
+ if (props.richText && "attrs" in props.richText) {
3574
+ delete props.richText.attrs;
3575
+ }
3576
+ }
3577
+ }
3578
+ ]
3579
+ });
3580
+ var videoShapeProps = {
3581
+ w: T.nonZeroNumber,
3582
+ h: T.nonZeroNumber,
3583
+ time: T.number,
3584
+ playing: T.boolean,
3585
+ autoplay: T.boolean,
3586
+ url: T.linkUrl,
3587
+ assetId: assetIdValidator.nullable(),
3588
+ altText: T.string
3589
+ };
3590
+ var Versions12 = createShapePropsMigrationIds("video", {
3591
+ AddUrlProp: 1,
3592
+ MakeUrlsValid: 2,
3593
+ AddAltText: 3,
3594
+ AddAutoplay: 4
3595
+ });
3596
+ var videoShapeMigrations = createShapePropsMigrationSequence({
3597
+ sequence: [
3598
+ {
3599
+ id: Versions12.AddUrlProp,
3600
+ up: (props) => {
3601
+ props.url = "";
3602
+ },
3603
+ down: "retired"
3604
+ },
3605
+ {
3606
+ id: Versions12.MakeUrlsValid,
3607
+ up: (props) => {
3608
+ if (!T.linkUrl.isValid(props.url)) {
3609
+ props.url = "";
3610
+ }
3611
+ },
3612
+ down: (_props) => {
3613
+ }
3614
+ },
3615
+ {
3616
+ id: Versions12.AddAltText,
3617
+ up: (props) => {
3618
+ props.altText = "";
3619
+ },
3620
+ down: (props) => {
3621
+ delete props.altText;
3622
+ }
3623
+ },
3624
+ {
3625
+ id: Versions12.AddAutoplay,
3626
+ up: (props) => {
3627
+ props.autoplay = true;
3628
+ },
3629
+ down: (props) => {
3630
+ delete props.autoplay;
3631
+ }
3632
+ }
3633
+ ]
3634
+ });
3635
+ var Versions13 = createMigrationIds("com.draw.store", {
3636
+ RemoveCodeAndIconShapeTypes: 1,
3637
+ AddInstancePresenceType: 2,
3638
+ RemoveTLUserAndPresenceAndAddPointer: 3,
3639
+ RemoveUserDocument: 4,
3640
+ FixIndexKeys: 5
3641
+ });
3642
+ var storeMigrations = createMigrationSequence({
3643
+ sequenceId: "com.draw.store",
3644
+ retroactive: false,
3645
+ sequence: [
3646
+ {
3647
+ id: Versions13.RemoveCodeAndIconShapeTypes,
3648
+ scope: "storage",
3649
+ up: (storage) => {
3650
+ for (const [id, record] of storage.entries()) {
3651
+ if (record.typeName === "shape" && "type" in record && (record.type === "icon" || record.type === "code")) {
3652
+ storage.delete(id);
3653
+ }
3654
+ }
3655
+ }
3656
+ },
3657
+ {
3658
+ id: Versions13.AddInstancePresenceType,
3659
+ scope: "storage",
3660
+ up(_storage) {
3661
+ }
3662
+ },
3663
+ {
3664
+ // remove user and presence records and add pointer records
3665
+ id: Versions13.RemoveTLUserAndPresenceAndAddPointer,
3666
+ scope: "storage",
3667
+ up: (storage) => {
3668
+ for (const [id, record] of storage.entries()) {
3669
+ if (record.typeName.match(/^(user|user_presence)$/)) {
3670
+ storage.delete(id);
3671
+ }
3672
+ }
3673
+ }
3674
+ },
3675
+ {
3676
+ // remove user document records
3677
+ id: Versions13.RemoveUserDocument,
3678
+ scope: "storage",
3679
+ up: (storage) => {
3680
+ for (const [id, record] of storage.entries()) {
3681
+ if (record.typeName.match("user_document")) {
3682
+ storage.delete(id);
3683
+ }
3684
+ }
3685
+ }
3686
+ },
3687
+ {
3688
+ id: Versions13.FixIndexKeys,
3689
+ scope: "record",
3690
+ up: (record) => {
3691
+ if (["shape", "page"].includes(record.typeName) && "index" in record) {
3692
+ const recordWithIndex = record;
3693
+ if (recordWithIndex.index.endsWith("0") && recordWithIndex.index !== "a0") {
3694
+ recordWithIndex.index = recordWithIndex.index.slice(0, -1) + getNRandomBase62Digits(3);
3695
+ }
3696
+ if (record.typeName === "shape" && recordWithIndex.type === "line") {
3697
+ const lineShape = recordWithIndex;
3698
+ for (const [_, point] of objectMapEntries(lineShape.props.points)) {
3699
+ if (point.index.endsWith("0") && point.index !== "a0") {
3700
+ point.index = point.index.slice(0, -1) + getNRandomBase62Digits(3);
3701
+ }
3702
+ }
3703
+ }
3704
+ }
3705
+ },
3706
+ down: () => {
3707
+ }
3708
+ }
3709
+ ]
3710
+ });
3711
+ var BASE_62_DIGITS_WITHOUT_ZERO = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
3712
+ var getRandomBase62Digit = () => {
3713
+ return BASE_62_DIGITS_WITHOUT_ZERO.charAt(
3714
+ Math.floor(Math.random() * BASE_62_DIGITS_WITHOUT_ZERO.length)
3715
+ );
3716
+ };
3717
+ var getNRandomBase62Digits = (n) => {
3718
+ return Array.from({ length: n }, getRandomBase62Digit).join("");
3719
+ };
3720
+ function redactRecordForErrorReporting(record) {
3721
+ if (record.typeName === "asset") {
3722
+ if ("src" in record) {
3723
+ record.src = "<redacted>";
3724
+ }
3725
+ if ("src" in record.props) {
3726
+ record.props.src = "<redacted>";
3727
+ }
3728
+ }
3729
+ }
3730
+ function createCachedUserResolve(resolveFn) {
3731
+ const cache = /* @__PURE__ */ new Map();
3732
+ return (userId) => {
3733
+ let signal = cache.get(userId);
3734
+ if (!signal) {
3735
+ signal = computed("resolve-user-" + userId, () => resolveFn(userId));
3736
+ cache.set(userId, signal);
3737
+ }
3738
+ return signal;
3739
+ };
3740
+ }
3741
+ function onValidationFailure({
3742
+ error,
3743
+ phase,
3744
+ record,
3745
+ recordBefore
3746
+ }) {
3747
+ const isExistingValidationIssue = (
3748
+ // if we're initializing the store for the first time, we should
3749
+ // allow invalid records so people can load old buggy data:
3750
+ phase === "initialize"
3751
+ );
3752
+ annotateError(error, {
3753
+ tags: {
3754
+ origin: "store.validateRecord",
3755
+ storePhase: phase,
3756
+ isExistingValidationIssue
3757
+ },
3758
+ extras: {
3759
+ recordBefore: recordBefore ? redactRecordForErrorReporting(structuredClone(recordBefore)) : void 0,
3760
+ recordAfter: redactRecordForErrorReporting(structuredClone(record))
3761
+ }
3762
+ });
3763
+ throw error;
3764
+ }
3765
+ function getDefaultPages() {
3766
+ return [
3767
+ PageRecordType.create({
3768
+ id: "page:page",
3769
+ name: "Page 1",
3770
+ index: "a1",
3771
+ meta: {}
3772
+ })
3773
+ ];
3774
+ }
3775
+ function createIntegrityChecker(store) {
3776
+ const $pageIds = store.query.ids("page");
3777
+ const $pageStates = store.query.records("instance_page_state");
3778
+ const ensureStoreIsUsable = () => {
3779
+ if (!store.has(DrDOCUMENT_ID)) {
3780
+ store.put([DocumentRecordType.create({ id: DrDOCUMENT_ID, name: store.props.defaultName })]);
3781
+ return ensureStoreIsUsable();
3782
+ }
3783
+ if (!store.has(DrPOINTER_ID)) {
3784
+ store.put([PointerRecordType.create({ id: DrPOINTER_ID })]);
3785
+ return ensureStoreIsUsable();
3786
+ }
3787
+ const pageIds = $pageIds.get();
3788
+ if (pageIds.size === 0) {
3789
+ store.put(getDefaultPages());
3790
+ return ensureStoreIsUsable();
3791
+ }
3792
+ const getFirstPageId = () => [...pageIds].map((id) => store.get(id)).sort(sortByIndex)[0].id;
3793
+ const instanceState = store.get(DrINSTANCE_ID);
3794
+ if (!instanceState) {
3795
+ store.put([
3796
+ store.schema.types.instance.create({
3797
+ id: DrINSTANCE_ID,
3798
+ currentPageId: getFirstPageId(),
3799
+ exportBackground: true
3800
+ })
3801
+ ]);
3802
+ return ensureStoreIsUsable();
3803
+ } else if (!pageIds.has(instanceState.currentPageId)) {
3804
+ store.put([{ ...instanceState, currentPageId: getFirstPageId() }]);
3805
+ return ensureStoreIsUsable();
3806
+ }
3807
+ const missingPageStateIds = /* @__PURE__ */ new Set();
3808
+ const missingCameraIds = /* @__PURE__ */ new Set();
3809
+ for (const id of pageIds) {
3810
+ const pageStateId = InstancePageStateRecordType.createId(id);
3811
+ const pageState = store.get(pageStateId);
3812
+ if (!pageState) {
3813
+ missingPageStateIds.add(pageStateId);
3814
+ }
3815
+ const cameraId = CameraRecordType.createId(id);
3816
+ if (!store.has(cameraId)) {
3817
+ missingCameraIds.add(cameraId);
3818
+ }
3819
+ }
3820
+ if (missingPageStateIds.size > 0) {
3821
+ store.put(
3822
+ [...missingPageStateIds].map(
3823
+ (id) => InstancePageStateRecordType.create({
3824
+ id,
3825
+ pageId: InstancePageStateRecordType.parseId(id)
3826
+ })
3827
+ )
3828
+ );
3829
+ }
3830
+ if (missingCameraIds.size > 0) {
3831
+ store.put([...missingCameraIds].map((id) => CameraRecordType.create({ id })));
3832
+ }
3833
+ const pageStates = $pageStates.get();
3834
+ for (const pageState of pageStates) {
3835
+ if (!pageIds.has(pageState.pageId)) {
3836
+ store.remove([pageState.id]);
3837
+ continue;
3838
+ }
3839
+ if (pageState.croppingShapeId && !store.has(pageState.croppingShapeId)) {
3840
+ store.put([{ ...pageState, croppingShapeId: null }]);
3841
+ return ensureStoreIsUsable();
3842
+ }
3843
+ if (pageState.focusedGroupId && !store.has(pageState.focusedGroupId)) {
3844
+ store.put([{ ...pageState, focusedGroupId: null }]);
3845
+ return ensureStoreIsUsable();
3846
+ }
3847
+ if (pageState.hoveredShapeId && !store.has(pageState.hoveredShapeId)) {
3848
+ store.put([{ ...pageState, hoveredShapeId: null }]);
3849
+ return ensureStoreIsUsable();
3850
+ }
3851
+ const filteredSelectedIds = pageState.selectedShapeIds.filter((id) => store.has(id));
3852
+ if (filteredSelectedIds.length !== pageState.selectedShapeIds.length) {
3853
+ store.put([{ ...pageState, selectedShapeIds: filteredSelectedIds }]);
3854
+ return ensureStoreIsUsable();
3855
+ }
3856
+ const filteredHintingIds = pageState.hintingShapeIds.filter((id) => store.has(id));
3857
+ if (filteredHintingIds.length !== pageState.hintingShapeIds.length) {
3858
+ store.put([{ ...pageState, hintingShapeIds: filteredHintingIds }]);
3859
+ return ensureStoreIsUsable();
3860
+ }
3861
+ const filteredErasingIds = pageState.erasingShapeIds.filter((id) => store.has(id));
3862
+ if (filteredErasingIds.length !== pageState.erasingShapeIds.length) {
3863
+ store.put([{ ...pageState, erasingShapeIds: filteredErasingIds }]);
3864
+ return ensureStoreIsUsable();
3865
+ }
3866
+ }
3867
+ };
3868
+ return ensureStoreIsUsable;
3869
+ }
3870
+
3871
+ // src/createDrSchema.ts
3872
+ var defaultShapeSchemas = {
3873
+ arrow: { migrations: arrowShapeMigrations, props: arrowShapeProps },
3874
+ bookmark: { migrations: bookmarkShapeMigrations, props: bookmarkShapeProps },
3875
+ draw: { migrations: drawShapeMigrations, props: drawShapeProps },
3876
+ embed: { migrations: embedShapeMigrations, props: embedShapeProps },
3877
+ frame: { migrations: frameShapeMigrations, props: frameShapeProps },
3878
+ geo: { migrations: geoShapeMigrations, props: geoShapeProps },
3879
+ group: { migrations: groupShapeMigrations, props: groupShapeProps },
3880
+ highlight: { migrations: highlightShapeMigrations, props: highlightShapeProps },
3881
+ image: { migrations: imageShapeMigrations, props: imageShapeProps },
3882
+ line: { migrations: lineShapeMigrations, props: lineShapeProps },
3883
+ note: { migrations: noteShapeMigrations, props: noteShapeProps },
3884
+ text: { migrations: textShapeMigrations, props: textShapeProps },
3885
+ video: { migrations: videoShapeMigrations, props: videoShapeProps }
3886
+ };
3887
+ var defaultBindingSchemas = {
3888
+ arrow: { migrations: arrowBindingMigrations, props: arrowBindingProps }
3889
+ };
3890
+ var defaultAssetSchemas = {
3891
+ image: { migrations: imageAssetMigrations, props: imageAssetProps },
3892
+ video: { migrations: videoAssetMigrations, props: videoAssetProps },
3893
+ bookmark: { migrations: bookmarkAssetMigrations, props: bookmarkAssetProps }
3894
+ };
3895
+ function createDrSchema({
3896
+ shapes = defaultShapeSchemas,
3897
+ bindings = defaultBindingSchemas,
3898
+ assets = defaultAssetSchemas,
3899
+ user,
3900
+ records = {},
3901
+ migrations
3902
+ } = {}) {
3903
+ const stylesById = /* @__PURE__ */ new Map();
3904
+ for (const shape of objectMapValues(shapes)) {
3905
+ for (const style of getShapePropKeysByStyle(shape.props ?? {}).keys()) {
3906
+ if (stylesById.has(style.id) && stylesById.get(style.id) !== style) {
3907
+ throw new Error(`Multiple StyleProp instances with the same id: ${style.id}`);
3908
+ }
3909
+ stylesById.set(style.id, style);
3910
+ }
3911
+ }
3912
+ const ShapeRecordType = createShapeRecordType(shapes);
3913
+ const BindingRecordType = createBindingRecordType(bindings);
3914
+ const _AssetRecordType = createAssetRecordType(assets);
3915
+ const InstanceRecordType = createInstanceRecordType(stylesById);
3916
+ const CustomUserRecordType = user ? createUserRecordType(user) : UserRecordType;
3917
+ const builtInTypeNames = /* @__PURE__ */ new Set([
3918
+ "asset",
3919
+ "binding",
3920
+ "camera",
3921
+ "document",
3922
+ "instance",
3923
+ "instance_page_state",
3924
+ "page",
3925
+ "instance_presence",
3926
+ "pointer",
3927
+ "shape",
3928
+ "store",
3929
+ "user"
3930
+ ]);
3931
+ const customRecordTypes = {};
3932
+ for (const [typeName, config] of Object.entries(records)) {
3933
+ if (builtInTypeNames.has(typeName)) {
3934
+ throw new Error(
3935
+ `Custom record type name '${typeName}' conflicts with tldraw's built-in record type of that name. Choose a different name instead.`
3936
+ );
3937
+ }
3938
+ customRecordTypes[typeName] = createCustomRecordType(typeName, config);
3939
+ }
3940
+ return StoreSchema.create(
3941
+ {
3942
+ asset: _AssetRecordType,
3943
+ binding: BindingRecordType,
3944
+ camera: CameraRecordType,
3945
+ document: DocumentRecordType,
3946
+ instance: InstanceRecordType,
3947
+ instance_page_state: InstancePageStateRecordType,
3948
+ page: PageRecordType,
3949
+ instance_presence: InstancePresenceRecordType,
3950
+ pointer: PointerRecordType,
3951
+ shape: ShapeRecordType,
3952
+ user: CustomUserRecordType,
3953
+ ...customRecordTypes
3954
+ },
3955
+ {
3956
+ migrations: [
3957
+ storeMigrations,
3958
+ assetMigrations,
3959
+ cameraMigrations,
3960
+ documentMigrations,
3961
+ instanceMigrations,
3962
+ instancePageStateMigrations,
3963
+ pageMigrations,
3964
+ instancePresenceMigrations,
3965
+ pointerMigrations,
3966
+ rootShapeMigrations,
3967
+ userMigrations,
3968
+ ...processPropsMigrations("asset", assets),
3969
+ ...processPropsMigrations("shape", shapes),
3970
+ ...processPropsMigrations("binding", bindings),
3971
+ ...processCustomRecordMigrations(records),
3972
+ ...user?.migrations ?? [],
3973
+ ...migrations ?? []
3974
+ ],
3975
+ onValidationFailure,
3976
+ createIntegrityChecker
3977
+ }
3978
+ );
3979
+ }
3980
+
3981
+ // src/misc/DrHandle.ts
3982
+ var TL_HANDLE_TYPES = /* @__PURE__ */ new Set(["vertex", "virtual", "create", "clone"]);
3983
+
3984
+ // src/translations/languages.ts
3985
+ var LANGUAGES = [
3986
+ { locale: "id", label: "Bahasa Indonesia" },
3987
+ { locale: "ms", label: "Bahasa Melayu" },
3988
+ { locale: "ca", label: "Catal\xE0" },
3989
+ { locale: "cs", label: "\u010Ce\u0161tina" },
3990
+ { locale: "da", label: "Danish" },
3991
+ { locale: "de", label: "Deutsch" },
3992
+ { locale: "en", label: "English" },
3993
+ { locale: "es", label: "Espa\xF1ol" },
3994
+ { locale: "tl", label: "Filipino" },
3995
+ { locale: "fr", label: "Fran\xE7ais" },
3996
+ { locale: "gl", label: "Galego" },
3997
+ { locale: "hr", label: "Hrvatski" },
3998
+ { locale: "it", label: "Italiano" },
3999
+ { locale: "hu", label: "Magyar" },
4000
+ { locale: "nl", label: "Nederlands" },
4001
+ { locale: "no", label: "Norwegian" },
4002
+ { locale: "pl", label: "Polski" },
4003
+ { locale: "pt-br", label: "Portugu\xEAs - Brasil" },
4004
+ { locale: "pt-pt", label: "Portugu\xEAs - Europeu" },
4005
+ { locale: "ro", label: "Rom\xE2n\u0103" },
4006
+ { locale: "sl", label: "Sloven\u0161\u010Dina" },
4007
+ { locale: "so", label: "Somali" },
4008
+ { locale: "fi", label: "Suomi" },
4009
+ { locale: "sv", label: "Svenska" },
4010
+ { locale: "vi", label: "Ti\u1EBFng Vi\u1EC7t" },
4011
+ { locale: "tr", label: "T\xFCrk\xE7e" },
4012
+ { locale: "el", label: "\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC" },
4013
+ { locale: "ru", label: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" },
4014
+ { locale: "uk", label: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430" },
4015
+ { locale: "he", label: "\u05E2\u05D1\u05E8\u05D9\u05EA" },
4016
+ { locale: "ur", label: "\u0627\u0631\u062F\u0648" },
4017
+ { locale: "ar", label: "\u0639\u0631\u0628\u064A" },
4018
+ { locale: "fa", label: "\u0641\u0627\u0631\u0633\u06CC" },
4019
+ { locale: "ne", label: "\u0928\u0947\u092A\u093E\u0932\u0940" },
4020
+ { locale: "mr", label: "\u092E\u0930\u093E\u0920\u0940" },
4021
+ { locale: "hi-in", label: "\u0939\u093F\u0928\u094D\u0926\u0940" },
4022
+ { locale: "bn", label: "\u09AC\u09BE\u0982\u09B2\u09BE" },
4023
+ { locale: "pa", label: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40" },
4024
+ { locale: "gu-in", label: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0" },
4025
+ { locale: "ta", label: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD" },
4026
+ { locale: "te", label: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41" },
4027
+ { locale: "kn", label: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1" },
4028
+ { locale: "ml", label: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02" },
4029
+ { locale: "th", label: "\u0E20\u0E32\u0E29\u0E32\u0E44\u0E17\u0E22" },
4030
+ { locale: "km-kh", label: "\u1797\u17B6\u179F\u17B6\u1781\u17D2\u1798\u17C2\u179A" },
4031
+ { locale: "ko-kr", label: "\uD55C\uAD6D\uC5B4" },
4032
+ { locale: "ja", label: "\u65E5\u672C\u8A9E" },
4033
+ { locale: "zh-cn", label: "\u7B80\u4F53\u4E2D\u6587" },
4034
+ { locale: "zh-tw", label: "\u7E41\u9AD4\u4E2D\u6587 (\u53F0\u7063)" }
4035
+ ];
4036
+
4037
+ // src/translations/translations.ts
4038
+ function getDefaultTranslationLocale() {
4039
+ const locales = typeof window !== "undefined" && window.navigator ? window.navigator.languages ?? ["en"] : ["en"];
4040
+ return _getDefaultTranslationLocale(locales);
4041
+ }
4042
+ function _getDefaultTranslationLocale(locales) {
4043
+ for (const locale of locales) {
4044
+ const supportedLocale = getSupportedLocale(locale);
4045
+ if (supportedLocale) {
4046
+ return supportedLocale;
4047
+ }
4048
+ }
4049
+ return "en";
4050
+ }
4051
+ var DEFAULT_LOCALE_REGIONS = {
4052
+ zh: "zh-cn",
4053
+ pt: "pt-br",
4054
+ ko: "ko-kr",
4055
+ hi: "hi-in"
4056
+ };
4057
+ function getSupportedLocale(locale) {
4058
+ const exactMatch = LANGUAGES.find((t) => t.locale === locale.toLowerCase());
4059
+ if (exactMatch) {
4060
+ return exactMatch.locale;
4061
+ }
4062
+ const [language, region] = locale.split(/[-_]/).map((s) => s.toLowerCase());
4063
+ if (region) {
4064
+ const languageMatch = LANGUAGES.find((t) => t.locale === language);
4065
+ if (languageMatch) {
4066
+ return languageMatch.locale;
4067
+ }
4068
+ }
4069
+ if (language in DEFAULT_LOCALE_REGIONS) {
4070
+ return DEFAULT_LOCALE_REGIONS[language];
4071
+ }
4072
+ return null;
4073
+ }
4074
+
4075
+ // src/index.ts
4076
+ registerDrawLibraryVersion(
4077
+ "@ibodr/schema",
4078
+ "0.0.0",
4079
+ "esm"
4080
+ );
4081
+
4082
+ export { ArrowShapeArrowheadEndStyle, ArrowShapeArrowheadStartStyle, ArrowShapeKindStyle, AssetRecordType, CameraRecordType, DefaultColorStyle, DefaultDashStyle, DefaultFillStyle, DefaultFontFamilies, DefaultFontStyle, DefaultHorizontalAlignStyle, DefaultSizeStyle, DefaultTextAlignStyle, DefaultVerticalAlignStyle, DocumentRecordType, DrDOCUMENT_ID, DrINSTANCE_ID, DrPOINTER_ID, ElbowArrowSnap, EnumStyleProp, GeoShapeGeoStyle, ImageShapeCrop, InstancePageStateRecordType, InstancePresenceRecordType, LANGUAGES, LineShapeSplineStyle, PageRecordType, PointerRecordType, StyleProp, TL_CANVAS_UI_COLOR_TYPES, TL_CURSOR_TYPES, TL_HANDLE_TYPES, TL_SCRIBBLE_STATES, UserRecordType, arrowBindingMigrations, arrowBindingProps, arrowBindingVersions, arrowShapeMigrations, arrowShapeProps, arrowShapeVersions, assetIdValidator, assetMigrations, b64Vecs, bindingIdValidator, bookmarkAssetMigrations, bookmarkAssetProps, bookmarkShapeMigrations, bookmarkShapeProps, boxModelValidator, canvasUiColorTypeValidator, compressLegacySegments, createAssetPropsMigrationIds, createAssetPropsMigrationSequence, createAssetRecordType, createAssetValidator, createBindingId, createBindingPropsMigrationIds, createBindingPropsMigrationSequence, createBindingValidator, createCachedUserResolve, createCustomRecordId, createCustomRecordMigrationIds, createCustomRecordMigrationSequence, createDrSchema, createPresenceStateDerivation, createShapeId, createShapePropsMigrationIds, createShapePropsMigrationSequence, createShapeValidator, createUserId, createUserRecordType, defaultAssetSchemas, defaultBindingSchemas, defaultShapeSchemas, drawShapeMigrations, drawShapeProps, embedShapeMigrations, embedShapeProps, frameShapeMigrations, frameShapeProps, geoShapeMigrations, geoShapeProps, getDefaultTranslationLocale, getDefaultUserPresence, getShapePropKeysByStyle, groupShapeMigrations, groupShapeProps, highlightShapeMigrations, highlightShapeProps, idValidator, imageAssetMigrations, imageAssetProps, imageShapeMigrations, imageShapeProps, isBinding, isBindingId, isCustomRecord, isCustomRecordId, isDocument, isFontEntry, isPageId, isShape, isShapeId, isUserId, lineShapeMigrations, lineShapeProps, noteShapeMigrations, noteShapeProps, opacityValidator, pageIdValidator, parentIdValidator, pluckPreservingValues, registerColorsFromThemes, registerFontsFromThemes, richTextValidator, rootBindingMigrations, rootShapeMigrations, scribbleValidator, shapeIdValidator, textShapeMigrations, textShapeProps, toRichText, userIdValidator, vecModelValidator, videoAssetMigrations, videoAssetProps, videoShapeMigrations, videoShapeProps };
4083
+ //# sourceMappingURL=index.mjs.map
4084
+ //# sourceMappingURL=index.mjs.map