@mocanvas/editor 4.0.1 → 4.1.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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { atom, computed, unsafe__withoutCapture, transact, react } from '@mocanvas/state';
2
2
  export { AtomMap, atom, computed, react, reactor, transact, transaction } from '@mocanvas/state';
3
- import { createRecordType, isIndexKey, createMigrationIds, uniqueId, isRecordsDiffEmpty, squashRecordDiffs, reverseRecordsDiff, ZERO_INDEX_KEY, sortByIndex, getIndexAbove, getIndexBetween, getIndexBelow, getIndicesAbove, indexKeyToZKey, parseMigrationId, createMigrationSequence, StoreSchema, Store } from '@mocanvas/store';
3
+ import { createRecordType, isIndexKey, createMigrationIds, uniqueId, isRecordsDiffEmpty, squashRecordDiffs, reverseRecordsDiff, sortByIndex, getIndexAbove, ZERO_INDEX_KEY, getIndexBetween, getIndexBelow, getIndicesAbove, indexKeyToZKey, parseMigrationId, createMigrationSequence, StoreSchema, Store } from '@mocanvas/store';
4
4
  export { ZERO_INDEX_KEY, createComputedCache, getGraphemeLength, getGraphemes, getIndexAbove, getIndexBelow, getIndexBetween, getIndices, getIndicesAbove, getIndicesBelow, getIndicesBetween, iterateGraphemes, sortByIndex } from '@mocanvas/store';
5
5
  import { PATH_OP, FLAG, GEO_FLAG, EngineBridge, VERTEX_FLOATS, BATCH_WORDS, readClip } from '@mocanvas/wasm';
6
6
  export { EngineBridge, FLAG, GEO_FLAG, GEO_KIND, PATH_OP, getLoadedEngine, loadEngine, loadEngineSync } from '@mocanvas/wasm';
@@ -2483,6 +2483,8 @@ var DOCUMENT_ID = DocumentRecordType.createId("document");
2483
2483
  var PageRecordType = createRecordType("page", { scope: "document" }).withDefaultProperties(() => ({
2484
2484
  meta: {}
2485
2485
  }));
2486
+ var DEFAULT_PAGE_ID = PageRecordType.createId("page");
2487
+ var FIRST_PAGE_INDEX = "a1";
2486
2488
  function isPage(record) {
2487
2489
  return record?.typeName === "page";
2488
2490
  }
@@ -3073,6 +3075,51 @@ function sourceSize(source) {
3073
3075
  const s = source;
3074
3076
  return [s.naturalWidth || s.videoWidth || s.width || 0, s.naturalHeight || s.videoHeight || s.height || 0];
3075
3077
  }
3078
+ var PRESENCE_COLORS = [
3079
+ "#e0575b",
3080
+ "#ef8b3a",
3081
+ "#d8a72e",
3082
+ "#4f9d55",
3083
+ "#2fa39a",
3084
+ "#3f86d8",
3085
+ "#7a63d8",
3086
+ "#c05aa8"
3087
+ ];
3088
+ function randomPresenceColor() {
3089
+ return PRESENCE_COLORS[Math.floor(Math.random() * PRESENCE_COLORS.length)];
3090
+ }
3091
+ var InstancePresenceRecordType = createRecordType("instance_presence", {
3092
+ scope: "presence"
3093
+ }).withDefaultProperties(() => ({
3094
+ userName: "",
3095
+ color: PRESENCE_COLORS[0],
3096
+ cursor: { x: 0, y: 0, type: "default", rotation: 0 },
3097
+ camera: { x: 0, y: 0, z: 1 },
3098
+ selectedShapeIds: [],
3099
+ brush: null,
3100
+ scribbles: [],
3101
+ followingUserId: null,
3102
+ lastActivityTimestamp: 0,
3103
+ chatMessage: "",
3104
+ meta: {}
3105
+ }));
3106
+ function isInstancePresenceId(id) {
3107
+ return id.startsWith("instance_presence:");
3108
+ }
3109
+ function createUserPreferences(init = {}) {
3110
+ const id = init.id ?? `user:${uniqueId(12)}`;
3111
+ const state = atom("editor.user", {
3112
+ name: init.name ?? `User ${id.slice(-4)}`,
3113
+ color: init.color ?? randomPresenceColor()
3114
+ });
3115
+ return {
3116
+ getId: () => id,
3117
+ getName: () => state.get().name,
3118
+ getColor: () => state.get().color,
3119
+ setName: (name) => state.update((s) => ({ ...s, name })),
3120
+ setColor: (color) => state.update((s) => ({ ...s, color }))
3121
+ };
3122
+ }
3076
3123
 
3077
3124
  // src/validation/validator.ts
3078
3125
  function formatValidationPath(path) {
@@ -3514,53 +3561,6 @@ function createMemoryUserStore(users, currentUserId) {
3514
3561
  resolve: (userId) => byId.get(userId) ?? null
3515
3562
  };
3516
3563
  }
3517
- var PRESENCE_COLORS = [
3518
- "#e0575b",
3519
- "#ef8b3a",
3520
- "#d8a72e",
3521
- "#4f9d55",
3522
- "#2fa39a",
3523
- "#3f86d8",
3524
- "#7a63d8",
3525
- "#c05aa8"
3526
- ];
3527
- function randomPresenceColor() {
3528
- return PRESENCE_COLORS[Math.floor(Math.random() * PRESENCE_COLORS.length)];
3529
- }
3530
- var InstancePresenceRecordType = createRecordType("instance_presence", {
3531
- scope: "presence"
3532
- }).withDefaultProperties(() => ({
3533
- userName: "",
3534
- color: PRESENCE_COLORS[0],
3535
- cursor: { x: 0, y: 0, type: "default", rotation: 0 },
3536
- camera: { x: 0, y: 0, z: 1 },
3537
- selectedShapeIds: [],
3538
- brush: null,
3539
- scribbles: [],
3540
- followingUserId: null,
3541
- lastActivityTimestamp: 0,
3542
- chatMessage: "",
3543
- meta: {}
3544
- }));
3545
- function isInstancePresenceId(id) {
3546
- return id.startsWith("instance_presence:");
3547
- }
3548
- function createUserPreferences(init = {}) {
3549
- const id = init.id ?? `user:${uniqueId(12)}`;
3550
- const state = atom("editor.user", {
3551
- name: init.name ?? `User ${id.slice(-4)}`,
3552
- color: init.color ?? randomPresenceColor()
3553
- });
3554
- return {
3555
- getId: () => id,
3556
- getName: () => state.get().name,
3557
- getColor: () => state.get().color,
3558
- setName: (name) => state.update((s) => ({ ...s, name })),
3559
- setColor: (color) => state.update((s) => ({ ...s, color }))
3560
- };
3561
- }
3562
-
3563
- // src/user/userPreferences.ts
3564
3564
  var DEFAULT_PRESENCE_COLOR = PRESENCE_COLORS[0];
3565
3565
  var USER_PREFERENCES_DEFAULTS = {
3566
3566
  name: "",
@@ -3572,7 +3572,8 @@ var USER_PREFERENCES_DEFAULTS = {
3572
3572
  isWrapMode: false,
3573
3573
  isDynamicSizeMode: false,
3574
3574
  isPasteAtCursorMode: false,
3575
- areKeyboardShortcutsEnabled: true
3575
+ areKeyboardShortcutsEnabled: true,
3576
+ isEnhancedA11yMode: false
3576
3577
  };
3577
3578
  function getFreshUserPreferences() {
3578
3579
  return { id: createUserId(), color: randomPresenceColor() };
@@ -3621,8 +3622,19 @@ var UserPreferencesManager = class {
3621
3622
  getLocale() {
3622
3623
  return this.getUserPreferences().locale ?? USER_PREFERENCES_DEFAULTS.locale;
3623
3624
  }
3625
+ /**
3626
+ * How fast the editor animates; `0` means "do not animate".
3627
+ *
3628
+ * A user who has expressed no preference inherits the operating system's,
3629
+ * the same way `colorScheme: "system"` does. Reduced motion is an
3630
+ * accessibility setting people set once, for every application, and an
3631
+ * editor that ignored it until it was told a second time would be reading
3632
+ * the setting and then disregarding it.
3633
+ */
3624
3634
  getAnimationSpeed() {
3625
- return this.getUserPreferences().animationSpeed ?? USER_PREFERENCES_DEFAULTS.animationSpeed;
3635
+ const own = this.getUserPreferences().animationSpeed;
3636
+ if (own !== void 0) return own;
3637
+ return prefersReducedMotion() ? 0 : USER_PREFERENCES_DEFAULTS.animationSpeed;
3626
3638
  }
3627
3639
  getEdgeScrollSpeed() {
3628
3640
  return this.getUserPreferences().edgeScrollSpeed ?? USER_PREFERENCES_DEFAULTS.edgeScrollSpeed;
@@ -3647,6 +3659,9 @@ var UserPreferencesManager = class {
3647
3659
  getIsPasteAtCursorMode() {
3648
3660
  return this.getUserPreferences().isPasteAtCursorMode ?? USER_PREFERENCES_DEFAULTS.isPasteAtCursorMode;
3649
3661
  }
3662
+ getIsEnhancedA11yMode() {
3663
+ return this.getUserPreferences().isEnhancedA11yMode ?? USER_PREFERENCES_DEFAULTS.isEnhancedA11yMode;
3664
+ }
3650
3665
  getAreKeyboardShortcutsEnabled() {
3651
3666
  return this.getUserPreferences().areKeyboardShortcutsEnabled ?? USER_PREFERENCES_DEFAULTS.areKeyboardShortcutsEnabled;
3652
3667
  }
@@ -3665,6 +3680,10 @@ function prefersDarkMode() {
3665
3680
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
3666
3681
  return window.matchMedia("(prefers-color-scheme: dark)").matches;
3667
3682
  }
3683
+ function prefersReducedMotion() {
3684
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
3685
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
3686
+ }
3668
3687
  function useCurrentUser({ userPreferences, setUserPreferences: setUserPreferences2 }) {
3669
3688
  const $preferences2 = useMemo(() => atom("react.userPreferences", userPreferences), []);
3670
3689
  const setter = useRef(setUserPreferences2);
@@ -6664,6 +6683,8 @@ var EdgeScrollManager = class extends EditorManager {
6664
6683
  const step = edgeScrollSpeed * ease * (elapsed / 16);
6665
6684
  const offset = new Vec(-direction.x * step, -direction.y * step);
6666
6685
  this.editor.pan(offset);
6686
+ const inputs = this.editor.inputs;
6687
+ inputs.currentPagePoint = this.editor.viewportToPage(inputs.currentScreenPoint);
6667
6688
  return offset;
6668
6689
  }
6669
6690
  /**
@@ -6989,10 +7010,11 @@ var ScribbleManager = class extends EditorManager {
6989
7010
  for (const item of [...this.items.values()]) {
6990
7011
  const points = [...item.scribble.points];
6991
7012
  let state = item.scribble.state;
7013
+ let moved = false;
6992
7014
  if (item.next && (!item.prev || item.prev.x !== item.next.x || item.prev.y !== item.next.y)) {
6993
7015
  points.push(item.next);
6994
7016
  item.prev = item.next;
6995
- changed = true;
7017
+ moved = true;
6996
7018
  }
6997
7019
  item.next = null;
6998
7020
  if (state === "starting" && points.length > 1) state = "active";
@@ -7002,7 +7024,7 @@ var ScribbleManager = class extends EditorManager {
7002
7024
  const shed = Math.max(1, Math.ceil(points.length * item.scribble.shrink));
7003
7025
  if (points.length > 0) {
7004
7026
  points.splice(0, shed);
7005
- changed = true;
7027
+ moved = true;
7006
7028
  }
7007
7029
  }
7008
7030
  if (points.length === 0 && (state === "stopping" || state === "paused")) {
@@ -7011,13 +7033,26 @@ var ScribbleManager = class extends EditorManager {
7011
7033
  changed = true;
7012
7034
  continue;
7013
7035
  }
7014
- if (points.length !== item.scribble.points.length || state !== item.scribble.state) {
7036
+ if (moved || state !== item.scribble.state) {
7015
7037
  item.scribble = { ...item.scribble, points, state };
7016
7038
  changed = true;
7017
7039
  }
7018
7040
  }
7019
7041
  if (changed) this.flush();
7020
7042
  }
7043
+ /**
7044
+ * Whether anything here still needs frames.
7045
+ *
7046
+ * A host's frame loop asks this to decide whether to schedule another one. It
7047
+ * is deliberately "is there a scribble at all" rather than "is there anything
7048
+ * visible to redraw": a point offered through {@link addPoint} is held in
7049
+ * `next` and writes nothing to the store, so a loop that parked itself
7050
+ * because the picture had settled would never wake up to commit it, and the
7051
+ * trail would stop dead under a moving pointer.
7052
+ */
7053
+ hasPendingWork() {
7054
+ return this.items.size > 0;
7055
+ }
7021
7056
  /** Every live scribble, in the order they were started. */
7022
7057
  getItems() {
7023
7058
  return [...this.items.values()];
@@ -7786,6 +7821,19 @@ var WHEEL_ZOOM_DELTA_CAP = 50;
7786
7821
  var MIDDLE_BUTTON = 1;
7787
7822
  var COLLABORATOR_INACTIVE_TIMEOUT = 6e4;
7788
7823
  var editorSequence = 0;
7824
+ var warnedKeys = /* @__PURE__ */ new Set();
7825
+ function warnOnce(key, message) {
7826
+ let isProduction = false;
7827
+ try {
7828
+ isProduction = typeof process !== "undefined" && process.env?.["NODE_ENV"] === "production";
7829
+ } catch {
7830
+ isProduction = false;
7831
+ }
7832
+ if (isProduction) return;
7833
+ if (warnedKeys.has(key)) return;
7834
+ warnedKeys.add(key);
7835
+ console.warn(message);
7836
+ }
7789
7837
  var Editor = class extends EventEmitter {
7790
7838
  store;
7791
7839
  engine;
@@ -7880,6 +7928,20 @@ var Editor = class extends EventEmitter {
7880
7928
  richTextEditor = null;
7881
7929
  /** Tools added or removed after construction, by id. */
7882
7930
  removedToolIds = /* @__PURE__ */ new Set();
7931
+ /**
7932
+ * This editor's own presence identity. See {@link getInstancePresenceId}.
7933
+ *
7934
+ * Minted per editor rather than per user: presence is about an *instance*,
7935
+ * and one person may have several.
7936
+ */
7937
+ _instancePresenceId = InstancePresenceRecordType.createId();
7938
+ /**
7939
+ * A {@link zoomToBounds} that arrived before the container had been measured,
7940
+ * waiting for the first non-empty viewport. See {@link zoomToBounds}.
7941
+ */
7942
+ pendingViewportFit = null;
7943
+ /** Whether a host has ever measured the canvas. See {@link getHasMeasuredViewport}. */
7944
+ hasMeasuredViewport = false;
7883
7945
  constructor(opts) {
7884
7946
  super();
7885
7947
  this.store = opts.store;
@@ -8064,7 +8126,7 @@ var Editor = class extends EventEmitter {
8064
8126
  }
8065
8127
  let pages = this.store.query.records("page").get();
8066
8128
  if (pages.length === 0) {
8067
- const page = PageRecordType.create({ id: PageRecordType.createId(), name: "Page 1", index: ZERO_INDEX_KEY });
8129
+ const page = PageRecordType.create({ id: DEFAULT_PAGE_ID, name: "Page 1", index: FIRST_PAGE_INDEX });
8068
8130
  this.store.put([page]);
8069
8131
  pages = [page];
8070
8132
  }
@@ -8558,6 +8620,18 @@ var Editor = class extends EventEmitter {
8558
8620
  getHitTestMargin() {
8559
8621
  return this.getInstanceState().isCoarsePointer ? this.options.coarseHitTestMargin : this.options.hitTestMargin;
8560
8622
  }
8623
+ /**
8624
+ * `opts.filter` with `renderingOnly` folded in.
8625
+ *
8626
+ * Returned as one predicate so each query applies both in the same place;
8627
+ * the culled set is read once per call rather than per candidate shape.
8628
+ */
8629
+ hitFilter(opts) {
8630
+ if (!opts.renderingOnly) return opts.filter;
8631
+ const culled = this.getCulledShapes();
8632
+ const filter = opts.filter;
8633
+ return filter ? (shape) => !culled.has(shape.id) && filter(shape) : (shape) => !culled.has(shape.id);
8634
+ }
8561
8635
  hitFilterBits(opts) {
8562
8636
  let bits = 0;
8563
8637
  if (opts.hitLocked) bits |= 1;
@@ -8589,13 +8663,14 @@ var Editor = class extends EventEmitter {
8589
8663
  this.flushEngine();
8590
8664
  const margin = (opts.margin ?? this.getHitTestMargin()) / this.getZoomLevel();
8591
8665
  const bits = this.hitFilterBits(opts) | (opts.hitInside ? 0 : 4);
8592
- if (!opts.filter) {
8666
+ const filter = this.hitFilter(opts);
8667
+ if (!filter) {
8593
8668
  const h = this.engine.hitTest(point.x, point.y, margin, bits);
8594
8669
  const id = this.handles.id(h);
8595
8670
  return id ? this.getShape(id) : void 0;
8596
8671
  }
8597
8672
  for (const shape of this.getShapesAtPoint(point, opts)) {
8598
- if (opts.filter(shape)) return shape;
8673
+ if (filter(shape)) return shape;
8599
8674
  }
8600
8675
  return void 0;
8601
8676
  }
@@ -8604,12 +8679,13 @@ var Editor = class extends EventEmitter {
8604
8679
  this.flushEngine();
8605
8680
  const margin = (opts.margin ?? this.getHitTestMargin()) / this.getZoomLevel();
8606
8681
  const handles = this.engine.queryBox(point.x - margin, point.y - margin, point.x + margin, point.y + margin, 0, this.hitFilterBits(opts));
8682
+ const filter = this.hitFilter(opts);
8607
8683
  const out = [];
8608
8684
  for (let i = handles.length - 1; i >= 0; i--) {
8609
8685
  const id = this.handles.id(handles[i]);
8610
8686
  const shape = id ? this.getShape(id) : void 0;
8611
8687
  if (!shape) continue;
8612
- if (opts.filter && !opts.filter(shape)) continue;
8688
+ if (filter && !filter(shape)) continue;
8613
8689
  const local = this.getPointInShapeSpace(shape, point);
8614
8690
  const geo = this.getShapeGeometry(shape);
8615
8691
  if (geo.hitTestPoint(local, margin, opts.hitInside ?? false)) out.push(shape);
@@ -8620,13 +8696,13 @@ var Editor = class extends EventEmitter {
8620
8696
  getShapesInsideBounds(box, opts = {}) {
8621
8697
  this.flushEngine();
8622
8698
  const handles = this.engine.queryBox(box.x, box.y, box.x + box.w, box.y + box.h, 1, this.hitFilterBits(opts));
8623
- return this.handlesToShapes(handles, opts.filter);
8699
+ return this.handlesToShapes(handles, this.hitFilter(opts));
8624
8700
  }
8625
8701
  /** Shapes whose outline touches a page box, in draw order. */
8626
8702
  getShapesIntersectingBounds(box, opts = {}) {
8627
8703
  this.flushEngine();
8628
8704
  const handles = this.engine.queryBox(box.x, box.y, box.x + box.w, box.y + box.h, 0, this.hitFilterBits(opts));
8629
- return this.handlesToShapes(handles, opts.filter);
8705
+ return this.handlesToShapes(handles, this.hitFilter(opts));
8630
8706
  }
8631
8707
  handlesToShapes(handles, filter) {
8632
8708
  const out = [];
@@ -8753,6 +8829,7 @@ var Editor = class extends EventEmitter {
8753
8829
  if (ps.editingShapeId && toDelete.has(ps.editingShapeId)) this.setEditingShape(null);
8754
8830
  this.store.remove([...toDelete]);
8755
8831
  });
8832
+ this.emit("deleted-shapes", [...toDelete]);
8756
8833
  return this;
8757
8834
  }
8758
8835
  /**
@@ -9514,13 +9591,16 @@ var Editor = class extends EventEmitter {
9514
9591
  */
9515
9592
  setCamera(point, opts = {}) {
9516
9593
  this.stopCameraAnimation();
9594
+ this.pendingViewportFit = null;
9517
9595
  if (this._cameraOptions.get().isLocked && opts.force !== true) return this;
9518
9596
  const cam = this.getCamera();
9519
9597
  const z = Math.min(this.options.zoomMax, Math.max(this.options.zoomMin, point.z ?? cam.z));
9520
9598
  const x = point.x ?? cam.x;
9521
9599
  const y = point.y ?? cam.y;
9522
9600
  if (cam.x === x && cam.y === y && cam.z === z) return this;
9523
- const duration = opts.immediate === true ? 0 : opts.animation?.duration ?? 0;
9601
+ const speed = this.user.getAnimationSpeed();
9602
+ const requested = opts.immediate === true ? 0 : opts.animation?.duration ?? 0;
9603
+ const duration = speed > 0 ? requested / speed : 0;
9524
9604
  if (duration > 0) {
9525
9605
  this.animateCameraTo({ x, y, z }, duration, opts.animation?.easing ?? easeInOutCubic);
9526
9606
  return this;
@@ -9612,6 +9692,19 @@ var Editor = class extends EventEmitter {
9612
9692
  const b = this.getInstanceState().screenBounds;
9613
9693
  return new Box(b.x, b.y, b.w, b.h);
9614
9694
  }
9695
+ /**
9696
+ * Whether a host has ever told us how big the canvas is
9697
+ * ({@link updateViewportScreenBounds}).
9698
+ *
9699
+ * Until it has, {@link getViewportScreenBounds} answers with the instance
9700
+ * record's default — a plausible-looking 1080x720 that is not this canvas —
9701
+ * or with zeros once a container that has not been laid out yet has been
9702
+ * measured. Both are wrong in the same way and neither announces itself,
9703
+ * which is why anything that needs the viewport asks this first.
9704
+ */
9705
+ getHasMeasuredViewport() {
9706
+ return this.hasMeasuredViewport;
9707
+ }
9615
9708
  getViewportScreenCenter() {
9616
9709
  const b = this.getViewportScreenBounds();
9617
9710
  return new Vec(b.w / 2, b.h / 2);
@@ -9633,10 +9726,18 @@ var Editor = class extends EventEmitter {
9633
9726
  * would otherwise fail on a `getBoundingClientRect` that was never there.
9634
9727
  */
9635
9728
  updateViewportScreenBounds(bounds, center = false) {
9729
+ this.hasMeasuredViewport = true;
9636
9730
  const prev = this.getViewportScreenBounds();
9637
9731
  const measured = typeof HTMLElement !== "undefined" && bounds instanceof HTMLElement ? (({ x, y, width, height }) => ({ x, y, w: width, h: height }))(bounds.getBoundingClientRect()) : bounds;
9638
9732
  const next = Box.From(measured);
9639
- if (prev.x === next.x && prev.y === next.y && prev.w === next.w && prev.h === next.h) return this;
9733
+ const unchanged = prev.x === next.x && prev.y === next.y && prev.w === next.w && prev.h === next.h;
9734
+ const pending = this.pendingViewportFit;
9735
+ this.pendingViewportFit = null;
9736
+ if (unchanged) {
9737
+ if (pending && next.w > 0 && next.h > 0) this.zoomToBounds(pending.bounds, pending.opts);
9738
+ else this.pendingViewportFit = pending;
9739
+ return this;
9740
+ }
9640
9741
  this.run(
9641
9742
  () => {
9642
9743
  this.updateInstanceState({ screenBounds: next.toJson() });
@@ -9647,6 +9748,8 @@ var Editor = class extends EventEmitter {
9647
9748
  },
9648
9749
  { history: "ignore" }
9649
9750
  );
9751
+ if (pending && next.w > 0 && next.h > 0) this.zoomToBounds(pending.bounds, pending.opts);
9752
+ else this.pendingViewportFit = pending;
9650
9753
  return this;
9651
9754
  }
9652
9755
  // Two screen-ish spaces, and the difference between them matters:
@@ -9710,6 +9813,15 @@ var Editor = class extends EventEmitter {
9710
9813
  */
9711
9814
  zoomToBounds(bounds, opts = {}) {
9712
9815
  const vp = this.getViewportScreenBounds();
9816
+ if (!this.hasMeasuredViewport || vp.w <= 0 || vp.h <= 0) {
9817
+ this.pendingViewportFit = { bounds: { x: bounds.x, y: bounds.y, w: bounds.w, h: bounds.h }, opts };
9818
+ warnOnce(
9819
+ `editor.zoom:${this.id}`,
9820
+ "mocanvas: zoomToFit / zoomToBounds was called before this canvas had a measured viewport \u2014 which is what onMount looks like, because the canvas component measures itself after the editor exists. Fitting against the placeholder bounds would put the camera somewhere that is not the shapes, so the move is deferred until the container reports a size; reading getCamera() straight afterwards therefore still sees the old camera. In a headless editor, call editor.updateViewportScreenBounds({ x: 0, y: 0, w, h }) first."
9821
+ );
9822
+ return this;
9823
+ }
9824
+ this.pendingViewportFit = null;
9713
9825
  const inset = opts.inset ?? Math.min(256, vp.w * 0.28);
9714
9826
  let z = Math.min((vp.w - inset) / bounds.w, (vp.h - inset) / bounds.h);
9715
9827
  if (opts.targetZoom !== void 0) z = Math.min(z, opts.targetZoom);
@@ -10280,11 +10392,36 @@ var Editor = class extends EventEmitter {
10280
10392
  * dynamic-size mode. Session-only, never persisted with the document.
10281
10393
  */
10282
10394
  user;
10283
- /** Presence records of everyone else in the room, in arrival order. */
10395
+ /**
10396
+ * This editor instance's presence record id — who *this tab* is, as opposed
10397
+ * to `user.getId()`, which is who the person is.
10398
+ *
10399
+ * The two are not interchangeable and conflating them was a bug: a user id
10400
+ * is per browser (it is the same in every tab, and the same on a phone and a
10401
+ * laptop signed in as one person), while a presence record is per editor
10402
+ * instance. Two tabs of one browser are two presences of one user, and they
10403
+ * must see each other.
10404
+ *
10405
+ * `@mocanvas/sync` publishes this tab's presence record under this id, which
10406
+ * is what lets {@link getCollaborators} drop our own record — and only our
10407
+ * own record — should it ever come back to us.
10408
+ */
10409
+ getInstancePresenceId() {
10410
+ return this._instancePresenceId;
10411
+ }
10412
+ /**
10413
+ * Presence records of everyone else in the room, in arrival order.
10414
+ *
10415
+ * "Else" means *another instance*, not another person: the filter is on the
10416
+ * presence record id ({@link getInstancePresenceId}), so a second tab, a
10417
+ * second window, or the same person on a phone and a laptop all show up as
10418
+ * collaborators. Filtering by user id instead made two tabs of one browser
10419
+ * invisible to each other while every message arrived correctly.
10420
+ */
10284
10421
  getCollaborators() {
10285
- const me = this.user.getId();
10422
+ const me = this.getInstancePresenceId();
10286
10423
  const records = this.store.query.records("instance_presence").get();
10287
- return records.filter((p) => p.userId !== me);
10424
+ return records.filter((p) => p.id !== me);
10288
10425
  }
10289
10426
  /** The subset of `getCollaborators()` looking at the page we are on. */
10290
10427
  getCollaboratorsOnCurrentPage() {
@@ -11589,6 +11726,29 @@ function createPropsMigrationSequences(options) {
11589
11726
  collect("binding", options.bindingUtils);
11590
11727
  return out;
11591
11728
  }
11729
+
11730
+ // src/records/defaultSchemas.ts
11731
+ var shapeSchemas = {};
11732
+ var bindingSchemas = {};
11733
+ var assetSchemas = {};
11734
+ var defaultShapeSchemas = shapeSchemas;
11735
+ var defaultBindingSchemas = bindingSchemas;
11736
+ var defaultAssetSchemas = assetSchemas;
11737
+ function registerDefaultShapeSchema(type, info) {
11738
+ return register(shapeSchemas, type, info);
11739
+ }
11740
+ function registerDefaultBindingSchema(type, info) {
11741
+ return register(bindingSchemas, type, info);
11742
+ }
11743
+ function registerDefaultAssetSchema(type, info) {
11744
+ return register(assetSchemas, type, info);
11745
+ }
11746
+ function register(map, type, info) {
11747
+ map[type] = info;
11748
+ return () => {
11749
+ if (map[type] === info) delete map[type];
11750
+ };
11751
+ }
11592
11752
  var CUSTOM_RECORD_TYPE_NAME = "custom";
11593
11753
  function createCustomRecordId(type, id) {
11594
11754
  return `${CUSTOM_RECORD_TYPE_NAME}:${type}:${id ?? uniqueId()}`;
@@ -11615,52 +11775,6 @@ function getCustomRecordIdType(id) {
11615
11775
  return colon > 0 ? rest.slice(0, colon) : void 0;
11616
11776
  }
11617
11777
 
11618
- // src/migrations/customRecordMigrations.ts
11619
- var CUSTOM_RECORD_MIGRATION_SEQUENCE_PREFIX = "com.tldraw.record";
11620
- function customRecordMigrationSequenceId(type) {
11621
- return `${CUSTOM_RECORD_MIGRATION_SEQUENCE_PREFIX}.${type}`;
11622
- }
11623
- function createCustomRecordMigrationIds(recordType, versions) {
11624
- return createMigrationIds(
11625
- customRecordMigrationSequenceId(recordType),
11626
- versions
11627
- );
11628
- }
11629
- function createCustomRecordMigrationSequence(migrations) {
11630
- return createShapePropsMigrationSequence(migrations);
11631
- }
11632
- function isPlainObject3(value) {
11633
- return typeof value === "object" && value !== null && !Array.isArray(value);
11634
- }
11635
- function toCustomRecordMigrationSequence(type, migrations) {
11636
- const first = migrations.sequence[0];
11637
- const sequenceId = first ? parseMigrationId(first.id).sequenceId : customRecordMigrationSequenceId(type);
11638
- if (!sequenceId.endsWith(`.${type}`)) {
11639
- throw new Error(
11640
- `Migration sequence "${sequenceId}" does not name custom record "${type}"; ids must end in ".${type}"`
11641
- );
11642
- }
11643
- const matches2 = (record) => record.typeName === CUSTOM_RECORD_TYPE_NAME && record.type === type;
11644
- const wrap = (fn) => (record) => {
11645
- const props = record.props;
11646
- if (!isPlainObject3(props)) return record;
11647
- const next = { ...props };
11648
- const replacement = fn(next);
11649
- return { ...record, props: replacement ?? next };
11650
- };
11651
- return createMigrationSequence({
11652
- sequenceId,
11653
- ...migrations.retroactive === void 0 ? {} : { retroactive: migrations.retroactive },
11654
- sequence: migrations.sequence.map((migration) => ({
11655
- id: migration.id,
11656
- scope: "record",
11657
- filter: matches2,
11658
- up: wrap(migration.up),
11659
- ...migration.down ? { down: wrap(migration.down) } : {}
11660
- }))
11661
- });
11662
- }
11663
-
11664
11778
  // src/records/uiValues.ts
11665
11779
  var TL_CANVAS_UI_COLOR_TYPES = [
11666
11780
  "accent",
@@ -11692,10 +11806,11 @@ var parentIdValidator = T.string.refine((value) => {
11692
11806
  });
11693
11807
 
11694
11808
  // src/records/recordValidators.ts
11695
- function propsValidator(props) {
11696
- return T.object(props);
11809
+ function propsValidator(props, unknownProps = "reject") {
11810
+ const validator = T.object(props);
11811
+ return unknownProps === "keep" ? validator.allowUnknownProperties() : validator;
11697
11812
  }
11698
- function createShapeValidator(type, props, meta) {
11813
+ function createShapeValidator(type, props, meta, options) {
11699
11814
  return T.model(
11700
11815
  `shape:${type}`,
11701
11816
  T.object({
@@ -11709,7 +11824,7 @@ function createShapeValidator(type, props, meta) {
11709
11824
  parentId: parentIdValidator,
11710
11825
  isLocked: T.boolean,
11711
11826
  opacity: opacityValidator,
11712
- props: propsValidator(props),
11827
+ props: propsValidator(props, options?.unknownProps),
11713
11828
  meta: meta ? propsValidator(meta) : T.jsonObject
11714
11829
  })
11715
11830
  // The config above describes exactly the record type named, but `T.object`
@@ -11718,7 +11833,41 @@ function createShapeValidator(type, props, meta) {
11718
11833
  // parameter. Restate what was actually built.
11719
11834
  );
11720
11835
  }
11721
- function createBindingValidator(type, props, meta) {
11836
+ function createBaseShapeValidator() {
11837
+ return T.model(
11838
+ "shape",
11839
+ T.object({
11840
+ id: T.idOfType("shape"),
11841
+ typeName: T.literal("shape"),
11842
+ type: T.string,
11843
+ x: T.number,
11844
+ y: T.number,
11845
+ rotation: T.number,
11846
+ index: T.indexKey,
11847
+ parentId: parentIdValidator,
11848
+ isLocked: T.boolean,
11849
+ opacity: opacityValidator,
11850
+ // Anything, deliberately — see above.
11851
+ props: T.jsonObject,
11852
+ meta: T.jsonObject
11853
+ })
11854
+ );
11855
+ }
11856
+ function createBaseBindingValidator() {
11857
+ return T.model(
11858
+ "binding",
11859
+ T.object({
11860
+ id: T.idOfType("binding"),
11861
+ typeName: T.literal("binding"),
11862
+ type: T.string,
11863
+ fromId: T.idOfType("shape"),
11864
+ toId: T.idOfType("shape"),
11865
+ props: T.jsonObject,
11866
+ meta: T.jsonObject
11867
+ })
11868
+ );
11869
+ }
11870
+ function createBindingValidator(type, props, meta, options) {
11722
11871
  return T.model(
11723
11872
  `binding:${type}`,
11724
11873
  T.object({
@@ -11727,7 +11876,7 @@ function createBindingValidator(type, props, meta) {
11727
11876
  type: T.literal(type),
11728
11877
  fromId: T.idOfType("shape"),
11729
11878
  toId: T.idOfType("shape"),
11730
- props: propsValidator(props),
11879
+ props: propsValidator(props, options?.unknownProps),
11731
11880
  meta: meta ? propsValidator(meta) : T.jsonObject
11732
11881
  })
11733
11882
  );
@@ -11763,6 +11912,100 @@ function createCustomRecordValidator(type, props, meta) {
11763
11912
  );
11764
11913
  }
11765
11914
 
11915
+ // src/records/validatedRecordTypes.ts
11916
+ function collectProps(utils, defaults2) {
11917
+ const out = {};
11918
+ for (const [type, info] of Object.entries(defaults2)) {
11919
+ if (info.props) out[type] = info.props;
11920
+ }
11921
+ for (const util of utils ?? []) {
11922
+ if (util.props) out[util.type] = util.props;
11923
+ }
11924
+ return out;
11925
+ }
11926
+ function dispatchingValidator(kind, byType, base) {
11927
+ return {
11928
+ validate(value) {
11929
+ const type = value?.type;
11930
+ if (typeof type !== "string") {
11931
+ throw new ValidationError(
11932
+ `Expected a ${kind} type, got ${type === void 0 ? "undefined" : typeof type}`,
11933
+ ["type"]
11934
+ );
11935
+ }
11936
+ const validator = byType.get(type);
11937
+ if (!validator) return base.validate(value);
11938
+ return validator.validate(value);
11939
+ }
11940
+ };
11941
+ }
11942
+ function createShapeRecordType(propsByType) {
11943
+ const byType = new Map(
11944
+ Object.entries(propsByType).map(
11945
+ ([type, props]) => [type, createShapeValidator(type, props, void 0, { unknownProps: "keep" })]
11946
+ )
11947
+ );
11948
+ return createRecordType("shape", {
11949
+ scope: "document",
11950
+ validator: dispatchingValidator("shape", byType, createBaseShapeValidator())
11951
+ }).withDefaultProperties(() => ({ x: 0, y: 0, rotation: 0, isLocked: false, opacity: 1, meta: {} }));
11952
+ }
11953
+ function createBindingRecordType(propsByType) {
11954
+ const byType = new Map(
11955
+ Object.entries(propsByType).map(
11956
+ ([type, props]) => [type, createBindingValidator(type, props, void 0, { unknownProps: "keep" })]
11957
+ )
11958
+ );
11959
+ return createRecordType("binding", {
11960
+ scope: "document",
11961
+ validator: dispatchingValidator("binding", byType, createBaseBindingValidator())
11962
+ }).withDefaultProperties(() => ({ meta: {} }));
11963
+ }
11964
+ var CUSTOM_RECORD_MIGRATION_SEQUENCE_PREFIX = "com.tldraw.record";
11965
+ function customRecordMigrationSequenceId(type) {
11966
+ return `${CUSTOM_RECORD_MIGRATION_SEQUENCE_PREFIX}.${type}`;
11967
+ }
11968
+ function createCustomRecordMigrationIds(recordType, versions) {
11969
+ return createMigrationIds(
11970
+ customRecordMigrationSequenceId(recordType),
11971
+ versions
11972
+ );
11973
+ }
11974
+ function createCustomRecordMigrationSequence(migrations) {
11975
+ return createShapePropsMigrationSequence(migrations);
11976
+ }
11977
+ function isPlainObject3(value) {
11978
+ return typeof value === "object" && value !== null && !Array.isArray(value);
11979
+ }
11980
+ function toCustomRecordMigrationSequence(type, migrations) {
11981
+ const first = migrations.sequence[0];
11982
+ const sequenceId = first ? parseMigrationId(first.id).sequenceId : customRecordMigrationSequenceId(type);
11983
+ if (!sequenceId.endsWith(`.${type}`)) {
11984
+ throw new Error(
11985
+ `Migration sequence "${sequenceId}" does not name custom record "${type}"; ids must end in ".${type}"`
11986
+ );
11987
+ }
11988
+ const matches2 = (record) => record.typeName === CUSTOM_RECORD_TYPE_NAME && record.type === type;
11989
+ const wrap = (fn) => (record) => {
11990
+ const props = record.props;
11991
+ if (!isPlainObject3(props)) return record;
11992
+ const next = { ...props };
11993
+ const replacement = fn(next);
11994
+ return { ...record, props: replacement ?? next };
11995
+ };
11996
+ return createMigrationSequence({
11997
+ sequenceId,
11998
+ ...migrations.retroactive === void 0 ? {} : { retroactive: migrations.retroactive },
11999
+ sequence: migrations.sequence.map((migration) => ({
12000
+ id: migration.id,
12001
+ scope: "record",
12002
+ filter: matches2,
12003
+ up: wrap(migration.up),
12004
+ ...migration.down ? { down: wrap(migration.down) } : {}
12005
+ }))
12006
+ });
12007
+ }
12008
+
11766
12009
  // src/records/schemaRecords.ts
11767
12010
  function createCustomRecordType(info) {
11768
12011
  const validator = createCustomRecordValidator(info.type, info.props);
@@ -11873,16 +12116,21 @@ function createSchema(migrationsOrUtils = []) {
11873
12116
  ...createCustomRecordMigrationSequences(records),
11874
12117
  ...migrationsOrUtils.migrations ?? []
11875
12118
  ];
11876
- return createSchemaWithMigrations(migrations, records);
12119
+ return createSchemaWithMigrations(migrations, records, {
12120
+ ...migrationsOrUtils.shapeUtils ? { shapeUtils: migrationsOrUtils.shapeUtils } : {},
12121
+ ...migrationsOrUtils.bindingUtils ? { bindingUtils: migrationsOrUtils.bindingUtils } : {}
12122
+ });
11877
12123
  }
11878
- function createSchemaWithMigrations(migrations, records) {
12124
+ function createSchemaWithMigrations(migrations, records, utils = {}) {
11879
12125
  const customRecords = createCustomRecordTypeMap(records);
12126
+ const shapeRecords = createShapeRecordType(collectProps(utils.shapeUtils, defaultShapeSchemas));
12127
+ const bindingRecords = createBindingRecordType(collectProps(utils.bindingUtils, defaultBindingSchemas));
11880
12128
  return StoreSchema.create(
11881
12129
  {
11882
12130
  document: DocumentRecordType,
11883
12131
  page: PageRecordType,
11884
- shape: ShapeRecordType,
11885
- binding: BindingRecordType,
12132
+ shape: shapeRecords,
12133
+ binding: bindingRecords,
11886
12134
  asset: AssetRecordType,
11887
12135
  camera: CameraRecordType,
11888
12136
  instance: InstanceRecordType,
@@ -11909,8 +12157,19 @@ function createStore(options = {}) {
11909
12157
  props: { defaultName: options.defaultName ?? "", assets: options.assets ?? createInMemoryAssetStore() }
11910
12158
  });
11911
12159
  if (options.snapshot) store.loadStoreSnapshot(options.snapshot);
12160
+ else seedBaseRecords(store);
11912
12161
  return store;
11913
12162
  }
12163
+ function seedBaseRecords(store) {
12164
+ if (store.has(DOCUMENT_ID)) return;
12165
+ if (store.query.records("page").get().length > 0) return;
12166
+ store.put([
12167
+ DocumentRecordType.create({ id: DOCUMENT_ID, name: store.props.defaultName }),
12168
+ // The same fixed id the editor uses, so two replicas that each seeded
12169
+ // their own store meet on one page rather than diverging into two.
12170
+ PageRecordType.create({ id: DEFAULT_PAGE_ID, name: "Page 1", index: FIRST_PAGE_INDEX })
12171
+ ]);
12172
+ }
11914
12173
 
11915
12174
  // src/editor/selectionHandles.ts
11916
12175
  var HANDLE_HIT_RADIUS = 12;
@@ -12344,27 +12603,6 @@ function stringOrUndefined(value) {
12344
12603
  }
12345
12604
 
12346
12605
  // src/editor/schemaFactories.ts
12347
- var shapeSchemas = {};
12348
- var bindingSchemas = {};
12349
- var assetSchemas = {};
12350
- var defaultShapeSchemas = shapeSchemas;
12351
- var defaultBindingSchemas = bindingSchemas;
12352
- var defaultAssetSchemas = assetSchemas;
12353
- function registerDefaultShapeSchema(type, info) {
12354
- return register(shapeSchemas, type, info);
12355
- }
12356
- function registerDefaultBindingSchema(type, info) {
12357
- return register(bindingSchemas, type, info);
12358
- }
12359
- function registerDefaultAssetSchema(type, info) {
12360
- return register(assetSchemas, type, info);
12361
- }
12362
- function register(map, type, info) {
12363
- map[type] = info;
12364
- return () => {
12365
- if (map[type] === info) delete map[type];
12366
- };
12367
- }
12368
12606
  function createTLSchemaFromUtils(options = {}) {
12369
12607
  return createSchema({
12370
12608
  ...options.shapeUtils ? { shapeUtils: options.shapeUtils } : {},
@@ -13725,6 +13963,12 @@ var BindingUtil = class {
13725
13963
  }
13726
13964
  editor;
13727
13965
  static type;
13966
+ /**
13967
+ * One validator per prop of the binding this util describes — the contract
13968
+ * the store checks a record against before it is written, and what
13969
+ * `createSchema()` reads to build the document schema. A binding type that
13970
+ * declares none is not validated: see `createBindingRecordType`.
13971
+ */
13728
13972
  static props;
13729
13973
  static migrations;
13730
13974
  get type() {
@@ -14432,6 +14676,10 @@ var BRUSH_FILL = "var(--mocanvas-brush-fill, rgba(47, 111, 228, 0.12))";
14432
14676
  var SNAP = "var(--mocanvas-snap, #cf3fe0)";
14433
14677
  var INDICATOR_STROKE = 1.5;
14434
14678
  var HANDLE = { corner: 9, rotate: 5.5, shape: 6, virtual: 4 };
14679
+ var MAX_TICK_MS = 64;
14680
+ function hasFrameWork(editor) {
14681
+ return editor.scribbles.hasPendingWork() || editor.edgeScrollManager.getIsEnabled();
14682
+ }
14435
14683
  function Canvas({ editor, className, style, children, components, indicatorOverlayUtil }) {
14436
14684
  const containerRef = useRef(null);
14437
14685
  const canvasRef = useRef(null);
@@ -14470,13 +14718,20 @@ function Canvas({ editor, className, style, children, components, indicatorOverl
14470
14718
  if (!backend) return;
14471
14719
  let raf = 0;
14472
14720
  let dirty = true;
14473
- const draw = () => {
14721
+ let last = null;
14722
+ const draw = (time) => {
14474
14723
  raf = 0;
14475
- if (!dirty) return;
14476
- dirty = false;
14477
- if (editor.renderFrame(backend).pending) {
14478
- dirty = true;
14724
+ const elapsed = last === null ? 0 : Math.min(time - last, MAX_TICK_MS);
14725
+ last = time;
14726
+ if (elapsed > 0) editor.emit("tick", elapsed);
14727
+ if (dirty) {
14728
+ dirty = false;
14729
+ if (editor.renderFrame(backend).pending) dirty = true;
14730
+ }
14731
+ if (dirty || hasFrameWork(editor)) {
14479
14732
  raf = requestAnimationFrame(draw);
14733
+ } else {
14734
+ last = null;
14480
14735
  }
14481
14736
  };
14482
14737
  const stop = react(
@@ -15418,6 +15673,6 @@ function setDefaultCdnBaseUrl(url) {
15418
15673
  cdnBaseUrl = url.replace(/\/+$/, "");
15419
15674
  }
15420
15675
 
15421
- export { ARROWHEAD_KINDS, ARROW_SHAPE_KINDS, ASSET_MIGRATION_SEQUENCE_PREFIX, Arc2d, ArrayOfValidator, ArrowShapeArrowheadEndStyle, ArrowShapeArrowheadStartStyle, ArrowShapeKindStyle, AssetRecordType, AssetUrlsProvider, AssetUtil, AssetUtilRegistry, BINDING_MIGRATION_SEQUENCE_PREFIX, BUILTIN_ASSET_MIGRATION_SEQUENCE_PREFIX, BUILTIN_BINDING_MIGRATION_SEQUENCE_PREFIX, BUILTIN_SHAPE_MIGRATION_SEQUENCE_PREFIX, BaseBoxShapeUtil, BaseFrameLikeShapeUtil, BindingRecordType, BindingUtil, BoundsSnaps, Box, CANVAS_THEME_VARS, COLLABORATOR_INACTIVE_TIMEOUT, CURRENT_SESSION_SCHEMA_VERSION, CURSOR_TYPES, CUSTOM_RECORD_MIGRATION_SEQUENCE_PREFIX, CUSTOM_RECORD_TYPE_NAME, CameraRecordType, CameraStateTracker, Canvas, Circle2d, ClickManager, CollaboratorsManager, CommentReactionRecordType, CommentRecordType, CommentThreadRecordType, ContainerProvider, ContentElementManager, CubicBezier2d, CubicSpline2d, DEFAULT_ASSET_CONTEXT, DEFAULT_CAMERA_OPTIONS, DEFAULT_COLORS, DEFAULT_DARK_COLORS, DEFAULT_DASHES, DEFAULT_EDITOR_CONFIG, DEFAULT_FILLS, DEFAULT_FILL_TOKENS, DEFAULT_FONTS, DEFAULT_FONT_FAMILIES, DEFAULT_H_ALIGNS, DEFAULT_LIGHT_COLORS, DEFAULT_LINE_HEIGHT, DEFAULT_MENU_CONTEXT, DEFAULT_SHAPE_INDICATOR_OPTIONS, DEFAULT_SIZES, DEFAULT_TEXT_ALIGNS, DEFAULT_THEME, DEFAULT_TIME_CONTEXT, DEFAULT_V_ALIGNS, DIM_2D, DIM_3D, DOCUMENT_ID, DefaultBackground, DefaultCanvas, DefaultColorStyle, DefaultCursor, DefaultDashStyle, DefaultErrorFallback, DefaultFillStyle, DefaultFontFaces, DefaultFontFamilies, DefaultFontStyle, DefaultGrid, DefaultHorizontalAlignStyle, DefaultLabelColorStyle, DefaultShapeWrapper, DefaultSizeStyle, DefaultSpinner, DefaultSvgDefs, DefaultTextAlignStyle, DefaultVerticalAlignStyle, DictValidator, DocumentRecordType, EASINGS, ELBOW_ARROW_SNAP_MODES, EVENT_NAME_MAP, Edge2d, EdgeScrollManager, Editor, EditorAtom, EditorContext, EditorManager, EditorPortal, EditorProvider, ElbowArrowSnap, Ellipse2d, EnumStyleProp, ErrorBoundary, ErrorScreen, EventEmitter, FONT_SIZES, FontManager, GEO_SHAPE_KINDS, GeoShapeGeoStyle, Geometry2d, Geometry2dFilters, Group2d, HALF_PI, HANDLE_HIT_RADIUS, HTMLContainer, HandleSnaps, HandleTable, HistoryManager, INSTANCE_ID, ImageShapeCrop, InputsManager, InstancePageStateRecordType, InstancePresenceRecordType, InstanceRecordType, LIGHT_THEME, LINE_SPLINE_KINDS, LOCAL_STATE_PREFIX, LineShapeSplineStyle, LoadingScreen, MAX_TEXTURE_RESOLUTION, MAX_TEXTURE_ZOOM, Mat, MenuClickCapture, MenuManager, MocanvasUiProvider, ObjectValidator, OverlayManager, OverlayUtil, PI, PI2, PRESENCE_COLORS, PageRecordType, PerformanceApiAdapter, PerformanceManager, Point2d, PointerRecordType, Polygon2d, Polyline2d, ROTATE_CORNER_TO_SELECTION_CORNER, ROTATE_HANDLE_OFFSET, ReadonlySharedStyleMap, Rectangle2d, RootState, SHAPE_MIGRATION_SEQUENCE_PREFIX, SIDES, SIN, STROKE_SIZES, SVGContainer, ScribbleManager, ShapeIndicatorCompositor, ShapeRecordType, ShapeUtil, SharedStyleMap, SnapManager, Stadium2d, StateNode, StyleProp, SvgExportContextProvider, T, TAB_ID, TLEditorsRegistry, TLPOINTER_ID, TL_CANVAS_UI_COLOR_TYPES, TL_CURSOR_TYPES, TL_HANDLE_TYPES, TL_SCRIBBLE_STATES, TextManager, TextureManager, ThemeManager, Timers, TransformedGeometry2d, UNKNOWN_EDIT_START_INFO, USER_PREFERENCES_DEFAULTS, UnionValidator, UserPreferencesManager, UserRecordType, ValidationError, Validator, Vec, WebGL2Backend, angleDistance, animateShape, animateShapes, applyThemePatch, approximately, areAnglesCompatible, arrowBindingVersions, assetIdValidator, assetMigrations, assetPropsMigrationSequenceId, assetValidator, assetValidators, average, b64Vecs, bindingIdValidator, bindingPropsMigrationSequenceId, bookmarkAssetMigrations, bookmarkAssetProps, bookmarkAssetPropsValidator, bookmarkAssetValidator, boundsIndicatorPath, boxModelValidator, bucketTextureResolution, canBindShapes, canBuildIndicatorPaths, canCreateShape, canCreateShapes, canCropShape, canEditShape, canonicalizeRotation, canvasUiColorTypeValidator, centerOfCircleFromThreePoints, clamp, clampRadians, clockwiseAngleDist, commentAnchorValidator, commentReactionRecordConfig, commentReactionValidator, commentRecordConfig, commentSchemaRecords, commentThreadRecordConfig, commentThreadValidator, commentValidator, compressLegacySegments, coreShapes, counterClockwiseAngleDist, createAssetId, createAssetPropsMigrationIds, createAssetPropsMigrationSequence, createAssetValidator, createBackend, createBindingId, createBindingPropsMigrationIds, createBindingPropsMigrationSequence, createBindingValidator, createBuiltInAssetPropsMigrationIds, createBuiltInBindingPropsMigrationIds, createBuiltInShapePropsMigrationIds, createCachedUserResolve, createComment, createCommentId, createCommentReaction, createCommentReactionId, createCommentThread, createCommentThreadId, createCurrentUser, createCustomRecord, createCustomRecordId, createCustomRecordMigrationIds, createCustomRecordMigrationSequence, createCustomRecordMigrationSequences, createCustomRecordType, createCustomRecordTypeMap, createCustomRecordValidator, createDeepLinkString, createInMemoryAssetStore, createMemoryUserStore, createPresenceStateDerivation, createPropsMigrationSequences, createRootState, createSchema, createSessionStateSnapshotSignal, createShapeId, createShapePropsMigrationIds, createShapePropsMigrationSequence, createShapeValidator, createStore, createTLCurrentUser, createTLSchemaFromUtils, createTheme, createUserId, createUserPreferences, createUserRecordType, cursorTypeValidator, cursorValidator, customRecordMigrationSequenceId, dataUrlToFile, decodeDrawSegmentPath, defaultAssetMigrations, defaultAssetSchemas, defaultBindingSchemas, defaultShapeSchemas, defaultTldrawOptions, defaultUserPreferences, defaultUserStore, degreesToRadians, deselect, drawShapeSegmentValidator, dropShapesOnFrameLike, duplicatePage, easeInOutCubic, fileToBase64DataUrl, fileToDataUrl, findCommonAncestor, findShapeAncestor, fontKey, formatValidationPath, getArcMeasure, getAssetSrc, getBaseZoomForCameraOptions, getColorNamesFromThemes, getColorValue, getCulledShapes, getCurrentPageRenderingShapesSorted, getCurrentPageShapesInReadingOrder, getCursor, getCustomRecordIdType, getDefaultAssetContext, getDefaultCdnBaseUrl, getDefaultCrop, getDefaultDisplayValues, getDefaultUserPresence, getDefaultUserProperties, getDisplayValues, getDroppedShapesToNewParents, getEngineProvider, getExportImplementation, getFocusedGroup, getFocusedGroupId, getFontNamesFromThemes, getFontsFromRichText, getFrameLikeDropTarget, getFreshUserPreferences, getHandleHitRadius, getIncrementedName, getIndicatorSource, getInitialMetaForShape, getLocaleChain, getNearestAdjacentShape, getNotVisibleShapes, getOnlySelectedShapeId, getOverlayDisplayValues, getOwnerDocument, getOwnerWindow, getPageStates, getPaletteEntries, getPerfectDashProps, getPointInArcT, getPointOnCircle, getPointerInfo, getPointsOnArc, getPolygonVertices, getRenderingShapes, getSelectedShapeAtPoint, getSelectionHandlePositions, getSelectionRotatedPageBounds, getSelectionRotatedScreenBounds, getSelectionScreenBounds, getSessionStateSnapshot, getSessionStateSnapshotFromStore, getShapeAndDescendantIds, getShapeClipPath, getShapeHandles, getShapeIdsInsideBounds, getShapeIndicatorNode, getShapeIndicatorPath, getShapeMaskedPageBounds, getShapeStyleIfExists, getShapesPageBounds, getSharedOpacity, getSnapshot, getStylePropsOf, getSvgAsImage, getSvgPathFromPoints, getTextMeasureProvider, getThemeCssVars, getUncroppedSize, getUserPreferences, handleTypeValidator, hardReset, hardResetEditor, hasAncestor, hexToRgba, hitTestSelectionBounds, hitTestSelectionHandles, idValidator, imageAssetMigrations, imageAssetProps, imageAssetPropsValidator, imageAssetValidator, inlineBase64AssetStore, intersectCircleCircle, intersectCirclePolygon, intersectCirclePolyline, intersectLineSegmentCircle, intersectLineSegmentLineSegment, intersectLineSegmentPolygon, intersectLineSegmentPolyline, intersectPolygonBounds, intersectPolygonPolygon, isAncestorSelected, isAsset, isAssetId, isBinding, isBindingId, isCommentId, isCommentReactionId, isCommentThreadId, isCursorInViewport, isCustomRecord, isCustomRecordId, isDocument, isFullCrop, isInstancePresenceId, isOptionalValidator, isPage, isPageId, isPointInShape, isPropsMigrations, isSafeFloat, isShape, isShapeHidden, isShapeId, isShapeInPage, isUserId, isValidProps, kickoutOccludedShapes, lerp, linesIntersect, loadSessionStateSnapshotIntoStore, loadSnapshot, loopToHtmlElement, maybeSnapToGrid, mixHexColors, moveElementInto, moveShapesToPage, normalizeIndicatorPath, normalizeLoadedRecords, noteReactivePointerType, opacityValidator, openWindow, packShapes, pageIdValidator, parentIdValidator, parseDeepLinkString, perimeterOfEllipse, pointInPolygon, pointerValidator, polygonIntersectsPolyline, polygonsIntersect, popFocusedGroupId, precise, prefixError, preventDefault, radiansToDegrees, randomPresenceColor, rangeIntersection, refreshPage, refreshReactiveEnvironment, registerColorsFromThemes, registerCoreShape, registerDefaultAssetSchema, registerDefaultBindingSchema, registerDefaultShapeSchema, registerEngineProvider, registerExportImplementation, registerFontsFromThemes, registerTextMeasureImplementation, releasePointerCapture, resizeBox, resizeScaled, resizeToBounds, resolveAssetUrl, resolveLineHeightPx, resolveShape, resolveThemes, resolveUiMessage, richTextToPlainText, rootBindingMigrations, rootShapeMigrations, rotateSelectionHandle, runtime, sanitizeId, scribbleValidator, selectAdjacentShape, selectFirstChildShape, selectParentShape, setDefaultCdnBaseUrl, setFocusedGroup, setOpacityForNextShapes, setOpacityForSelectedShapes, setPointerCapture, setRuntimeOverrides, setUserPreferences, shapeIdValidator, shapePropsMigrationSequenceId, shortAngleDist, snapAngle, stopEventPropagation, strokeShapeIndicators, suffixSafeId, tleditors, tlenv, tlenvReactive, tlmenus, tltime, toCustomRecordMigrationSequence, toDomPrecision, toFixed, toMigrationSequence, toPrecision, trackPointer, uniq, updatePage, useActions, useAssetUrls, useColorMode, useContainer, useContainerIfExists, useCurrentTheme, useCurrentUser, useDelaySvgExport, useEditor, useEditorComponents, useEditorPortalHost, useGlobalMenuIsOpen, useIsCropping, useIsEditing, useIsToolSelected, useMaybeEditor, useMocanvasUi, usePassThroughWheelEvents, useSharedSafeId, useSvgExportContext, useTLSchemaFromUtils, useTLStore, useThemeColors, useThemeCssVars, useTools, useTransform, useUniqueSafeId, useViewportHeight, userIdValidator, userPreferencesValidator, userTypeValidator, userValidator, validateCustomRecordInfos, validateProps, vecModelValidator, videoAssetMigrations, videoAssetProps, videoAssetValidator, visitDescendants, withCoreShapes };
15676
+ export { ARROWHEAD_KINDS, ARROW_SHAPE_KINDS, ASSET_MIGRATION_SEQUENCE_PREFIX, Arc2d, ArrayOfValidator, ArrowShapeArrowheadEndStyle, ArrowShapeArrowheadStartStyle, ArrowShapeKindStyle, AssetRecordType, AssetUrlsProvider, AssetUtil, AssetUtilRegistry, BINDING_MIGRATION_SEQUENCE_PREFIX, BUILTIN_ASSET_MIGRATION_SEQUENCE_PREFIX, BUILTIN_BINDING_MIGRATION_SEQUENCE_PREFIX, BUILTIN_SHAPE_MIGRATION_SEQUENCE_PREFIX, BaseBoxShapeUtil, BaseFrameLikeShapeUtil, BindingRecordType, BindingUtil, BoundsSnaps, Box, CANVAS_THEME_VARS, COLLABORATOR_INACTIVE_TIMEOUT, CURRENT_SESSION_SCHEMA_VERSION, CURSOR_TYPES, CUSTOM_RECORD_MIGRATION_SEQUENCE_PREFIX, CUSTOM_RECORD_TYPE_NAME, CameraRecordType, CameraStateTracker, Canvas, Circle2d, ClickManager, CollaboratorsManager, CommentReactionRecordType, CommentRecordType, CommentThreadRecordType, ContainerProvider, ContentElementManager, CubicBezier2d, CubicSpline2d, DEFAULT_ASSET_CONTEXT, DEFAULT_CAMERA_OPTIONS, DEFAULT_COLORS, DEFAULT_DARK_COLORS, DEFAULT_DASHES, DEFAULT_EDITOR_CONFIG, DEFAULT_FILLS, DEFAULT_FILL_TOKENS, DEFAULT_FONTS, DEFAULT_FONT_FAMILIES, DEFAULT_H_ALIGNS, DEFAULT_LIGHT_COLORS, DEFAULT_LINE_HEIGHT, DEFAULT_MENU_CONTEXT, DEFAULT_PAGE_ID, DEFAULT_SHAPE_INDICATOR_OPTIONS, DEFAULT_SIZES, DEFAULT_TEXT_ALIGNS, DEFAULT_THEME, DEFAULT_TIME_CONTEXT, DEFAULT_V_ALIGNS, DIM_2D, DIM_3D, DOCUMENT_ID, DefaultBackground, DefaultCanvas, DefaultColorStyle, DefaultCursor, DefaultDashStyle, DefaultErrorFallback, DefaultFillStyle, DefaultFontFaces, DefaultFontFamilies, DefaultFontStyle, DefaultGrid, DefaultHorizontalAlignStyle, DefaultLabelColorStyle, DefaultShapeWrapper, DefaultSizeStyle, DefaultSpinner, DefaultSvgDefs, DefaultTextAlignStyle, DefaultVerticalAlignStyle, DictValidator, DocumentRecordType, EASINGS, ELBOW_ARROW_SNAP_MODES, EVENT_NAME_MAP, Edge2d, EdgeScrollManager, Editor, EditorAtom, EditorContext, EditorManager, EditorPortal, EditorProvider, ElbowArrowSnap, Ellipse2d, EnumStyleProp, ErrorBoundary, ErrorScreen, EventEmitter, FIRST_PAGE_INDEX, FONT_SIZES, FontManager, GEO_SHAPE_KINDS, GeoShapeGeoStyle, Geometry2d, Geometry2dFilters, Group2d, HALF_PI, HANDLE_HIT_RADIUS, HTMLContainer, HandleSnaps, HandleTable, HistoryManager, INSTANCE_ID, ImageShapeCrop, InputsManager, InstancePageStateRecordType, InstancePresenceRecordType, InstanceRecordType, LIGHT_THEME, LINE_SPLINE_KINDS, LOCAL_STATE_PREFIX, LineShapeSplineStyle, LoadingScreen, MAX_TEXTURE_RESOLUTION, MAX_TEXTURE_ZOOM, Mat, MenuClickCapture, MenuManager, MocanvasUiProvider, ObjectValidator, OverlayManager, OverlayUtil, PI, PI2, PRESENCE_COLORS, PageRecordType, PerformanceApiAdapter, PerformanceManager, Point2d, PointerRecordType, Polygon2d, Polyline2d, ROTATE_CORNER_TO_SELECTION_CORNER, ROTATE_HANDLE_OFFSET, ReadonlySharedStyleMap, Rectangle2d, RootState, SHAPE_MIGRATION_SEQUENCE_PREFIX, SIDES, SIN, STROKE_SIZES, SVGContainer, ScribbleManager, ShapeIndicatorCompositor, ShapeRecordType, ShapeUtil, SharedStyleMap, SnapManager, Stadium2d, StateNode, StyleProp, SvgExportContextProvider, T, TAB_ID, TLEditorsRegistry, TLPOINTER_ID, TL_CANVAS_UI_COLOR_TYPES, TL_CURSOR_TYPES, TL_HANDLE_TYPES, TL_SCRIBBLE_STATES, TextManager, TextureManager, ThemeManager, Timers, TransformedGeometry2d, UNKNOWN_EDIT_START_INFO, USER_PREFERENCES_DEFAULTS, UnionValidator, UserPreferencesManager, UserRecordType, ValidationError, Validator, Vec, WebGL2Backend, angleDistance, animateShape, animateShapes, applyThemePatch, approximately, areAnglesCompatible, arrowBindingVersions, assetIdValidator, assetMigrations, assetPropsMigrationSequenceId, assetValidator, assetValidators, average, b64Vecs, bindingIdValidator, bindingPropsMigrationSequenceId, bookmarkAssetMigrations, bookmarkAssetProps, bookmarkAssetPropsValidator, bookmarkAssetValidator, boundsIndicatorPath, boxModelValidator, bucketTextureResolution, canBindShapes, canBuildIndicatorPaths, canCreateShape, canCreateShapes, canCropShape, canEditShape, canonicalizeRotation, canvasUiColorTypeValidator, centerOfCircleFromThreePoints, clamp, clampRadians, clockwiseAngleDist, commentAnchorValidator, commentReactionRecordConfig, commentReactionValidator, commentRecordConfig, commentSchemaRecords, commentThreadRecordConfig, commentThreadValidator, commentValidator, compressLegacySegments, coreShapes, counterClockwiseAngleDist, createAssetId, createAssetPropsMigrationIds, createAssetPropsMigrationSequence, createAssetValidator, createBackend, createBaseBindingValidator, createBaseShapeValidator, createBindingId, createBindingPropsMigrationIds, createBindingPropsMigrationSequence, createBindingValidator, createBuiltInAssetPropsMigrationIds, createBuiltInBindingPropsMigrationIds, createBuiltInShapePropsMigrationIds, createCachedUserResolve, createComment, createCommentId, createCommentReaction, createCommentReactionId, createCommentThread, createCommentThreadId, createCurrentUser, createCustomRecord, createCustomRecordId, createCustomRecordMigrationIds, createCustomRecordMigrationSequence, createCustomRecordMigrationSequences, createCustomRecordType, createCustomRecordTypeMap, createCustomRecordValidator, createDeepLinkString, createInMemoryAssetStore, createMemoryUserStore, createPresenceStateDerivation, createPropsMigrationSequences, createRootState, createSchema, createSessionStateSnapshotSignal, createShapeId, createShapePropsMigrationIds, createShapePropsMigrationSequence, createShapeValidator, createStore, createTLCurrentUser, createTLSchemaFromUtils, createTheme, createUserId, createUserPreferences, createUserRecordType, cursorTypeValidator, cursorValidator, customRecordMigrationSequenceId, dataUrlToFile, decodeDrawSegmentPath, defaultAssetMigrations, defaultAssetSchemas, defaultBindingSchemas, defaultShapeSchemas, defaultTldrawOptions, defaultUserPreferences, defaultUserStore, degreesToRadians, deselect, drawShapeSegmentValidator, dropShapesOnFrameLike, duplicatePage, easeInOutCubic, fileToBase64DataUrl, fileToDataUrl, findCommonAncestor, findShapeAncestor, fontKey, formatValidationPath, getArcMeasure, getAssetSrc, getBaseZoomForCameraOptions, getColorNamesFromThemes, getColorValue, getCulledShapes, getCurrentPageRenderingShapesSorted, getCurrentPageShapesInReadingOrder, getCursor, getCustomRecordIdType, getDefaultAssetContext, getDefaultCdnBaseUrl, getDefaultCrop, getDefaultDisplayValues, getDefaultUserPresence, getDefaultUserProperties, getDisplayValues, getDroppedShapesToNewParents, getEngineProvider, getExportImplementation, getFocusedGroup, getFocusedGroupId, getFontNamesFromThemes, getFontsFromRichText, getFrameLikeDropTarget, getFreshUserPreferences, getHandleHitRadius, getIncrementedName, getIndicatorSource, getInitialMetaForShape, getLocaleChain, getNearestAdjacentShape, getNotVisibleShapes, getOnlySelectedShapeId, getOverlayDisplayValues, getOwnerDocument, getOwnerWindow, getPageStates, getPaletteEntries, getPerfectDashProps, getPointInArcT, getPointOnCircle, getPointerInfo, getPointsOnArc, getPolygonVertices, getRenderingShapes, getSelectedShapeAtPoint, getSelectionHandlePositions, getSelectionRotatedPageBounds, getSelectionRotatedScreenBounds, getSelectionScreenBounds, getSessionStateSnapshot, getSessionStateSnapshotFromStore, getShapeAndDescendantIds, getShapeClipPath, getShapeHandles, getShapeIdsInsideBounds, getShapeIndicatorNode, getShapeIndicatorPath, getShapeMaskedPageBounds, getShapeStyleIfExists, getShapesPageBounds, getSharedOpacity, getSnapshot, getStylePropsOf, getSvgAsImage, getSvgPathFromPoints, getTextMeasureProvider, getThemeCssVars, getUncroppedSize, getUserPreferences, handleTypeValidator, hardReset, hardResetEditor, hasAncestor, hexToRgba, hitTestSelectionBounds, hitTestSelectionHandles, idValidator, imageAssetMigrations, imageAssetProps, imageAssetPropsValidator, imageAssetValidator, inlineBase64AssetStore, intersectCircleCircle, intersectCirclePolygon, intersectCirclePolyline, intersectLineSegmentCircle, intersectLineSegmentLineSegment, intersectLineSegmentPolygon, intersectLineSegmentPolyline, intersectPolygonBounds, intersectPolygonPolygon, isAncestorSelected, isAsset, isAssetId, isBinding, isBindingId, isCommentId, isCommentReactionId, isCommentThreadId, isCursorInViewport, isCustomRecord, isCustomRecordId, isDocument, isFullCrop, isInstancePresenceId, isOptionalValidator, isPage, isPageId, isPointInShape, isPropsMigrations, isSafeFloat, isShape, isShapeHidden, isShapeId, isShapeInPage, isUserId, isValidProps, kickoutOccludedShapes, lerp, linesIntersect, loadSessionStateSnapshotIntoStore, loadSnapshot, loopToHtmlElement, maybeSnapToGrid, mixHexColors, moveElementInto, moveShapesToPage, normalizeIndicatorPath, normalizeLoadedRecords, noteReactivePointerType, opacityValidator, openWindow, packShapes, pageIdValidator, parentIdValidator, parseDeepLinkString, perimeterOfEllipse, pointInPolygon, pointerValidator, polygonIntersectsPolyline, polygonsIntersect, popFocusedGroupId, precise, prefixError, preventDefault, radiansToDegrees, randomPresenceColor, rangeIntersection, refreshPage, refreshReactiveEnvironment, registerColorsFromThemes, registerCoreShape, registerDefaultAssetSchema, registerDefaultBindingSchema, registerDefaultShapeSchema, registerEngineProvider, registerExportImplementation, registerFontsFromThemes, registerTextMeasureImplementation, releasePointerCapture, resizeBox, resizeScaled, resizeToBounds, resolveAssetUrl, resolveLineHeightPx, resolveShape, resolveThemes, resolveUiMessage, richTextToPlainText, rootBindingMigrations, rootShapeMigrations, rotateSelectionHandle, runtime, sanitizeId, scribbleValidator, selectAdjacentShape, selectFirstChildShape, selectParentShape, setDefaultCdnBaseUrl, setFocusedGroup, setOpacityForNextShapes, setOpacityForSelectedShapes, setPointerCapture, setRuntimeOverrides, setUserPreferences, shapeIdValidator, shapePropsMigrationSequenceId, shortAngleDist, snapAngle, stopEventPropagation, strokeShapeIndicators, suffixSafeId, tleditors, tlenv, tlenvReactive, tlmenus, tltime, toCustomRecordMigrationSequence, toDomPrecision, toFixed, toMigrationSequence, toPrecision, trackPointer, uniq, updatePage, useActions, useAssetUrls, useColorMode, useContainer, useContainerIfExists, useCurrentTheme, useCurrentUser, useDelaySvgExport, useEditor, useEditorComponents, useEditorPortalHost, useGlobalMenuIsOpen, useIsCropping, useIsEditing, useIsToolSelected, useMaybeEditor, useMocanvasUi, usePassThroughWheelEvents, useSharedSafeId, useSvgExportContext, useTLSchemaFromUtils, useTLStore, useThemeColors, useThemeCssVars, useTools, useTransform, useUniqueSafeId, useViewportHeight, userIdValidator, userPreferencesValidator, userTypeValidator, userValidator, validateCustomRecordInfos, validateProps, vecModelValidator, videoAssetMigrations, videoAssetProps, videoAssetValidator, visitDescendants, warnOnce, withCoreShapes };
15422
15677
  //# sourceMappingURL=index.js.map
15423
15678
  //# sourceMappingURL=index.js.map