@tldraw/sync 3.16.0-next.fe14f1b4181f → 4.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-cjs/index.js
CHANGED
|
@@ -29,7 +29,7 @@ var import_useSync = require("./useSync");
|
|
|
29
29
|
var import_useSyncDemo = require("./useSyncDemo");
|
|
30
30
|
(0, import_utils.registerTldrawLibraryVersion)(
|
|
31
31
|
"@tldraw/sync",
|
|
32
|
-
"
|
|
32
|
+
"4.0.0",
|
|
33
33
|
"cjs"
|
|
34
34
|
);
|
|
35
35
|
//# sourceMappingURL=index.js.map
|
package/dist-cjs/useSyncDemo.js
CHANGED
|
@@ -31,7 +31,7 @@ function getEnv(cb) {
|
|
|
31
31
|
return void 0;
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
const DEMO_WORKER = getEnv(() => "https://
|
|
34
|
+
const DEMO_WORKER = getEnv(() => "https://demo.tldraw.xyz") ?? "https://demo.tldraw.xyz";
|
|
35
35
|
const IMAGE_WORKER = getEnv(() => process.env.TLDRAW_IMAGE_URL) ?? "https://images.tldraw.xyz";
|
|
36
36
|
function useSyncDemo(options) {
|
|
37
37
|
const { roomId, host = DEMO_WORKER, ..._syncOpts } = options;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/useSyncDemo.ts"],
|
|
4
4
|
"sourcesContent": ["import { useCallback, useMemo } from 'react'\nimport {\n\tAssetRecordType,\n\tEditor,\n\tMediaHelpers,\n\tSignal,\n\tTLAsset,\n\tTLAssetStore,\n\tTLPresenceStateInfo,\n\tTLPresenceUserInfo,\n\tTLStore,\n\tTLStoreSchemaOptions,\n\tclamp,\n\tdefaultBindingUtils,\n\tdefaultShapeUtils,\n\tgetHashForString,\n\tuniqueId,\n\tuseShallowObjectIdentity,\n} from 'tldraw'\nimport { RemoteTLStoreWithStatus, useSync } from './useSync'\n\n/** @public */\nexport interface UseSyncDemoOptions {\n\t/**\n\t * The room ID to sync with. Make sure the room ID is unique. The namespace is shared by\n\t * everyone using the demo server. Consider prefixing it with your company or project name.\n\t */\n\troomId: string\n\t/**\n\t * A signal that contains the user information needed for multiplayer features.\n\t * This should be synchronized with the `userPreferences` configuration for the main `<Tldraw />` component.\n\t * If not provided, a default implementation based on localStorage will be used.\n\t */\n\tuserInfo?: TLPresenceUserInfo | Signal<TLPresenceUserInfo>\n\n\t/** @internal */\n\thost?: string\n\n\t/**\n\t * {@inheritdoc UseSyncOptions.getUserPresence}\n\t * @public\n\t */\n\tgetUserPresence?(store: TLStore, user: TLPresenceUserInfo): TLPresenceStateInfo | null\n}\n\n/**\n * Depending on the environment this package is used in, process.env may not be available. Wrap\n * `process.env` accesses in this to make sure they don't fail.\n *\n * The reason that this is just a try/catch and not a dynamic check e.g. `process &&\n * process.env[key]` is that many bundlers implement `process.env.WHATEVER` using compile-time\n * string replacement, rather than actually creating a runtime implementation of a `process` object.\n */\nfunction getEnv(cb: () => string | undefined): string | undefined {\n\ttry {\n\t\treturn cb()\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nconst DEMO_WORKER = getEnv(() => process.env.TLDRAW_BEMO_URL) ?? 'https://demo.tldraw.xyz'\nconst IMAGE_WORKER = getEnv(() => process.env.TLDRAW_IMAGE_URL) ?? 'https://images.tldraw.xyz'\n\n/**\n * Creates a tldraw store synced with a multiplayer room hosted on tldraw's demo server `https://demo.tldraw.xyz`.\n *\n * The store can be passed directly into the `<Tldraw />` component to enable multiplayer features.\n * It will handle loading states, and enable multiplayer UX like user cursors and following.\n *\n * All data on the demo server is\n *\n * - Deleted after a day or so.\n * - Publicly accessible to anyone who knows the room ID. Use your company name as a prefix to help avoid collisions, or generate UUIDs for maximum privacy.\n *\n * @example\n * ```tsx\n * function MyApp() {\n * const store = useSyncDemo({roomId: 'my-app-test-room'})\n * return <Tldraw store={store} />\n * }\n * ```\n *\n * @param options - Options for the multiplayer demo sync store. See {@link UseSyncDemoOptions} and {@link tldraw#TLStoreSchemaOptions}.\n *\n * @public\n */\nexport function useSyncDemo(\n\toptions: UseSyncDemoOptions & TLStoreSchemaOptions\n): RemoteTLStoreWithStatus {\n\tconst { roomId, host = DEMO_WORKER, ..._syncOpts } = options\n\tconst assets = useMemo(() => createDemoAssetStore(host), [host])\n\n\tconst syncOpts = useShallowObjectIdentity(_syncOpts)\n\tconst syncOptsWithDefaults = useMemo(() => {\n\t\tif ('schema' in syncOpts && syncOpts.schema) return syncOpts\n\n\t\treturn {\n\t\t\t...syncOpts,\n\t\t\tshapeUtils:\n\t\t\t\t'shapeUtils' in syncOpts\n\t\t\t\t\t? [...defaultShapeUtils, ...(syncOpts.shapeUtils ?? [])]\n\t\t\t\t\t: defaultShapeUtils,\n\t\t\tbindingUtils:\n\t\t\t\t'bindingUtils' in syncOpts\n\t\t\t\t\t? [...defaultBindingUtils, ...(syncOpts.bindingUtils ?? [])]\n\t\t\t\t\t: defaultBindingUtils,\n\t\t}\n\t}, [syncOpts])\n\n\treturn useSync({\n\t\turi: `${host}/connect/${encodeURIComponent(roomId)}`,\n\t\troomId,\n\t\tassets,\n\t\tonMount: useCallback(\n\t\t\t(editor: Editor) => {\n\t\t\t\teditor.registerExternalAssetHandler('url', async ({ url }) => {\n\t\t\t\t\treturn await createAssetFromUrlUsingDemoServer(host, url)\n\t\t\t\t})\n\t\t\t},\n\t\t\t[host]\n\t\t),\n\t\t...syncOptsWithDefaults,\n\t})\n}\n\nfunction shouldDisallowUploads(host: string) {\n\tconst disallowedHosts = ['tldraw.com', 'tldraw.xyz']\n\treturn disallowedHosts.some(\n\t\t(disallowedHost) => host === disallowedHost || host.endsWith(`.${disallowedHost}`)\n\t)\n}\n\nfunction createDemoAssetStore(host: string): TLAssetStore {\n\treturn {\n\t\tupload: async (_asset, file) => {\n\t\t\tif (shouldDisallowUploads(host)) {\n\t\t\t\talert('Uploading images is disabled in this demo.')\n\t\t\t\tthrow new Error('Uploading images is disabled in this demo.')\n\t\t\t}\n\t\t\tconst id = uniqueId()\n\n\t\t\tconst objectName = `${id}-${file.name}`.replace(/\\W/g, '-')\n\t\t\tconst url = `${host}/uploads/${objectName}`\n\n\t\t\tawait fetch(url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tbody: file,\n\t\t\t})\n\n\t\t\treturn { src: url }\n\t\t},\n\n\t\tresolve(asset, context) {\n\t\t\tif (!asset.props.src) return null\n\n\t\t\t// We don't deal with videos at the moment.\n\t\t\tif (asset.type === 'video') return asset.props.src\n\n\t\t\t// Assert it's an image to make TS happy.\n\t\t\tif (asset.type !== 'image') return null\n\n\t\t\t// Don't try to transform data: URLs, yikes.\n\t\t\tif (!asset.props.src.startsWith('http:') && !asset.props.src.startsWith('https:'))\n\t\t\t\treturn asset.props.src\n\n\t\t\tif (context.shouldResolveToOriginal) return asset.props.src\n\n\t\t\t// Don't try to transform animated images.\n\t\t\tif (MediaHelpers.isAnimatedImageType(asset?.props.mimeType) || asset.props.isAnimated)\n\t\t\t\treturn asset.props.src\n\n\t\t\t// Don't try to transform vector images.\n\t\t\tif (MediaHelpers.isVectorImageType(asset?.props.mimeType)) return asset.props.src\n\n\t\t\tconst url = new URL(asset.props.src)\n\n\t\t\t// we only transform images that are hosted on domains we control\n\t\t\tconst isTldrawImage =\n\t\t\t\turl.origin === host || /\\.tldraw\\.(?:com|xyz|dev|workers\\.dev)$/.test(url.host)\n\n\t\t\tif (!isTldrawImage) return asset.props.src\n\n\t\t\t// Assets that are under a certain file size aren't worth transforming (and incurring cost).\n\t\t\t// We still send them through the image worker to get them optimized though.\n\t\t\tconst { fileSize = 0 } = asset.props\n\t\t\tconst isWorthResizing = fileSize >= 1024 * 1024 * 1.5\n\n\t\t\tif (isWorthResizing) {\n\t\t\t\t// N.B. navigator.connection is only available in certain browsers (mainly Blink-based browsers)\n\t\t\t\t// 4g is as high the 'effectiveType' goes and we can pick a lower effective image quality for slower connections.\n\t\t\t\tconst networkCompensation =\n\t\t\t\t\t!context.networkEffectiveType || context.networkEffectiveType === '4g' ? 1 : 0.5\n\n\t\t\t\tconst width = Math.ceil(\n\t\t\t\t\tMath.min(\n\t\t\t\t\t\tasset.props.w *\n\t\t\t\t\t\t\tclamp(context.steppedScreenScale, 1 / 32, 1) *\n\t\t\t\t\t\t\tnetworkCompensation *\n\t\t\t\t\t\t\tcontext.dpr,\n\t\t\t\t\t\tasset.props.w\n\t\t\t\t\t)\n\t\t\t\t)\n\n\t\t\t\turl.searchParams.set('w', width.toString())\n\t\t\t}\n\n\t\t\tconst newUrl = `${IMAGE_WORKER}/${url.host}/${url.toString().slice(url.origin.length + 1)}`\n\t\t\treturn newUrl\n\t\t},\n\t}\n}\n\nasync function createAssetFromUrlUsingDemoServer(host: string, url: string): Promise<TLAsset> {\n\tconst urlHash = getHashForString(url)\n\ttry {\n\t\t// First, try to get the meta data from our endpoint\n\t\tconst fetchUrl = new URL(`${host}/bookmarks/unfurl`)\n\t\tfetchUrl.searchParams.set('url', url)\n\n\t\tconst meta = (await (await fetch(fetchUrl, { method: 'POST' })).json()) as {\n\t\t\tdescription?: string\n\t\t\timage?: string\n\t\t\tfavicon?: string\n\t\t\ttitle?: string\n\t\t} | null\n\n\t\treturn {\n\t\t\tid: AssetRecordType.createId(urlHash),\n\t\t\ttypeName: 'asset',\n\t\t\ttype: 'bookmark',\n\t\t\tprops: {\n\t\t\t\tsrc: url,\n\t\t\t\tdescription: meta?.description ?? '',\n\t\t\t\timage: meta?.image ?? '',\n\t\t\t\tfavicon: meta?.favicon ?? '',\n\t\t\t\ttitle: meta?.title ?? '',\n\t\t\t},\n\t\t\tmeta: {},\n\t\t}\n\t} catch (error) {\n\t\t// Otherwise, fallback to a blank bookmark\n\t\tconsole.error(error)\n\t\treturn {\n\t\t\tid: AssetRecordType.createId(urlHash),\n\t\t\ttypeName: 'asset',\n\t\t\ttype: 'bookmark',\n\t\t\tprops: {\n\t\t\t\tsrc: url,\n\t\t\t\tdescription: '',\n\t\t\t\timage: '',\n\t\t\t\tfavicon: '',\n\t\t\t\ttitle: '',\n\t\t\t},\n\t\t\tmeta: {},\n\t\t}\n\t}\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAqC;AACrC,oBAiBO;AACP,qBAAiD;AAkCjD,SAAS,OAAO,IAAkD;AACjE,MAAI;AACH,WAAO,GAAG;AAAA,EACX,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEA,MAAM,cAAc,OAAO,MAAM,
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist-esm/index.mjs
CHANGED
package/dist-esm/useSyncDemo.mjs
CHANGED
|
@@ -17,7 +17,7 @@ function getEnv(cb) {
|
|
|
17
17
|
return void 0;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
const DEMO_WORKER = getEnv(() => "https://
|
|
20
|
+
const DEMO_WORKER = getEnv(() => "https://demo.tldraw.xyz") ?? "https://demo.tldraw.xyz";
|
|
21
21
|
const IMAGE_WORKER = getEnv(() => process.env.TLDRAW_IMAGE_URL) ?? "https://images.tldraw.xyz";
|
|
22
22
|
function useSyncDemo(options) {
|
|
23
23
|
const { roomId, host = DEMO_WORKER, ..._syncOpts } = options;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/useSyncDemo.ts"],
|
|
4
4
|
"sourcesContent": ["import { useCallback, useMemo } from 'react'\nimport {\n\tAssetRecordType,\n\tEditor,\n\tMediaHelpers,\n\tSignal,\n\tTLAsset,\n\tTLAssetStore,\n\tTLPresenceStateInfo,\n\tTLPresenceUserInfo,\n\tTLStore,\n\tTLStoreSchemaOptions,\n\tclamp,\n\tdefaultBindingUtils,\n\tdefaultShapeUtils,\n\tgetHashForString,\n\tuniqueId,\n\tuseShallowObjectIdentity,\n} from 'tldraw'\nimport { RemoteTLStoreWithStatus, useSync } from './useSync'\n\n/** @public */\nexport interface UseSyncDemoOptions {\n\t/**\n\t * The room ID to sync with. Make sure the room ID is unique. The namespace is shared by\n\t * everyone using the demo server. Consider prefixing it with your company or project name.\n\t */\n\troomId: string\n\t/**\n\t * A signal that contains the user information needed for multiplayer features.\n\t * This should be synchronized with the `userPreferences` configuration for the main `<Tldraw />` component.\n\t * If not provided, a default implementation based on localStorage will be used.\n\t */\n\tuserInfo?: TLPresenceUserInfo | Signal<TLPresenceUserInfo>\n\n\t/** @internal */\n\thost?: string\n\n\t/**\n\t * {@inheritdoc UseSyncOptions.getUserPresence}\n\t * @public\n\t */\n\tgetUserPresence?(store: TLStore, user: TLPresenceUserInfo): TLPresenceStateInfo | null\n}\n\n/**\n * Depending on the environment this package is used in, process.env may not be available. Wrap\n * `process.env` accesses in this to make sure they don't fail.\n *\n * The reason that this is just a try/catch and not a dynamic check e.g. `process &&\n * process.env[key]` is that many bundlers implement `process.env.WHATEVER` using compile-time\n * string replacement, rather than actually creating a runtime implementation of a `process` object.\n */\nfunction getEnv(cb: () => string | undefined): string | undefined {\n\ttry {\n\t\treturn cb()\n\t} catch {\n\t\treturn undefined\n\t}\n}\n\nconst DEMO_WORKER = getEnv(() => process.env.TLDRAW_BEMO_URL) ?? 'https://demo.tldraw.xyz'\nconst IMAGE_WORKER = getEnv(() => process.env.TLDRAW_IMAGE_URL) ?? 'https://images.tldraw.xyz'\n\n/**\n * Creates a tldraw store synced with a multiplayer room hosted on tldraw's demo server `https://demo.tldraw.xyz`.\n *\n * The store can be passed directly into the `<Tldraw />` component to enable multiplayer features.\n * It will handle loading states, and enable multiplayer UX like user cursors and following.\n *\n * All data on the demo server is\n *\n * - Deleted after a day or so.\n * - Publicly accessible to anyone who knows the room ID. Use your company name as a prefix to help avoid collisions, or generate UUIDs for maximum privacy.\n *\n * @example\n * ```tsx\n * function MyApp() {\n * const store = useSyncDemo({roomId: 'my-app-test-room'})\n * return <Tldraw store={store} />\n * }\n * ```\n *\n * @param options - Options for the multiplayer demo sync store. See {@link UseSyncDemoOptions} and {@link tldraw#TLStoreSchemaOptions}.\n *\n * @public\n */\nexport function useSyncDemo(\n\toptions: UseSyncDemoOptions & TLStoreSchemaOptions\n): RemoteTLStoreWithStatus {\n\tconst { roomId, host = DEMO_WORKER, ..._syncOpts } = options\n\tconst assets = useMemo(() => createDemoAssetStore(host), [host])\n\n\tconst syncOpts = useShallowObjectIdentity(_syncOpts)\n\tconst syncOptsWithDefaults = useMemo(() => {\n\t\tif ('schema' in syncOpts && syncOpts.schema) return syncOpts\n\n\t\treturn {\n\t\t\t...syncOpts,\n\t\t\tshapeUtils:\n\t\t\t\t'shapeUtils' in syncOpts\n\t\t\t\t\t? [...defaultShapeUtils, ...(syncOpts.shapeUtils ?? [])]\n\t\t\t\t\t: defaultShapeUtils,\n\t\t\tbindingUtils:\n\t\t\t\t'bindingUtils' in syncOpts\n\t\t\t\t\t? [...defaultBindingUtils, ...(syncOpts.bindingUtils ?? [])]\n\t\t\t\t\t: defaultBindingUtils,\n\t\t}\n\t}, [syncOpts])\n\n\treturn useSync({\n\t\turi: `${host}/connect/${encodeURIComponent(roomId)}`,\n\t\troomId,\n\t\tassets,\n\t\tonMount: useCallback(\n\t\t\t(editor: Editor) => {\n\t\t\t\teditor.registerExternalAssetHandler('url', async ({ url }) => {\n\t\t\t\t\treturn await createAssetFromUrlUsingDemoServer(host, url)\n\t\t\t\t})\n\t\t\t},\n\t\t\t[host]\n\t\t),\n\t\t...syncOptsWithDefaults,\n\t})\n}\n\nfunction shouldDisallowUploads(host: string) {\n\tconst disallowedHosts = ['tldraw.com', 'tldraw.xyz']\n\treturn disallowedHosts.some(\n\t\t(disallowedHost) => host === disallowedHost || host.endsWith(`.${disallowedHost}`)\n\t)\n}\n\nfunction createDemoAssetStore(host: string): TLAssetStore {\n\treturn {\n\t\tupload: async (_asset, file) => {\n\t\t\tif (shouldDisallowUploads(host)) {\n\t\t\t\talert('Uploading images is disabled in this demo.')\n\t\t\t\tthrow new Error('Uploading images is disabled in this demo.')\n\t\t\t}\n\t\t\tconst id = uniqueId()\n\n\t\t\tconst objectName = `${id}-${file.name}`.replace(/\\W/g, '-')\n\t\t\tconst url = `${host}/uploads/${objectName}`\n\n\t\t\tawait fetch(url, {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tbody: file,\n\t\t\t})\n\n\t\t\treturn { src: url }\n\t\t},\n\n\t\tresolve(asset, context) {\n\t\t\tif (!asset.props.src) return null\n\n\t\t\t// We don't deal with videos at the moment.\n\t\t\tif (asset.type === 'video') return asset.props.src\n\n\t\t\t// Assert it's an image to make TS happy.\n\t\t\tif (asset.type !== 'image') return null\n\n\t\t\t// Don't try to transform data: URLs, yikes.\n\t\t\tif (!asset.props.src.startsWith('http:') && !asset.props.src.startsWith('https:'))\n\t\t\t\treturn asset.props.src\n\n\t\t\tif (context.shouldResolveToOriginal) return asset.props.src\n\n\t\t\t// Don't try to transform animated images.\n\t\t\tif (MediaHelpers.isAnimatedImageType(asset?.props.mimeType) || asset.props.isAnimated)\n\t\t\t\treturn asset.props.src\n\n\t\t\t// Don't try to transform vector images.\n\t\t\tif (MediaHelpers.isVectorImageType(asset?.props.mimeType)) return asset.props.src\n\n\t\t\tconst url = new URL(asset.props.src)\n\n\t\t\t// we only transform images that are hosted on domains we control\n\t\t\tconst isTldrawImage =\n\t\t\t\turl.origin === host || /\\.tldraw\\.(?:com|xyz|dev|workers\\.dev)$/.test(url.host)\n\n\t\t\tif (!isTldrawImage) return asset.props.src\n\n\t\t\t// Assets that are under a certain file size aren't worth transforming (and incurring cost).\n\t\t\t// We still send them through the image worker to get them optimized though.\n\t\t\tconst { fileSize = 0 } = asset.props\n\t\t\tconst isWorthResizing = fileSize >= 1024 * 1024 * 1.5\n\n\t\t\tif (isWorthResizing) {\n\t\t\t\t// N.B. navigator.connection is only available in certain browsers (mainly Blink-based browsers)\n\t\t\t\t// 4g is as high the 'effectiveType' goes and we can pick a lower effective image quality for slower connections.\n\t\t\t\tconst networkCompensation =\n\t\t\t\t\t!context.networkEffectiveType || context.networkEffectiveType === '4g' ? 1 : 0.5\n\n\t\t\t\tconst width = Math.ceil(\n\t\t\t\t\tMath.min(\n\t\t\t\t\t\tasset.props.w *\n\t\t\t\t\t\t\tclamp(context.steppedScreenScale, 1 / 32, 1) *\n\t\t\t\t\t\t\tnetworkCompensation *\n\t\t\t\t\t\t\tcontext.dpr,\n\t\t\t\t\t\tasset.props.w\n\t\t\t\t\t)\n\t\t\t\t)\n\n\t\t\t\turl.searchParams.set('w', width.toString())\n\t\t\t}\n\n\t\t\tconst newUrl = `${IMAGE_WORKER}/${url.host}/${url.toString().slice(url.origin.length + 1)}`\n\t\t\treturn newUrl\n\t\t},\n\t}\n}\n\nasync function createAssetFromUrlUsingDemoServer(host: string, url: string): Promise<TLAsset> {\n\tconst urlHash = getHashForString(url)\n\ttry {\n\t\t// First, try to get the meta data from our endpoint\n\t\tconst fetchUrl = new URL(`${host}/bookmarks/unfurl`)\n\t\tfetchUrl.searchParams.set('url', url)\n\n\t\tconst meta = (await (await fetch(fetchUrl, { method: 'POST' })).json()) as {\n\t\t\tdescription?: string\n\t\t\timage?: string\n\t\t\tfavicon?: string\n\t\t\ttitle?: string\n\t\t} | null\n\n\t\treturn {\n\t\t\tid: AssetRecordType.createId(urlHash),\n\t\t\ttypeName: 'asset',\n\t\t\ttype: 'bookmark',\n\t\t\tprops: {\n\t\t\t\tsrc: url,\n\t\t\t\tdescription: meta?.description ?? '',\n\t\t\t\timage: meta?.image ?? '',\n\t\t\t\tfavicon: meta?.favicon ?? '',\n\t\t\t\ttitle: meta?.title ?? '',\n\t\t\t},\n\t\t\tmeta: {},\n\t\t}\n\t} catch (error) {\n\t\t// Otherwise, fallback to a blank bookmark\n\t\tconsole.error(error)\n\t\treturn {\n\t\t\tid: AssetRecordType.createId(urlHash),\n\t\t\ttypeName: 'asset',\n\t\t\ttype: 'bookmark',\n\t\t\tprops: {\n\t\t\t\tsrc: url,\n\t\t\t\tdescription: '',\n\t\t\t\timage: '',\n\t\t\t\tfavicon: '',\n\t\t\t\ttitle: '',\n\t\t\t},\n\t\t\tmeta: {},\n\t\t}\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,aAAa,eAAe;AACrC;AAAA,EACC;AAAA,EAEA;AAAA,EAQA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAkC,eAAe;AAkCjD,SAAS,OAAO,IAAkD;AACjE,MAAI;AACH,WAAO,GAAG;AAAA,EACX,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEA,MAAM,cAAc,OAAO,MAAM,
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tldraw/sync",
|
|
3
3
|
"description": "tldraw infinite canvas SDK (multiplayer sync react bindings).",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw GB Ltd.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
"uuid-readable": "^0.0.2"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@tldraw/state": "
|
|
56
|
-
"@tldraw/state-react": "
|
|
57
|
-
"@tldraw/sync-core": "
|
|
58
|
-
"@tldraw/utils": "
|
|
55
|
+
"@tldraw/state": "4.0.0",
|
|
56
|
+
"@tldraw/state-react": "4.0.0",
|
|
57
|
+
"@tldraw/sync-core": "4.0.0",
|
|
58
|
+
"@tldraw/utils": "4.0.0",
|
|
59
59
|
"nanoevents": "^7.0.1",
|
|
60
|
-
"tldraw": "
|
|
60
|
+
"tldraw": "4.0.0",
|
|
61
61
|
"ws": "^8.18.0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|