@tldraw/editor 4.6.0-next.0eb36d65eec3 → 4.6.0-next.20de11b7e238

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist-cjs/index.d.ts +60 -18
  2. package/dist-cjs/index.js +5 -4
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +4 -2
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/config/{createTLUser.js → createTLCurrentUser.js} +9 -9
  7. package/dist-cjs/lib/config/createTLCurrentUser.js.map +7 -0
  8. package/dist-cjs/lib/config/createTLStore.js +23 -0
  9. package/dist-cjs/lib/config/createTLStore.js.map +2 -2
  10. package/dist-cjs/lib/editor/Editor.js +111 -4
  11. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  12. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +1 -1
  13. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +10 -0
  14. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  15. package/dist-cjs/lib/editor/types/clipboard-types.js.map +1 -1
  16. package/dist-cjs/lib/hooks/useGestureEvents.js +171 -127
  17. package/dist-cjs/lib/hooks/useGestureEvents.js.map +3 -3
  18. package/dist-cjs/version.js +3 -3
  19. package/dist-cjs/version.js.map +1 -1
  20. package/dist-esm/index.d.mts +60 -18
  21. package/dist-esm/index.mjs +9 -4
  22. package/dist-esm/index.mjs.map +2 -2
  23. package/dist-esm/lib/TldrawEditor.mjs +4 -2
  24. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  25. package/dist-esm/lib/config/{createTLUser.mjs → createTLCurrentUser.mjs} +6 -6
  26. package/dist-esm/lib/config/createTLCurrentUser.mjs.map +7 -0
  27. package/dist-esm/lib/config/createTLStore.mjs +27 -1
  28. package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
  29. package/dist-esm/lib/editor/Editor.mjs +113 -4
  30. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  31. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +1 -1
  32. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +10 -0
  33. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  34. package/dist-esm/lib/hooks/useGestureEvents.mjs +171 -127
  35. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +3 -3
  36. package/dist-esm/version.mjs +3 -3
  37. package/dist-esm/version.mjs.map +1 -1
  38. package/editor.css +13 -0
  39. package/package.json +8 -9
  40. package/src/index.ts +6 -1
  41. package/src/lib/TldrawEditor.tsx +8 -6
  42. package/src/lib/config/{createTLUser.ts → createTLCurrentUser.ts} +6 -6
  43. package/src/lib/config/createTLStore.ts +35 -1
  44. package/src/lib/editor/Editor.ts +140 -3
  45. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +2 -2
  46. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +2 -2
  47. package/src/lib/editor/shapes/ShapeUtil.ts +11 -0
  48. package/src/lib/editor/types/clipboard-types.ts +2 -1
  49. package/src/lib/hooks/useGestureEvents.ts +240 -168
  50. package/src/lib/primitives/Box.test.ts +30 -0
  51. package/src/lib/primitives/geometry/Geometry2d.test.ts +21 -0
  52. package/src/version.ts +3 -3
  53. package/dist-cjs/lib/config/createTLUser.js.map +0 -7
  54. package/dist-esm/lib/config/createTLUser.mjs.map +0 -7
@@ -20,9 +20,11 @@ var createTLStore_exports = {};
20
20
  __export(createTLStore_exports, {
21
21
  createTLSchemaFromUtils: () => createTLSchemaFromUtils,
22
22
  createTLStore: () => createTLStore,
23
+ defaultUserStore: () => defaultUserStore,
23
24
  inlineBase64AssetStore: () => inlineBase64AssetStore
24
25
  });
25
26
  module.exports = __toCommonJS(createTLStore_exports);
27
+ var import_state = require("@tldraw/state");
26
28
  var import_store = require("@tldraw/store");
27
29
  var import_tlschema = require("@tldraw/tlschema");
28
30
  var import_utils = require("@tldraw/utils");
@@ -30,7 +32,20 @@ var import_Editor = require("../editor/Editor");
30
32
  var import_defaultBindings = require("./defaultBindings");
31
33
  var import_defaultShapes = require("./defaultShapes");
32
34
  var import_TLEditorSnapshot = require("./TLEditorSnapshot");
35
+ var import_TLUserPreferences = require("./TLUserPreferences");
33
36
  const defaultAssetResolve = (asset) => asset.props.src;
37
+ const _defaultCurrentUser = (0, import_state.computed)("defaultCurrentUser", () => {
38
+ const prefs = (0, import_TLUserPreferences.getUserPreferences)();
39
+ if (!prefs.id) return null;
40
+ return import_tlschema.UserRecordType.create({
41
+ id: (0, import_tlschema.createUserId)(prefs.id),
42
+ name: prefs.name ?? "",
43
+ color: prefs.color ?? import_TLUserPreferences.defaultUserPreferences.color
44
+ });
45
+ });
46
+ const defaultUserStore = {
47
+ currentUser: _defaultCurrentUser
48
+ };
34
49
  const inlineBase64AssetStore = {
35
50
  upload: async (_, file) => {
36
51
  return { src: await import_utils.FileHelpers.blobToDataUrl(file) };
@@ -50,6 +65,7 @@ function createTLStore({
50
65
  defaultName = "",
51
66
  id,
52
67
  assets = inlineBase64AssetStore,
68
+ users = defaultUserStore,
53
69
  onMount,
54
70
  collaboration,
55
71
  ...rest
@@ -66,6 +82,13 @@ function createTLStore({
66
82
  resolve: assets.resolve ?? defaultAssetResolve,
67
83
  remove: assets.remove ?? (() => Promise.resolve())
68
84
  },
85
+ users: {
86
+ currentUser: users.currentUser,
87
+ resolve: users.resolve ?? (0, import_tlschema.createCachedUserResolve)((userId) => {
88
+ const current = users.currentUser.get();
89
+ return current && current.id === (0, import_tlschema.createUserId)(userId) ? current : null;
90
+ })
91
+ },
69
92
  onMount: (editor) => {
70
93
  (0, import_utils.assert)(editor instanceof import_Editor.Editor);
71
94
  onMount?.(editor);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/config/createTLStore.ts"],
4
- "sourcesContent": ["import { Signal } from '@tldraw/state'\nimport { HistoryEntry, MigrationSequence, SerializedStore, Store, StoreSchema } from '@tldraw/store'\nimport {\n\tCustomRecordInfo,\n\tSchemaPropsInfo,\n\tTLAssetStore,\n\tTLRecord,\n\tTLStore,\n\tTLStoreProps,\n\tTLStoreSnapshot,\n\tcreateTLSchema,\n} from '@tldraw/tlschema'\nimport { FileHelpers, assert } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { TLAnyBindingUtilConstructor, checkBindings } from './defaultBindings'\nimport { TLAnyShapeUtilConstructor, checkShapesAndAddCore } from './defaultShapes'\nimport { TLEditorSnapshot, loadSnapshot } from './TLEditorSnapshot'\n\n/** @public */\nexport interface TLStoreBaseOptions {\n\t/** The initial data for the store. */\n\tinitialData?: SerializedStore<TLRecord>\n\n\t/** A snapshot of initial data to migrate and load into the store. */\n\tsnapshot?: Partial<TLEditorSnapshot> | TLStoreSnapshot\n\n\t/** The default name for the store. */\n\tdefaultName?: string\n\n\t/** How should this store upload & resolve assets? */\n\tassets?: TLAssetStore\n\n\t/** Called when the store is connected to an {@link @tldraw/editor#Editor}. */\n\tonMount?(editor: Editor): void | (() => void)\n}\n\n/** @public */\nexport type TLStoreSchemaOptions =\n\t| {\n\t\t\tschema?: StoreSchema<TLRecord, TLStoreProps>\n\t }\n\t| {\n\t\t\tshapeUtils?: readonly TLAnyShapeUtilConstructor[]\n\t\t\tmigrations?: readonly MigrationSequence[]\n\t\t\tbindingUtils?: readonly TLAnyBindingUtilConstructor[]\n\t\t\trecords?: Record<string, CustomRecordInfo>\n\t }\n\n/** @public */\nexport type TLStoreOptions = TLStoreBaseOptions & {\n\tid?: string\n\t/** Collaboration options for the store. */\n\tcollaboration?: {\n\t\tstatus: Signal<'online' | 'offline'> | null\n\t\tmode?: Signal<'readonly' | 'readwrite'> | null\n\t}\n} & TLStoreSchemaOptions\n\n/** @public */\nexport type TLStoreEventInfo = HistoryEntry<TLRecord>\n\nconst defaultAssetResolve: NonNullable<TLAssetStore['resolve']> = (asset) => asset.props.src\n\n/** @public */\nexport const inlineBase64AssetStore: TLAssetStore = {\n\tupload: async (_, file) => {\n\t\treturn { src: await FileHelpers.blobToDataUrl(file) }\n\t},\n}\n\n/**\n * A helper for creating a TLStore schema from either an object with shapeUtils, bindingUtils, and\n * migrations, or a schema.\n *\n * @param opts - Options for creating the schema.\n *\n * @public\n */\nexport function createTLSchemaFromUtils(\n\topts: TLStoreSchemaOptions\n): StoreSchema<TLRecord, TLStoreProps> {\n\tif ('schema' in opts && opts.schema) return opts.schema\n\n\treturn createTLSchema({\n\t\tshapes:\n\t\t\t'shapeUtils' in opts && opts.shapeUtils\n\t\t\t\t? utilsToMap(checkShapesAndAddCore(opts.shapeUtils))\n\t\t\t\t: undefined,\n\t\tbindings:\n\t\t\t'bindingUtils' in opts && opts.bindingUtils\n\t\t\t\t? utilsToMap(checkBindings(opts.bindingUtils))\n\t\t\t\t: undefined,\n\t\trecords: 'records' in opts ? opts.records : undefined,\n\t\tmigrations: 'migrations' in opts ? opts.migrations : undefined,\n\t})\n}\n\n/**\n * A helper for creating a TLStore.\n *\n * @param opts - Options for creating the store.\n *\n * @public\n */\nexport function createTLStore({\n\tinitialData,\n\tdefaultName = '',\n\tid,\n\tassets = inlineBase64AssetStore,\n\tonMount,\n\tcollaboration,\n\t...rest\n}: TLStoreOptions = {}): TLStore {\n\tconst schema = createTLSchemaFromUtils(rest)\n\n\tconst store = new Store({\n\t\tid,\n\t\tschema,\n\t\tinitialData,\n\t\tprops: {\n\t\t\tdefaultName,\n\t\t\tassets: {\n\t\t\t\tupload: assets.upload,\n\t\t\t\tresolve: assets.resolve ?? defaultAssetResolve,\n\t\t\t\tremove: assets.remove ?? (() => Promise.resolve()),\n\t\t\t},\n\t\t\tonMount: (editor) => {\n\t\t\t\tassert(editor instanceof Editor)\n\t\t\t\tonMount?.(editor)\n\t\t\t},\n\t\t\tcollaboration,\n\t\t},\n\t})\n\n\tif (rest.snapshot) {\n\t\tif (initialData) throw new Error('Cannot provide both initialData and snapshot')\n\t\tloadSnapshot(store, rest.snapshot, { forceOverwriteSessionState: true })\n\t}\n\n\treturn store\n}\n\nfunction utilsToMap<T extends SchemaPropsInfo & { type: string }>(utils: T[]) {\n\treturn Object.fromEntries(\n\t\tutils.map((s): [string, SchemaPropsInfo] => [\n\t\t\ts.type,\n\t\t\t{\n\t\t\t\tprops: s.props,\n\t\t\t\tmigrations: s.migrations,\n\t\t\t},\n\t\t])\n\t)\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAqF;AACrF,sBASO;AACP,mBAAoC;AACpC,oBAAuB;AACvB,6BAA2D;AAC3D,2BAAiE;AACjE,8BAA+C;AA6C/C,MAAM,sBAA4D,CAAC,UAAU,MAAM,MAAM;AAGlF,MAAM,yBAAuC;AAAA,EACnD,QAAQ,OAAO,GAAG,SAAS;AAC1B,WAAO,EAAE,KAAK,MAAM,yBAAY,cAAc,IAAI,EAAE;AAAA,EACrD;AACD;AAUO,SAAS,wBACf,MACsC;AACtC,MAAI,YAAY,QAAQ,KAAK,OAAQ,QAAO,KAAK;AAEjD,aAAO,gCAAe;AAAA,IACrB,QACC,gBAAgB,QAAQ,KAAK,aAC1B,eAAW,4CAAsB,KAAK,UAAU,CAAC,IACjD;AAAA,IACJ,UACC,kBAAkB,QAAQ,KAAK,eAC5B,eAAW,sCAAc,KAAK,YAAY,CAAC,IAC3C;AAAA,IACJ,SAAS,aAAa,OAAO,KAAK,UAAU;AAAA,IAC5C,YAAY,gBAAgB,OAAO,KAAK,aAAa;AAAA,EACtD,CAAC;AACF;AASO,SAAS,cAAc;AAAA,EAC7B;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA,GAAG;AACJ,IAAoB,CAAC,GAAY;AAChC,QAAM,SAAS,wBAAwB,IAAI;AAE3C,QAAM,QAAQ,IAAI,mBAAM;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,MACN;AAAA,MACA,QAAQ;AAAA,QACP,QAAQ,OAAO;AAAA,QACf,SAAS,OAAO,WAAW;AAAA,QAC3B,QAAQ,OAAO,WAAW,MAAM,QAAQ,QAAQ;AAAA,MACjD;AAAA,MACA,SAAS,CAAC,WAAW;AACpB,iCAAO,kBAAkB,oBAAM;AAC/B,kBAAU,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,IACD;AAAA,EACD,CAAC;AAED,MAAI,KAAK,UAAU;AAClB,QAAI,YAAa,OAAM,IAAI,MAAM,8CAA8C;AAC/E,8CAAa,OAAO,KAAK,UAAU,EAAE,4BAA4B,KAAK,CAAC;AAAA,EACxE;AAEA,SAAO;AACR;AAEA,SAAS,WAAyD,OAAY;AAC7E,SAAO,OAAO;AAAA,IACb,MAAM,IAAI,CAAC,MAAiC;AAAA,MAC3C,EAAE;AAAA,MACF;AAAA,QACC,OAAO,EAAE;AAAA,QACT,YAAY,EAAE;AAAA,MACf;AAAA,IACD,CAAC;AAAA,EACF;AACD;",
4
+ "sourcesContent": ["import { Signal, computed } from '@tldraw/state'\nimport { HistoryEntry, MigrationSequence, SerializedStore, Store, StoreSchema } from '@tldraw/store'\nimport {\n\tCustomRecordInfo,\n\tSchemaPropsInfo,\n\tTLAssetStore,\n\tTLRecord,\n\tTLStore,\n\tTLStoreProps,\n\tTLStoreSnapshot,\n\tTLUser,\n\tTLUserStore,\n\tUserRecordType,\n\tcreateCachedUserResolve,\n\tcreateTLSchema,\n\tcreateUserId,\n} from '@tldraw/tlschema'\nimport { FileHelpers, assert } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { TLAnyBindingUtilConstructor, checkBindings } from './defaultBindings'\nimport { TLAnyShapeUtilConstructor, checkShapesAndAddCore } from './defaultShapes'\nimport { TLEditorSnapshot, loadSnapshot } from './TLEditorSnapshot'\nimport { defaultUserPreferences, getUserPreferences } from './TLUserPreferences'\n\n/** @public */\nexport interface TLStoreBaseOptions {\n\t/** The initial data for the store. */\n\tinitialData?: SerializedStore<TLRecord>\n\n\t/** A snapshot of initial data to migrate and load into the store. */\n\tsnapshot?: Partial<TLEditorSnapshot> | TLStoreSnapshot\n\n\t/** The default name for the store. */\n\tdefaultName?: string\n\n\t/** How should this store upload & resolve assets? */\n\tassets?: TLAssetStore\n\n\t/** How should this store resolve users for attribution? */\n\tusers?: TLUserStore\n\n\t/** Called when the store is connected to an {@link @tldraw/editor#Editor}. */\n\tonMount?(editor: Editor): void | (() => void)\n}\n\n/** @public */\nexport type TLStoreSchemaOptions =\n\t| {\n\t\t\tschema?: StoreSchema<TLRecord, TLStoreProps>\n\t }\n\t| {\n\t\t\tshapeUtils?: readonly TLAnyShapeUtilConstructor[]\n\t\t\tmigrations?: readonly MigrationSequence[]\n\t\t\tbindingUtils?: readonly TLAnyBindingUtilConstructor[]\n\t\t\trecords?: Record<string, CustomRecordInfo>\n\t }\n\n/** @public */\nexport type TLStoreOptions = TLStoreBaseOptions & {\n\tid?: string\n\t/** Collaboration options for the store. */\n\tcollaboration?: {\n\t\tstatus: Signal<'online' | 'offline'> | null\n\t\tmode?: Signal<'readonly' | 'readwrite'> | null\n\t}\n} & TLStoreSchemaOptions\n\n/** @public */\nexport type TLStoreEventInfo = HistoryEntry<TLRecord>\n\nconst defaultAssetResolve: NonNullable<TLAssetStore['resolve']> = (asset) => asset.props.src\n\nconst _defaultCurrentUser: Signal<TLUser | null> = computed('defaultCurrentUser', () => {\n\tconst prefs = getUserPreferences()\n\tif (!prefs.id) return null\n\treturn UserRecordType.create({\n\t\tid: createUserId(prefs.id),\n\t\tname: prefs.name ?? '',\n\t\tcolor: prefs.color ?? defaultUserPreferences.color,\n\t})\n})\n\n/** @public */\nexport const defaultUserStore: TLUserStore = {\n\tcurrentUser: _defaultCurrentUser,\n}\n\n/** @public */\nexport const inlineBase64AssetStore: TLAssetStore = {\n\tupload: async (_, file) => {\n\t\treturn { src: await FileHelpers.blobToDataUrl(file) }\n\t},\n}\n\n/**\n * A helper for creating a TLStore schema from either an object with shapeUtils, bindingUtils, and\n * migrations, or a schema.\n *\n * @param opts - Options for creating the schema.\n *\n * @public\n */\nexport function createTLSchemaFromUtils(\n\topts: TLStoreSchemaOptions\n): StoreSchema<TLRecord, TLStoreProps> {\n\tif ('schema' in opts && opts.schema) return opts.schema\n\n\treturn createTLSchema({\n\t\tshapes:\n\t\t\t'shapeUtils' in opts && opts.shapeUtils\n\t\t\t\t? utilsToMap(checkShapesAndAddCore(opts.shapeUtils))\n\t\t\t\t: undefined,\n\t\tbindings:\n\t\t\t'bindingUtils' in opts && opts.bindingUtils\n\t\t\t\t? utilsToMap(checkBindings(opts.bindingUtils))\n\t\t\t\t: undefined,\n\t\trecords: 'records' in opts ? opts.records : undefined,\n\t\tmigrations: 'migrations' in opts ? opts.migrations : undefined,\n\t})\n}\n\n/**\n * A helper for creating a TLStore.\n *\n * @param opts - Options for creating the store.\n *\n * @public\n */\nexport function createTLStore({\n\tinitialData,\n\tdefaultName = '',\n\tid,\n\tassets = inlineBase64AssetStore,\n\tusers = defaultUserStore,\n\tonMount,\n\tcollaboration,\n\t...rest\n}: TLStoreOptions = {}): TLStore {\n\tconst schema = createTLSchemaFromUtils(rest)\n\n\tconst store = new Store({\n\t\tid,\n\t\tschema,\n\t\tinitialData,\n\t\tprops: {\n\t\t\tdefaultName,\n\t\t\tassets: {\n\t\t\t\tupload: assets.upload,\n\t\t\t\tresolve: assets.resolve ?? defaultAssetResolve,\n\t\t\t\tremove: assets.remove ?? (() => Promise.resolve()),\n\t\t\t},\n\t\t\tusers: {\n\t\t\t\tcurrentUser: users.currentUser,\n\t\t\t\tresolve:\n\t\t\t\t\tusers.resolve ??\n\t\t\t\t\tcreateCachedUserResolve((userId) => {\n\t\t\t\t\t\tconst current = users.currentUser.get()\n\t\t\t\t\t\treturn current && current.id === createUserId(userId) ? current : null\n\t\t\t\t\t}),\n\t\t\t},\n\t\t\tonMount: (editor) => {\n\t\t\t\tassert(editor instanceof Editor)\n\t\t\t\tonMount?.(editor)\n\t\t\t},\n\t\t\tcollaboration,\n\t\t},\n\t})\n\n\tif (rest.snapshot) {\n\t\tif (initialData) throw new Error('Cannot provide both initialData and snapshot')\n\t\tloadSnapshot(store, rest.snapshot, { forceOverwriteSessionState: true })\n\t}\n\n\treturn store\n}\n\nfunction utilsToMap<T extends SchemaPropsInfo & { type: string }>(utils: T[]) {\n\treturn Object.fromEntries(\n\t\tutils.map((s): [string, SchemaPropsInfo] => [\n\t\t\ts.type,\n\t\t\t{\n\t\t\t\tprops: s.props,\n\t\t\t\tmigrations: s.migrations,\n\t\t\t},\n\t\t])\n\t)\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAiC;AACjC,mBAAqF;AACrF,sBAcO;AACP,mBAAoC;AACpC,oBAAuB;AACvB,6BAA2D;AAC3D,2BAAiE;AACjE,8BAA+C;AAC/C,+BAA2D;AAgD3D,MAAM,sBAA4D,CAAC,UAAU,MAAM,MAAM;AAEzF,MAAM,0BAA6C,uBAAS,sBAAsB,MAAM;AACvF,QAAM,YAAQ,6CAAmB;AACjC,MAAI,CAAC,MAAM,GAAI,QAAO;AACtB,SAAO,+BAAe,OAAO;AAAA,IAC5B,QAAI,8BAAa,MAAM,EAAE;AAAA,IACzB,MAAM,MAAM,QAAQ;AAAA,IACpB,OAAO,MAAM,SAAS,gDAAuB;AAAA,EAC9C,CAAC;AACF,CAAC;AAGM,MAAM,mBAAgC;AAAA,EAC5C,aAAa;AACd;AAGO,MAAM,yBAAuC;AAAA,EACnD,QAAQ,OAAO,GAAG,SAAS;AAC1B,WAAO,EAAE,KAAK,MAAM,yBAAY,cAAc,IAAI,EAAE;AAAA,EACrD;AACD;AAUO,SAAS,wBACf,MACsC;AACtC,MAAI,YAAY,QAAQ,KAAK,OAAQ,QAAO,KAAK;AAEjD,aAAO,gCAAe;AAAA,IACrB,QACC,gBAAgB,QAAQ,KAAK,aAC1B,eAAW,4CAAsB,KAAK,UAAU,CAAC,IACjD;AAAA,IACJ,UACC,kBAAkB,QAAQ,KAAK,eAC5B,eAAW,sCAAc,KAAK,YAAY,CAAC,IAC3C;AAAA,IACJ,SAAS,aAAa,OAAO,KAAK,UAAU;AAAA,IAC5C,YAAY,gBAAgB,OAAO,KAAK,aAAa;AAAA,EACtD,CAAC;AACF;AASO,SAAS,cAAc;AAAA,EAC7B;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,SAAS;AAAA,EACT,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,GAAG;AACJ,IAAoB,CAAC,GAAY;AAChC,QAAM,SAAS,wBAAwB,IAAI;AAE3C,QAAM,QAAQ,IAAI,mBAAM;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,MACN;AAAA,MACA,QAAQ;AAAA,QACP,QAAQ,OAAO;AAAA,QACf,SAAS,OAAO,WAAW;AAAA,QAC3B,QAAQ,OAAO,WAAW,MAAM,QAAQ,QAAQ;AAAA,MACjD;AAAA,MACA,OAAO;AAAA,QACN,aAAa,MAAM;AAAA,QACnB,SACC,MAAM,eACN,yCAAwB,CAAC,WAAW;AACnC,gBAAM,UAAU,MAAM,YAAY,IAAI;AACtC,iBAAO,WAAW,QAAQ,WAAO,8BAAa,MAAM,IAAI,UAAU;AAAA,QACnE,CAAC;AAAA,MACH;AAAA,MACA,SAAS,CAAC,WAAW;AACpB,iCAAO,kBAAkB,oBAAM;AAC/B,kBAAU,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,IACD;AAAA,EACD,CAAC;AAED,MAAI,KAAK,UAAU;AAClB,QAAI,YAAa,OAAM,IAAI,MAAM,8CAA8C;AAC/E,8CAAa,OAAO,KAAK,UAAU,EAAE,4BAA4B,KAAK,CAAC;AAAA,EACxE;AAEA,SAAO;AACR;AAEA,SAAS,WAAyD,OAAY;AAC7E,SAAO,OAAO;AAAA,IACb,MAAM,IAAI,CAAC,MAAiC;AAAA,MAC3C,EAAE;AAAA,MACF;AAAA,QACC,OAAO,EAAE;AAAA,QACT,YAAY,EAAE;AAAA,MACf;AAAA,IACD,CAAC;AAAA,EACF;AACD;",
6
6
  "names": []
7
7
  }
@@ -44,7 +44,7 @@ var import_store = require("@tldraw/store");
44
44
  var import_tlschema = require("@tldraw/tlschema");
45
45
  var import_utils = require("@tldraw/utils");
46
46
  var import_eventemitter3 = __toESM(require("eventemitter3"), 1);
47
- var import_createTLUser = require("../config/createTLUser");
47
+ var import_createTLCurrentUser = require("../config/createTLCurrentUser");
48
48
  var import_defaultBindings = require("../config/defaultBindings");
49
49
  var import_defaultShapes = require("../config/defaultShapes");
50
50
  var import_TLEditorSnapshot = require("../config/TLEditorSnapshot");
@@ -132,13 +132,17 @@ class Editor extends import_eventemitter3.default {
132
132
  ...options?.camera
133
133
  });
134
134
  this._textOptions = (0, import_state.atom)("text options", options?.text ?? null);
135
- this.user = new import_UserPreferencesManager.UserPreferencesManager(user ?? (0, import_createTLUser.createTLUser)(), inferDarkMode ?? false);
135
+ this.user = new import_UserPreferencesManager.UserPreferencesManager(user ?? (0, import_createTLCurrentUser.createTLCurrentUser)(), inferDarkMode ?? false);
136
136
  this.disposables.add(() => this.user.dispose());
137
137
  this.getContainer = getContainer;
138
138
  this.textMeasure = new import_TextManager.TextManager(this);
139
139
  this.disposables.add(() => this.textMeasure.dispose());
140
140
  this.fonts = new import_FontManager.FontManager(this, fontAssetUrls);
141
141
  this._tickManager = new import_TickManager.TickManager(this);
142
+ this.disposables.add(() => {
143
+ this.off("tick", this._decayCameraStateTimeout);
144
+ this._setCameraState("idle");
145
+ });
142
146
  this.inputs = new import_InputsManager.InputsManager(this);
143
147
  class NewRoot extends import_RootState.RootState {
144
148
  static initial = initialState ?? "";
@@ -515,6 +519,14 @@ class Editor extends import_eventemitter3.default {
515
519
  })
516
520
  );
517
521
  }
522
+ this.disposables.add(
523
+ (0, import_state.react)("sync current user record", () => {
524
+ const user2 = this.store.props.users.currentUser.get();
525
+ if (user2) {
526
+ this._ensureUserRecord(user2);
527
+ }
528
+ })
529
+ );
518
530
  }
519
531
  _getShapeVisibility;
520
532
  getIsShapeHiddenCache() {
@@ -2821,6 +2833,76 @@ class Editor extends import_eventemitter3.default {
2821
2833
  const currentPageId = this.getCurrentPageId();
2822
2834
  return this.getCollaborators().filter((c) => c.currentPageId === currentPageId);
2823
2835
  }
2836
+ // Attribution
2837
+ /**
2838
+ * Get the current user's ID for attribution purposes.
2839
+ * Also ensures a `user:` record exists in the store for the current user.
2840
+ * Returns `null` when the user store has no current user.
2841
+ *
2842
+ * @public
2843
+ */
2844
+ getAttributionUserId() {
2845
+ const user = this.store.props.users.currentUser.get();
2846
+ if (!user) return null;
2847
+ this._ensureUserRecord(user);
2848
+ return import_tlschema.UserRecordType.parseId(user.id);
2849
+ }
2850
+ /**
2851
+ * Ensure a user record exists in the store for the given user,
2852
+ * updating it if the data has changed.
2853
+ *
2854
+ * @internal
2855
+ */
2856
+ _ensureUserRecord(user) {
2857
+ const existing = this.store.get(user.id);
2858
+ if (existing && existing.name === user.name && existing.color === user.color && existing.imageUrl === user.imageUrl && existing.meta === user.meta) {
2859
+ return;
2860
+ }
2861
+ this.run(
2862
+ () => {
2863
+ this.store.put([user]);
2864
+ },
2865
+ { history: "ignore" }
2866
+ );
2867
+ }
2868
+ /**
2869
+ * Resolve a display name for a user ID. Asks the
2870
+ * {@link @tldraw/tlschema#TLUserStore} first (the app's source of truth),
2871
+ * falling back to the `user:` record in the store.
2872
+ *
2873
+ * @public
2874
+ */
2875
+ getAttributionDisplayName(userId) {
2876
+ if (!userId) return null;
2877
+ return this.store.props.users.resolve(userId).get()?.name ?? this.store.get((0, import_tlschema.createUserId)(userId))?.name ?? null;
2878
+ }
2879
+ /**
2880
+ * Resolve a user record by ID. Asks the
2881
+ * {@link @tldraw/tlschema#TLUserStore} first (the app's source of truth),
2882
+ * falling back to the `user:` record in the store.
2883
+ *
2884
+ * @public
2885
+ */
2886
+ getAttributionUser(userId) {
2887
+ if (!userId) return null;
2888
+ return this.store.props.users.resolve(userId).get() ?? this.store.get((0, import_tlschema.createUserId)(userId)) ?? null;
2889
+ }
2890
+ /**
2891
+ * Collect user IDs referenced by a set of shapes via shape-specific props
2892
+ * (e.g. `textFirstEditedBy` on notes).
2893
+ *
2894
+ * @internal
2895
+ */
2896
+ _getReferencedUserIds(shapes) {
2897
+ const userIds = /* @__PURE__ */ new Set();
2898
+ for (const shape of shapes) {
2899
+ const util = this.getShapeUtil(shape);
2900
+ for (const id of util.getReferencedUserIds(shape)) {
2901
+ userIds.add(id);
2902
+ }
2903
+ }
2904
+ return userIds;
2905
+ }
2824
2906
  // Following
2825
2907
  // When we are 'locked on' to a user, our camera is derived from their camera.
2826
2908
  _isLockedOnFollowingUser = (0, import_state.atom)("isLockedOnFollowingUser", false);
@@ -6534,12 +6616,22 @@ class Editor extends import_eventemitter3.default {
6534
6616
  if (!asset) continue;
6535
6617
  assets.push(asset);
6536
6618
  }
6619
+ const users = [];
6620
+ const seenUserIds = /* @__PURE__ */ new Set();
6621
+ for (const userId of this._getReferencedUserIds(shapes2)) {
6622
+ const recordId = (0, import_tlschema.createUserId)(userId);
6623
+ if (seenUserIds.has(recordId)) continue;
6624
+ seenUserIds.add(recordId);
6625
+ const user = this.store.get(recordId);
6626
+ if (user) users.push(user);
6627
+ }
6537
6628
  return {
6538
6629
  schema: this.store.schema.serialize(),
6539
6630
  shapes: shapes2,
6540
6631
  rootShapeIds,
6541
6632
  bindings,
6542
- assets
6633
+ assets,
6634
+ users
6543
6635
  };
6544
6636
  });
6545
6637
  }
@@ -6589,13 +6681,15 @@ class Editor extends import_eventemitter3.default {
6589
6681
  const assets = [];
6590
6682
  const shapes = [];
6591
6683
  const bindings = [];
6684
+ const users = [];
6592
6685
  const store = {
6593
6686
  store: {
6594
6687
  ...Object.fromEntries(content.assets.map((asset) => [asset.id, asset])),
6595
6688
  ...Object.fromEntries(content.shapes.map((shape) => [shape.id, shape])),
6596
6689
  ...Object.fromEntries(
6597
6690
  content.bindings?.map((bindings2) => [bindings2.id, bindings2]) ?? []
6598
- )
6691
+ ),
6692
+ ...Object.fromEntries(content.users?.map((user) => [user.id, user]) ?? [])
6599
6693
  },
6600
6694
  schema: content.schema
6601
6695
  };
@@ -6617,6 +6711,19 @@ class Editor extends import_eventemitter3.default {
6617
6711
  bindings.push(record);
6618
6712
  break;
6619
6713
  }
6714
+ case "user": {
6715
+ users.push(record);
6716
+ break;
6717
+ }
6718
+ }
6719
+ }
6720
+ if (users.length > 0) {
6721
+ const existingUserIds = new Set(
6722
+ this.store.allRecords().filter((r) => r.typeName === "user").map((r) => r.id)
6723
+ );
6724
+ const usersToCreate = users.filter((u) => !existingUserIds.has(u.id));
6725
+ if (usersToCreate.length > 0) {
6726
+ this.store.put(usersToCreate);
6620
6727
  }
6621
6728
  }
6622
6729
  const shapeIdMap = new Map(