@tldraw/editor 3.8.0-canary.e81e3057aaac → 3.8.0-canary.f50a5a203139
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.d.ts +259 -60
- package/dist-cjs/index.js +15 -8
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +2 -5
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +2 -2
- package/dist-cjs/lib/config/createTLStore.js +4 -2
- package/dist-cjs/lib/config/createTLStore.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +98 -23
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager.js +1 -0
- package/dist-cjs/lib/editor/managers/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/shared/resizeScaled.js +66 -0
- package/dist-cjs/lib/editor/shapes/shared/resizeScaled.js.map +7 -0
- package/dist-cjs/lib/editor/types/SvgExportContext.js.map +2 -2
- package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
- package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
- package/dist-cjs/lib/exports/exportToSvg.js.map +2 -2
- package/dist-cjs/lib/exports/getSvgAsImage.js +83 -0
- package/dist-cjs/lib/exports/getSvgAsImage.js.map +7 -0
- package/dist-cjs/lib/exports/getSvgJsx.js +16 -3
- package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
- package/dist-cjs/lib/hooks/useDocumentEvents.js +1 -3
- package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useLocalStore.js +1 -1
- package/dist-cjs/lib/hooks/useLocalStore.js.map +2 -2
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -0
- package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +3 -3
- package/dist-cjs/lib/options.js +3 -1
- package/dist-cjs/lib/options.js.map +2 -2
- package/dist-cjs/lib/utils/browserCanvasMaxSize.js +75 -0
- package/dist-cjs/lib/utils/browserCanvasMaxSize.js.map +7 -0
- package/dist-cjs/lib/utils/dom.js +6 -0
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +3 -1
- package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +259 -60
- package/dist-esm/index.mjs +9 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +2 -5
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
- package/dist-esm/lib/config/createTLStore.mjs +4 -2
- package/dist-esm/lib/config/createTLStore.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +98 -23
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager.mjs +1 -0
- package/dist-esm/lib/editor/managers/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/shared/resizeScaled.mjs +46 -0
- package/dist-esm/lib/editor/shapes/shared/resizeScaled.mjs.map +7 -0
- package/dist-esm/lib/editor/types/SvgExportContext.mjs.map +2 -2
- package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
- package/dist-esm/lib/exports/exportToSvg.mjs.map +2 -2
- package/dist-esm/lib/exports/getSvgAsImage.mjs +63 -0
- package/dist-esm/lib/exports/getSvgAsImage.mjs.map +7 -0
- package/dist-esm/lib/exports/getSvgJsx.mjs +16 -3
- package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
- package/dist-esm/lib/hooks/useDocumentEvents.mjs +2 -4
- package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useLocalStore.mjs +1 -1
- package/dist-esm/lib/hooks/useLocalStore.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -0
- package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +3 -3
- package/dist-esm/lib/options.mjs +3 -1
- package/dist-esm/lib/options.mjs.map +2 -2
- package/dist-esm/lib/utils/browserCanvasMaxSize.mjs +45 -0
- package/dist-esm/lib/utils/browserCanvasMaxSize.mjs.map +7 -0
- package/dist-esm/lib/utils/dom.mjs +6 -0
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +3 -1
- package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +2 -1
- package/package.json +22 -20
- package/src/index.ts +20 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +2 -5
- package/src/lib/config/TLSessionStateSnapshot.ts +3 -1
- package/src/lib/config/createTLStore.ts +4 -2
- package/src/lib/editor/Editor.ts +147 -57
- package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +4 -4
- package/src/lib/editor/managers/TextManager.ts +1 -0
- package/src/lib/editor/shapes/ShapeUtil.ts +30 -1
- package/src/lib/editor/shapes/shared/resizeScaled.ts +61 -0
- package/src/lib/editor/types/SvgExportContext.tsx +21 -0
- package/src/lib/editor/types/emit-types.ts +1 -0
- package/src/lib/editor/types/external-content.ts +90 -50
- package/src/lib/editor/types/misc-types.ts +55 -2
- package/src/lib/exports/StyleEmbedder.ts +1 -1
- package/src/lib/exports/exportToSvg.tsx +2 -2
- package/src/lib/exports/getSvgAsImage.ts +92 -0
- package/src/lib/exports/getSvgJsx.tsx +17 -2
- package/src/lib/hooks/useDocumentEvents.ts +2 -11
- package/src/lib/hooks/useLocalStore.ts +1 -1
- package/src/lib/hooks/usePassThroughWheelEvents.ts +7 -0
- package/src/lib/options.ts +11 -0
- package/src/lib/utils/browserCanvasMaxSize.ts +65 -0
- package/src/lib/utils/dom.ts +12 -0
- package/src/lib/utils/sync/TLLocalSyncClient.ts +3 -1
- package/src/version.ts +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/hooks/useLocalStore.ts"],
|
|
4
|
-
"sourcesContent": ["import { TLAsset, TLAssetStore, TLStoreSnapshot } from '@tldraw/tlschema'\nimport { WeakCache } from '@tldraw/utils'\nimport { useEffect } from 'react'\nimport { TLEditorSnapshot } from '../config/TLEditorSnapshot'\nimport { TLStoreOptions, createTLStore } from '../config/createTLStore'\nimport { TLStoreWithStatus } from '../utils/sync/StoreWithStatus'\nimport { TLLocalSyncClient } from '../utils/sync/TLLocalSyncClient'\nimport { useShallowObjectIdentity } from './useIdentity'\nimport { useRefState } from './useRefState'\n\n/** @internal */\nexport function useLocalStore(\n\toptions: {\n\t\tpersistenceKey?: string\n\t\tsessionId?: string\n\t\tsnapshot?: TLEditorSnapshot | TLStoreSnapshot\n\t} & TLStoreOptions\n): TLStoreWithStatus {\n\tconst [state, setState] = useRefState<TLStoreWithStatus>({ status: 'loading' })\n\n\toptions = useShallowObjectIdentity(options)\n\n\tuseEffect(() => {\n\t\tconst { persistenceKey, sessionId, ...rest } = options\n\n\t\tif (!persistenceKey) {\n\t\t\tsetState({\n\t\t\t\tstatus: 'not-synced',\n\t\t\t\tstore: createTLStore(rest),\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tsetState({ status: 'loading' })\n\n\t\tconst objectURLCache = new WeakCache<TLAsset, Promise<string | null>>()\n\t\tconst assets: TLAssetStore = {\n\t\t\tupload: async (asset, file) => {\n\t\t\t\tawait client.db.storeAsset(asset.id, file)\n\t\t\t\treturn asset.id\n\t\t\t},\n\t\t\tresolve: async (asset) => {\n\t\t\t\tif (!asset.props.src) return null\n\n\t\t\t\tif (asset.props.src.startsWith('asset:')) {\n\t\t\t\t\treturn await objectURLCache.get(asset, async () => {\n\t\t\t\t\t\tconst blob = await client.db.getAsset(asset.id)\n\t\t\t\t\t\tif (!blob) return null\n\t\t\t\t\t\treturn URL.createObjectURL(blob)\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\treturn asset.props.src\n\t\t\t},\n\t\t\t...rest.assets,\n\t\t}\n\n\t\tconst store = createTLStore({ ...rest, assets })\n\n\t\tlet isClosed = false\n\n\t\tconst client = new TLLocalSyncClient(store, {\n\t\t\tsessionId,\n\t\t\tpersistenceKey,\n\t\t\tonLoad() {\n\t\t\t\tif (isClosed) return\n\t\t\t\tsetState({ store, status: 'synced-local' })\n\t\t\t},\n\t\t\tonLoadError(err: any) {\n\t\t\t\tif (isClosed) return\n\t\t\t\tsetState({ status: 'error', error: err })\n\t\t\t},\n\t\t})\n\n\t\treturn () => {\n\t\t\tisClosed = true\n\t\t\tclient.close()\n\t\t}\n\t}, [options, setState])\n\n\treturn state\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAA0B;AAC1B,mBAA0B;AAE1B,2BAA8C;AAE9C,+BAAkC;AAClC,yBAAyC;AACzC,yBAA4B;AAGrB,SAAS,cACf,SAKoB;AACpB,QAAM,CAAC,OAAO,QAAQ,QAAI,gCAA+B,EAAE,QAAQ,UAAU,CAAC;AAE9E,gBAAU,6CAAyB,OAAO;AAE1C,8BAAU,MAAM;AACf,UAAM,EAAE,gBAAgB,WAAW,GAAG,KAAK,IAAI;AAE/C,QAAI,CAAC,gBAAgB;AACpB,eAAS;AAAA,QACR,QAAQ;AAAA,QACR,WAAO,oCAAc,IAAI;AAAA,MAC1B,CAAC;AACD;AAAA,IACD;AAEA,aAAS,EAAE,QAAQ,UAAU,CAAC;AAE9B,UAAM,iBAAiB,IAAI,uBAA2C;AACtE,UAAM,SAAuB;AAAA,MAC5B,QAAQ,OAAO,OAAO,SAAS;AAC9B,cAAM,OAAO,GAAG,WAAW,MAAM,IAAI,IAAI;AACzC,eAAO,MAAM;AAAA,
|
|
4
|
+
"sourcesContent": ["import { TLAsset, TLAssetStore, TLStoreSnapshot } from '@tldraw/tlschema'\nimport { WeakCache } from '@tldraw/utils'\nimport { useEffect } from 'react'\nimport { TLEditorSnapshot } from '../config/TLEditorSnapshot'\nimport { TLStoreOptions, createTLStore } from '../config/createTLStore'\nimport { TLStoreWithStatus } from '../utils/sync/StoreWithStatus'\nimport { TLLocalSyncClient } from '../utils/sync/TLLocalSyncClient'\nimport { useShallowObjectIdentity } from './useIdentity'\nimport { useRefState } from './useRefState'\n\n/** @internal */\nexport function useLocalStore(\n\toptions: {\n\t\tpersistenceKey?: string\n\t\tsessionId?: string\n\t\tsnapshot?: TLEditorSnapshot | TLStoreSnapshot\n\t} & TLStoreOptions\n): TLStoreWithStatus {\n\tconst [state, setState] = useRefState<TLStoreWithStatus>({ status: 'loading' })\n\n\toptions = useShallowObjectIdentity(options)\n\n\tuseEffect(() => {\n\t\tconst { persistenceKey, sessionId, ...rest } = options\n\n\t\tif (!persistenceKey) {\n\t\t\tsetState({\n\t\t\t\tstatus: 'not-synced',\n\t\t\t\tstore: createTLStore(rest),\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tsetState({ status: 'loading' })\n\n\t\tconst objectURLCache = new WeakCache<TLAsset, Promise<string | null>>()\n\t\tconst assets: TLAssetStore = {\n\t\t\tupload: async (asset, file) => {\n\t\t\t\tawait client.db.storeAsset(asset.id, file)\n\t\t\t\treturn { src: asset.id }\n\t\t\t},\n\t\t\tresolve: async (asset) => {\n\t\t\t\tif (!asset.props.src) return null\n\n\t\t\t\tif (asset.props.src.startsWith('asset:')) {\n\t\t\t\t\treturn await objectURLCache.get(asset, async () => {\n\t\t\t\t\t\tconst blob = await client.db.getAsset(asset.id)\n\t\t\t\t\t\tif (!blob) return null\n\t\t\t\t\t\treturn URL.createObjectURL(blob)\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\treturn asset.props.src\n\t\t\t},\n\t\t\t...rest.assets,\n\t\t}\n\n\t\tconst store = createTLStore({ ...rest, assets })\n\n\t\tlet isClosed = false\n\n\t\tconst client = new TLLocalSyncClient(store, {\n\t\t\tsessionId,\n\t\t\tpersistenceKey,\n\t\t\tonLoad() {\n\t\t\t\tif (isClosed) return\n\t\t\t\tsetState({ store, status: 'synced-local' })\n\t\t\t},\n\t\t\tonLoadError(err: any) {\n\t\t\t\tif (isClosed) return\n\t\t\t\tsetState({ status: 'error', error: err })\n\t\t\t},\n\t\t})\n\n\t\treturn () => {\n\t\t\tisClosed = true\n\t\t\tclient.close()\n\t\t}\n\t}, [options, setState])\n\n\treturn state\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAA0B;AAC1B,mBAA0B;AAE1B,2BAA8C;AAE9C,+BAAkC;AAClC,yBAAyC;AACzC,yBAA4B;AAGrB,SAAS,cACf,SAKoB;AACpB,QAAM,CAAC,OAAO,QAAQ,QAAI,gCAA+B,EAAE,QAAQ,UAAU,CAAC;AAE9E,gBAAU,6CAAyB,OAAO;AAE1C,8BAAU,MAAM;AACf,UAAM,EAAE,gBAAgB,WAAW,GAAG,KAAK,IAAI;AAE/C,QAAI,CAAC,gBAAgB;AACpB,eAAS;AAAA,QACR,QAAQ;AAAA,QACR,WAAO,oCAAc,IAAI;AAAA,MAC1B,CAAC;AACD;AAAA,IACD;AAEA,aAAS,EAAE,QAAQ,UAAU,CAAC;AAE9B,UAAM,iBAAiB,IAAI,uBAA2C;AACtE,UAAM,SAAuB;AAAA,MAC5B,QAAQ,OAAO,OAAO,SAAS;AAC9B,cAAM,OAAO,GAAG,WAAW,MAAM,IAAI,IAAI;AACzC,eAAO,EAAE,KAAK,MAAM,GAAG;AAAA,MACxB;AAAA,MACA,SAAS,OAAO,UAAU;AACzB,YAAI,CAAC,MAAM,MAAM,IAAK,QAAO;AAE7B,YAAI,MAAM,MAAM,IAAI,WAAW,QAAQ,GAAG;AACzC,iBAAO,MAAM,eAAe,IAAI,OAAO,YAAY;AAClD,kBAAM,OAAO,MAAM,OAAO,GAAG,SAAS,MAAM,EAAE;AAC9C,gBAAI,CAAC,KAAM,QAAO;AAClB,mBAAO,IAAI,gBAAgB,IAAI;AAAA,UAChC,CAAC;AAAA,QACF;AAEA,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,MACA,GAAG,KAAK;AAAA,IACT;AAEA,UAAM,YAAQ,oCAAc,EAAE,GAAG,MAAM,OAAO,CAAC;AAE/C,QAAI,WAAW;AAEf,UAAM,SAAS,IAAI,2CAAkB,OAAO;AAAA,MAC3C;AAAA,MACA;AAAA,MACA,SAAS;AACR,YAAI,SAAU;AACd,iBAAS,EAAE,OAAO,QAAQ,eAAe,CAAC;AAAA,MAC3C;AAAA,MACA,YAAY,KAAU;AACrB,YAAI,SAAU;AACd,iBAAS,EAAE,QAAQ,SAAS,OAAO,IAAI,CAAC;AAAA,MACzC;AAAA,IACD,CAAC;AAED,WAAO,MAAM;AACZ,iBAAW;AACX,aAAO,MAAM;AAAA,IACd;AAAA,EACD,GAAG,CAAC,SAAS,QAAQ,CAAC;AAEtB,SAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -30,6 +30,10 @@ function usePassThroughWheelEvents(ref) {
|
|
|
30
30
|
(0, import_react.useEffect)(() => {
|
|
31
31
|
function onWheel(e) {
|
|
32
32
|
if (e.isSpecialRedispatchedEvent) return;
|
|
33
|
+
const elm2 = ref.current;
|
|
34
|
+
if (elm2 && elm2.scrollHeight > elm2.clientHeight) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
33
37
|
(0, import_dom.preventDefault)(e);
|
|
34
38
|
const cvs = container.querySelector(".tl-canvas");
|
|
35
39
|
if (!cvs) return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/hooks/usePassThroughWheelEvents.ts"],
|
|
4
|
-
"sourcesContent": ["import { RefObject, useEffect } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useContainer } from './useContainer'\n\n/** @public */\nexport function usePassThroughWheelEvents(ref: RefObject<HTMLElement>) {\n\tif (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')\n\n\tconst container = useContainer()\n\n\tuseEffect(() => {\n\t\tfunction onWheel(e: WheelEvent) {\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\t\t\tpreventDefault(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new WheelEvent('wheel', e as any)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\n\t\telm.addEventListener('wheel', onWheel, { passive: false })\n\t\treturn () => {\n\t\t\telm.removeEventListener('wheel', onWheel)\n\t\t}\n\t}, [container, ref])\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAqC;AACrC,iBAA+B;AAC/B,0BAA6B;AAGtB,SAAS,0BAA0B,KAA6B;AACtE,MAAI,CAAC,IAAK,OAAM,MAAM,gDAAgD;AAEtE,QAAM,gBAAY,kCAAa;AAE/B,8BAAU,MAAM;AACf,aAAS,QAAQ,GAAe;AAC/B,UAAK,EAAU,2BAA4B;
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import { RefObject, useEffect } from 'react'\nimport { preventDefault } from '../utils/dom'\nimport { useContainer } from './useContainer'\n\n/** @public */\nexport function usePassThroughWheelEvents(ref: RefObject<HTMLElement>) {\n\tif (!ref) throw Error('usePassThroughWheelEvents must be passed a ref')\n\n\tconst container = useContainer()\n\n\tuseEffect(() => {\n\t\tfunction onWheel(e: WheelEvent) {\n\t\t\tif ((e as any).isSpecialRedispatchedEvent) return\n\n\t\t\t// if the element is scrollable, don't redispatch the event\n\t\t\tconst elm = ref.current\n\t\t\tif (elm && elm.scrollHeight > elm.clientHeight) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpreventDefault(e)\n\t\t\tconst cvs = container.querySelector('.tl-canvas')\n\t\t\tif (!cvs) return\n\t\t\tconst newEvent = new WheelEvent('wheel', e as any)\n\t\t\t;(newEvent as any).isSpecialRedispatchedEvent = true\n\t\t\tcvs.dispatchEvent(newEvent)\n\t\t}\n\n\t\tconst elm = ref.current\n\t\tif (!elm) return\n\n\t\telm.addEventListener('wheel', onWheel, { passive: false })\n\t\treturn () => {\n\t\t\telm.removeEventListener('wheel', onWheel)\n\t\t}\n\t}, [container, ref])\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAqC;AACrC,iBAA+B;AAC/B,0BAA6B;AAGtB,SAAS,0BAA0B,KAA6B;AACtE,MAAI,CAAC,IAAK,OAAM,MAAM,gDAAgD;AAEtE,QAAM,gBAAY,kCAAa;AAE/B,8BAAU,MAAM;AACf,aAAS,QAAQ,GAAe;AAC/B,UAAK,EAAU,2BAA4B;AAG3C,YAAMA,OAAM,IAAI;AAChB,UAAIA,QAAOA,KAAI,eAAeA,KAAI,cAAc;AAC/C;AAAA,MACD;AAEA,qCAAe,CAAC;AAChB,YAAM,MAAM,UAAU,cAAc,YAAY;AAChD,UAAI,CAAC,IAAK;AACV,YAAM,WAAW,IAAI,WAAW,SAAS,CAAQ;AAChD,MAAC,SAAiB,6BAA6B;AAChD,UAAI,cAAc,QAAQ;AAAA,IAC3B;AAEA,UAAM,MAAM,IAAI;AAChB,QAAI,CAAC,IAAK;AAEV,QAAI,iBAAiB,SAAS,SAAS,EAAE,SAAS,MAAM,CAAC;AACzD,WAAO,MAAM;AACZ,UAAI,oBAAoB,SAAS,OAAO;AAAA,IACzC;AAAA,EACD,GAAG,CAAC,WAAW,GAAG,CAAC;AACpB;",
|
|
6
|
+
"names": ["elm"]
|
|
7
7
|
}
|
package/dist-cjs/lib/options.js
CHANGED
|
@@ -65,6 +65,8 @@ const defaultTldrawOptions = {
|
|
|
65
65
|
temporaryAssetPreviewLifetimeMs: 18e4,
|
|
66
66
|
actionShortcutsLocation: "swap",
|
|
67
67
|
createTextOnCanvasDoubleClick: true,
|
|
68
|
-
exportProvider: import_react.Fragment
|
|
68
|
+
exportProvider: import_react.Fragment,
|
|
69
|
+
noteShapeResizeMode: "none",
|
|
70
|
+
enableToolbarKeyboardShortcuts: true
|
|
69
71
|
};
|
|
70
72
|
//# sourceMappingURL=options.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/options.ts"],
|
|
4
|
-
"sourcesContent": ["import { ComponentType, Fragment } from 'react'\n\n/**\n * Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.\n *\n * @example\n * ```tsx\n * const options: Partial<TldrawOptions> = {\n * maxPages: 3,\n * maxShapesPerPage: 1000,\n * }\n *\n * function MyTldrawComponent() {\n * return <Tldraw options={options} />\n * }\n * ```\n *\n * @public\n */\nexport interface TldrawOptions {\n\treadonly maxShapesPerPage: number\n\treadonly maxFilesAtOnce: number\n\treadonly maxPages: number\n\treadonly animationMediumMs: number\n\treadonly followChaseViewportSnap: number\n\treadonly doubleClickDurationMs: number\n\treadonly multiClickDurationMs: number\n\treadonly coarseDragDistanceSquared: number\n\treadonly dragDistanceSquared: number\n\treadonly defaultSvgPadding: number\n\treadonly cameraSlideFriction: number\n\treadonly maxPointsPerDrawShape: number\n\treadonly gridSteps: readonly {\n\t\treadonly min: number\n\t\treadonly mid: number\n\t\treadonly step: number\n\t}[]\n\treadonly collaboratorInactiveTimeoutMs: number\n\treadonly collaboratorIdleTimeoutMs: number\n\treadonly collaboratorCheckIntervalMs: number\n\treadonly cameraMovingTimeoutMs: number\n\treadonly hitTestMargin: number\n\treadonly edgeScrollDelay: number\n\treadonly edgeScrollEaseDuration: number\n\treadonly edgeScrollSpeed: number\n\treadonly edgeScrollDistance: number\n\treadonly coarsePointerWidth: number\n\treadonly coarseHandleRadius: number\n\treadonly handleRadius: number\n\treadonly longPressDurationMs: number\n\treadonly textShadowLod: number\n\treadonly adjacentShapeMargin: number\n\treadonly flattenImageBoundsExpand: number\n\treadonly flattenImageBoundsPadding: number\n\treadonly laserDelayMs: number\n\treadonly maxExportDelayMs: number\n\t/**\n\t * How long should previews created by {@link Editor.createTemporaryAssetPreview} last before\n\t * they expire? Defaults to 3 minutes.\n\t */\n\treadonly temporaryAssetPreviewLifetimeMs: number\n\treadonly actionShortcutsLocation: 'menu' | 'toolbar' | 'swap'\n\treadonly createTextOnCanvasDoubleClick: boolean\n\t/**\n\t * The react provider to use when exporting an image. This is useful if your shapes depend on\n\t * external context providers. By default, this is `React.Fragment`.\n\t */\n\treadonly exportProvider: ComponentType<{ children: React.ReactNode }>\n}\n\n/** @public */\nexport const defaultTldrawOptions = {\n\tmaxShapesPerPage: 4000,\n\tmaxFilesAtOnce: 100,\n\tmaxPages: 40,\n\tanimationMediumMs: 320,\n\tfollowChaseViewportSnap: 2,\n\tdoubleClickDurationMs: 450,\n\tmultiClickDurationMs: 200,\n\tcoarseDragDistanceSquared: 36, // 6 squared\n\tdragDistanceSquared: 16, // 4 squared\n\tdefaultSvgPadding: 32,\n\tcameraSlideFriction: 0.09,\n\tmaxPointsPerDrawShape: 500,\n\tgridSteps: [\n\t\t{ min: -1, mid: 0.15, step: 64 },\n\t\t{ min: 0.05, mid: 0.375, step: 16 },\n\t\t{ min: 0.15, mid: 1, step: 4 },\n\t\t{ min: 0.7, mid: 2.5, step: 1 },\n\t],\n\tcollaboratorInactiveTimeoutMs: 60000,\n\tcollaboratorIdleTimeoutMs: 3000,\n\tcollaboratorCheckIntervalMs: 1200,\n\tcameraMovingTimeoutMs: 64,\n\thitTestMargin: 8,\n\tedgeScrollDelay: 200,\n\tedgeScrollEaseDuration: 200,\n\tedgeScrollSpeed: 25,\n\tedgeScrollDistance: 8,\n\tcoarsePointerWidth: 12,\n\tcoarseHandleRadius: 20,\n\thandleRadius: 12,\n\tlongPressDurationMs: 500,\n\ttextShadowLod: 0.35,\n\tadjacentShapeMargin: 10,\n\tflattenImageBoundsExpand: 64,\n\tflattenImageBoundsPadding: 16,\n\tlaserDelayMs: 1200,\n\tmaxExportDelayMs: 5000,\n\ttemporaryAssetPreviewLifetimeMs: 180000,\n\tactionShortcutsLocation: 'swap',\n\tcreateTextOnCanvasDoubleClick: true,\n\texportProvider: Fragment,\n} as const satisfies TldrawOptions\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAwC;
|
|
4
|
+
"sourcesContent": ["import { ComponentType, Fragment } from 'react'\n\n/**\n * Options for configuring tldraw. For defaults, see {@link defaultTldrawOptions}.\n *\n * @example\n * ```tsx\n * const options: Partial<TldrawOptions> = {\n * maxPages: 3,\n * maxShapesPerPage: 1000,\n * }\n *\n * function MyTldrawComponent() {\n * return <Tldraw options={options} />\n * }\n * ```\n *\n * @public\n */\nexport interface TldrawOptions {\n\treadonly maxShapesPerPage: number\n\treadonly maxFilesAtOnce: number\n\treadonly maxPages: number\n\treadonly animationMediumMs: number\n\treadonly followChaseViewportSnap: number\n\treadonly doubleClickDurationMs: number\n\treadonly multiClickDurationMs: number\n\treadonly coarseDragDistanceSquared: number\n\treadonly dragDistanceSquared: number\n\treadonly defaultSvgPadding: number\n\treadonly cameraSlideFriction: number\n\treadonly maxPointsPerDrawShape: number\n\treadonly gridSteps: readonly {\n\t\treadonly min: number\n\t\treadonly mid: number\n\t\treadonly step: number\n\t}[]\n\treadonly collaboratorInactiveTimeoutMs: number\n\treadonly collaboratorIdleTimeoutMs: number\n\treadonly collaboratorCheckIntervalMs: number\n\treadonly cameraMovingTimeoutMs: number\n\treadonly hitTestMargin: number\n\treadonly edgeScrollDelay: number\n\treadonly edgeScrollEaseDuration: number\n\treadonly edgeScrollSpeed: number\n\treadonly edgeScrollDistance: number\n\treadonly coarsePointerWidth: number\n\treadonly coarseHandleRadius: number\n\treadonly handleRadius: number\n\treadonly longPressDurationMs: number\n\treadonly textShadowLod: number\n\treadonly adjacentShapeMargin: number\n\treadonly flattenImageBoundsExpand: number\n\treadonly flattenImageBoundsPadding: number\n\treadonly laserDelayMs: number\n\treadonly maxExportDelayMs: number\n\t/**\n\t * How long should previews created by {@link Editor.createTemporaryAssetPreview} last before\n\t * they expire? Defaults to 3 minutes.\n\t */\n\treadonly temporaryAssetPreviewLifetimeMs: number\n\treadonly actionShortcutsLocation: 'menu' | 'toolbar' | 'swap'\n\treadonly createTextOnCanvasDoubleClick: boolean\n\t/**\n\t * The react provider to use when exporting an image. This is useful if your shapes depend on\n\t * external context providers. By default, this is `React.Fragment`.\n\t */\n\treadonly exportProvider: ComponentType<{ children: React.ReactNode }>\n\t/**\n\t * How should the note shape resize? By default it does not resize (except automatically based on its text content),\n\t * but you can set it to be user-resizable using scale.\n\t */\n\treadonly noteShapeResizeMode: 'none' | 'scale'\n\t/**\n\t * By default, the toolbar items are accessible via number shortcuts according to their order. To disable this, set this option to false.\n\t */\n\treadonly enableToolbarKeyboardShortcuts: boolean\n}\n\n/** @public */\nexport const defaultTldrawOptions = {\n\tmaxShapesPerPage: 4000,\n\tmaxFilesAtOnce: 100,\n\tmaxPages: 40,\n\tanimationMediumMs: 320,\n\tfollowChaseViewportSnap: 2,\n\tdoubleClickDurationMs: 450,\n\tmultiClickDurationMs: 200,\n\tcoarseDragDistanceSquared: 36, // 6 squared\n\tdragDistanceSquared: 16, // 4 squared\n\tdefaultSvgPadding: 32,\n\tcameraSlideFriction: 0.09,\n\tmaxPointsPerDrawShape: 500,\n\tgridSteps: [\n\t\t{ min: -1, mid: 0.15, step: 64 },\n\t\t{ min: 0.05, mid: 0.375, step: 16 },\n\t\t{ min: 0.15, mid: 1, step: 4 },\n\t\t{ min: 0.7, mid: 2.5, step: 1 },\n\t],\n\tcollaboratorInactiveTimeoutMs: 60000,\n\tcollaboratorIdleTimeoutMs: 3000,\n\tcollaboratorCheckIntervalMs: 1200,\n\tcameraMovingTimeoutMs: 64,\n\thitTestMargin: 8,\n\tedgeScrollDelay: 200,\n\tedgeScrollEaseDuration: 200,\n\tedgeScrollSpeed: 25,\n\tedgeScrollDistance: 8,\n\tcoarsePointerWidth: 12,\n\tcoarseHandleRadius: 20,\n\thandleRadius: 12,\n\tlongPressDurationMs: 500,\n\ttextShadowLod: 0.35,\n\tadjacentShapeMargin: 10,\n\tflattenImageBoundsExpand: 64,\n\tflattenImageBoundsPadding: 16,\n\tlaserDelayMs: 1200,\n\tmaxExportDelayMs: 5000,\n\ttemporaryAssetPreviewLifetimeMs: 180000,\n\tactionShortcutsLocation: 'swap',\n\tcreateTextOnCanvasDoubleClick: true,\n\texportProvider: Fragment,\n\tnoteShapeResizeMode: 'none',\n\tenableToolbarKeyboardShortcuts: true,\n} as const satisfies TldrawOptions\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAwC;AAgFjC,MAAM,uBAAuB;AAAA,EACnC,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,mBAAmB;AAAA,EACnB,yBAAyB;AAAA,EACzB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,2BAA2B;AAAA;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,mBAAmB;AAAA,EACnB,qBAAqB;AAAA,EACrB,uBAAuB;AAAA,EACvB,WAAW;AAAA,IACV,EAAE,KAAK,IAAI,KAAK,MAAM,MAAM,GAAG;AAAA,IAC/B,EAAE,KAAK,MAAM,KAAK,OAAO,MAAM,GAAG;AAAA,IAClC,EAAE,KAAK,MAAM,KAAK,GAAG,MAAM,EAAE;AAAA,IAC7B,EAAE,KAAK,KAAK,KAAK,KAAK,MAAM,EAAE;AAAA,EAC/B;AAAA,EACA,+BAA+B;AAAA,EAC/B,2BAA2B;AAAA,EAC3B,6BAA6B;AAAA,EAC7B,uBAAuB;AAAA,EACvB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,qBAAqB;AAAA,EACrB,0BAA0B;AAAA,EAC1B,2BAA2B;AAAA,EAC3B,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,iCAAiC;AAAA,EACjC,yBAAyB;AAAA,EACzB,+BAA+B;AAAA,EAC/B,gBAAgB;AAAA,EAChB,qBAAqB;AAAA,EACrB,gCAAgC;AACjC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var browserCanvasMaxSize_exports = {};
|
|
30
|
+
__export(browserCanvasMaxSize_exports, {
|
|
31
|
+
clampToBrowserMaxCanvasSize: () => clampToBrowserMaxCanvasSize
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(browserCanvasMaxSize_exports);
|
|
34
|
+
var import_canvas_size = __toESM(require("canvas-size"));
|
|
35
|
+
let maxSizePromise = null;
|
|
36
|
+
function getBrowserCanvasMaxSize() {
|
|
37
|
+
if (!maxSizePromise) {
|
|
38
|
+
maxSizePromise = calculateBrowserCanvasMaxSize();
|
|
39
|
+
}
|
|
40
|
+
return maxSizePromise;
|
|
41
|
+
}
|
|
42
|
+
async function calculateBrowserCanvasMaxSize() {
|
|
43
|
+
const maxWidth = await import_canvas_size.default.maxWidth({ usePromise: true });
|
|
44
|
+
const maxHeight = await import_canvas_size.default.maxHeight({ usePromise: true });
|
|
45
|
+
const maxArea = await import_canvas_size.default.maxArea({ usePromise: true });
|
|
46
|
+
return {
|
|
47
|
+
maxWidth: maxWidth.width,
|
|
48
|
+
maxHeight: maxHeight.height,
|
|
49
|
+
maxArea: maxArea.width * maxArea.height
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const MAX_SAFE_CANVAS_DIMENSION = 8192;
|
|
53
|
+
const MAX_SAFE_CANVAS_AREA = 4096 * 4096;
|
|
54
|
+
async function clampToBrowserMaxCanvasSize(width, height) {
|
|
55
|
+
if (width <= MAX_SAFE_CANVAS_DIMENSION && height <= MAX_SAFE_CANVAS_DIMENSION && width * height <= MAX_SAFE_CANVAS_AREA) {
|
|
56
|
+
return [width, height];
|
|
57
|
+
}
|
|
58
|
+
const { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize();
|
|
59
|
+
const aspectRatio = width / height;
|
|
60
|
+
if (width > maxWidth) {
|
|
61
|
+
width = maxWidth;
|
|
62
|
+
height = width / aspectRatio;
|
|
63
|
+
}
|
|
64
|
+
if (height > maxHeight) {
|
|
65
|
+
height = maxHeight;
|
|
66
|
+
width = height * aspectRatio;
|
|
67
|
+
}
|
|
68
|
+
if (width * height > maxArea) {
|
|
69
|
+
const ratio = Math.sqrt(maxArea / (width * height));
|
|
70
|
+
width *= ratio;
|
|
71
|
+
height *= ratio;
|
|
72
|
+
}
|
|
73
|
+
return [width, height];
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=browserCanvasMaxSize.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/utils/browserCanvasMaxSize.ts"],
|
|
4
|
+
"sourcesContent": ["import canvasSize from 'canvas-size'\n\n/** @internal */\nexport interface CanvasMaxSize {\n\tmaxWidth: number\n\tmaxHeight: number\n\tmaxArea: number\n}\n\nlet maxSizePromise: Promise<CanvasMaxSize> | null = null\n\nfunction getBrowserCanvasMaxSize() {\n\tif (!maxSizePromise) {\n\t\tmaxSizePromise = calculateBrowserCanvasMaxSize()\n\t}\n\n\treturn maxSizePromise\n}\n\nasync function calculateBrowserCanvasMaxSize(): Promise<CanvasMaxSize> {\n\tconst maxWidth = await canvasSize.maxWidth({ usePromise: true })\n\tconst maxHeight = await canvasSize.maxHeight({ usePromise: true })\n\tconst maxArea = await canvasSize.maxArea({ usePromise: true })\n\treturn {\n\t\tmaxWidth: maxWidth.width,\n\t\tmaxHeight: maxHeight.height,\n\t\tmaxArea: maxArea.width * maxArea.height,\n\t}\n}\n\n// https://github.com/jhildenbiddle/canvas-size?tab=readme-ov-file#test-results\nconst MAX_SAFE_CANVAS_DIMENSION = 8192\nconst MAX_SAFE_CANVAS_AREA = 4096 * 4096\n\n/** @internal */\nexport async function clampToBrowserMaxCanvasSize(width: number, height: number) {\n\tif (\n\t\twidth <= MAX_SAFE_CANVAS_DIMENSION &&\n\t\theight <= MAX_SAFE_CANVAS_DIMENSION &&\n\t\twidth * height <= MAX_SAFE_CANVAS_AREA\n\t) {\n\t\treturn [width, height]\n\t}\n\n\tconst { maxWidth, maxHeight, maxArea } = await getBrowserCanvasMaxSize()\n\tconst aspectRatio = width / height\n\n\tif (width > maxWidth) {\n\t\twidth = maxWidth\n\t\theight = width / aspectRatio\n\t}\n\n\tif (height > maxHeight) {\n\t\theight = maxHeight\n\t\twidth = height * aspectRatio\n\t}\n\n\tif (width * height > maxArea) {\n\t\tconst ratio = Math.sqrt(maxArea / (width * height))\n\t\twidth *= ratio\n\t\theight *= ratio\n\t}\n\n\treturn [width, height]\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAuB;AASvB,IAAI,iBAAgD;AAEpD,SAAS,0BAA0B;AAClC,MAAI,CAAC,gBAAgB;AACpB,qBAAiB,8BAA8B;AAAA,EAChD;AAEA,SAAO;AACR;AAEA,eAAe,gCAAwD;AACtE,QAAM,WAAW,MAAM,mBAAAA,QAAW,SAAS,EAAE,YAAY,KAAK,CAAC;AAC/D,QAAM,YAAY,MAAM,mBAAAA,QAAW,UAAU,EAAE,YAAY,KAAK,CAAC;AACjE,QAAM,UAAU,MAAM,mBAAAA,QAAW,QAAQ,EAAE,YAAY,KAAK,CAAC;AAC7D,SAAO;AAAA,IACN,UAAU,SAAS;AAAA,IACnB,WAAW,UAAU;AAAA,IACrB,SAAS,QAAQ,QAAQ,QAAQ;AAAA,EAClC;AACD;AAGA,MAAM,4BAA4B;AAClC,MAAM,uBAAuB,OAAO;AAGpC,eAAsB,4BAA4B,OAAe,QAAgB;AAChF,MACC,SAAS,6BACT,UAAU,6BACV,QAAQ,UAAU,sBACjB;AACD,WAAO,CAAC,OAAO,MAAM;AAAA,EACtB;AAEA,QAAM,EAAE,UAAU,WAAW,QAAQ,IAAI,MAAM,wBAAwB;AACvE,QAAM,cAAc,QAAQ;AAE5B,MAAI,QAAQ,UAAU;AACrB,YAAQ;AACR,aAAS,QAAQ;AAAA,EAClB;AAEA,MAAI,SAAS,WAAW;AACvB,aAAS;AACT,YAAQ,SAAS;AAAA,EAClB;AAEA,MAAI,QAAQ,SAAS,SAAS;AAC7B,UAAM,QAAQ,KAAK,KAAK,WAAW,QAAQ,OAAO;AAClD,aAAS;AACT,cAAU;AAAA,EACX;AAEA,SAAO,CAAC,OAAO,MAAM;AACtB;",
|
|
6
|
+
"names": ["canvasSize"]
|
|
7
|
+
}
|
|
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var dom_exports = {};
|
|
20
20
|
__export(dom_exports, {
|
|
21
|
+
activeElementShouldCaptureKeys: () => activeElementShouldCaptureKeys,
|
|
21
22
|
loopToHtmlElement: () => loopToHtmlElement,
|
|
22
23
|
preventDefault: () => preventDefault,
|
|
23
24
|
releasePointerCapture: () => releasePointerCapture,
|
|
@@ -68,4 +69,9 @@ const setStyleProperty = (elm, property, value) => {
|
|
|
68
69
|
if (!elm) return;
|
|
69
70
|
elm.style.setProperty(property, value);
|
|
70
71
|
};
|
|
72
|
+
const INPUTS = ["input", "select", "button", "textarea"];
|
|
73
|
+
function activeElementShouldCaptureKeys() {
|
|
74
|
+
const { activeElement } = document;
|
|
75
|
+
return !!(activeElement && (activeElement.getAttribute("contenteditable") || INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1));
|
|
76
|
+
}
|
|
71
77
|
//# sourceMappingURL=dom.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/utils/dom.ts"],
|
|
4
|
-
"sourcesContent": ["/*\nThis is used to facilitate double clicking and pointer capture on elements.\n\nThe events in this file are possibly set on individual SVG elements, \nsuch as handles or corner handles, rather than on HTML elements or \nSVGSVGElements. Raw SVG elemnets do not support pointerCapture in \nmost cases, meaning that in order for pointer capture to work, we \nneed to crawl up the DOM tree to find the nearest HTML element. Then,\nin order for that element to also call the `onPointerUp` event from\nthis file, we need to manually set that event on that element and\nlater remove it when the pointerup occurs. This is a potential leak\nif the user clicks on a handle but the pointerup does not fire for\nwhatever reason.\n*/\n\nimport React from 'react'\nimport { debugFlags, pointerCaptureTrackingObject } from './debug-flags'\n\n/** @public */\nexport function loopToHtmlElement(elm: Element): HTMLElement {\n\tif (elm instanceof HTMLElement) return elm\n\tif (elm.parentElement) return loopToHtmlElement(elm.parentElement)\n\telse throw Error('Could not find a parent element of an HTML type!')\n}\n\n/**\n * This function calls `event.preventDefault()` for you. Why is that useful?\n *\n * Beacuase if you enable `window.preventDefaultLogging = true` it'll log out a message when it\n * happens. Because we use console.warn rather than (log) you'll get a stack trace in the inspector\n * telling you exactly where it happened. This is important because `e.preventDefault()` is the\n * source of many bugs, but unfortuantly it can't be avoided because it also stops a lot of default\n * behaviour which doesn't make sense in our UI\n *\n * @param event - To prevent default on\n * @public\n */\nexport function preventDefault(event: React.BaseSyntheticEvent | Event) {\n\tevent.preventDefault()\n\tif (debugFlags.logPreventDefaults.get()) {\n\t\tconsole.warn('preventDefault called on event:', event)\n\t}\n}\n\n/** @public */\nexport function setPointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\telement.setPointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\ttrackingObj.set(element, (trackingObj.get(element) ?? 0) + 1)\n\t\tconsole.warn('setPointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport function releasePointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\tif (!element.hasPointerCapture(event.pointerId)) {\n\t\treturn\n\t}\n\n\telement.releasePointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\tif (trackingObj.get(element) === 1) {\n\t\t\ttrackingObj.delete(element)\n\t\t} else if (trackingObj.has(element)) {\n\t\t\ttrackingObj.set(element, trackingObj.get(element)! - 1)\n\t\t} else {\n\t\t\tconsole.warn('Release without capture')\n\t\t}\n\t\tconsole.warn('releasePointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport const stopEventPropagation = (e: any) => e.stopPropagation()\n\n/** @internal */\nexport const setStyleProperty = (\n\telm: HTMLElement | null,\n\tproperty: string,\n\tvalue: string | number\n) => {\n\tif (!elm) return\n\telm.style.setProperty(property, value as string)\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,yBAAyD;AAGlD,SAAS,kBAAkB,KAA2B;AAC5D,MAAI,eAAe,YAAa,QAAO;AACvC,MAAI,IAAI,cAAe,QAAO,kBAAkB,IAAI,aAAa;AAAA,MAC5D,OAAM,MAAM,kDAAkD;AACpE;AAcO,SAAS,eAAe,OAAyC;AACvE,QAAM,eAAe;AACrB,MAAI,8BAAW,mBAAmB,IAAI,GAAG;AACxC,YAAQ,KAAK,mCAAmC,KAAK;AAAA,EACtD;AACD;AAGO,SAAS,kBACf,SACA,OACC;AACD,UAAQ,kBAAkB,MAAM,SAAS;AACzC,MAAI,8BAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,gDAA6B,IAAI;AACrD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAC5D,YAAQ,KAAK,wCAAwC,SAAS,KAAK;AAAA,EACpE;AACD;AAGO,SAAS,sBACf,SACA,OACC;AACD,MAAI,CAAC,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAChD;AAAA,EACD;AAEA,UAAQ,sBAAsB,MAAM,SAAS;AAC7C,MAAI,8BAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,gDAA6B,IAAI;AACrD,QAAI,YAAY,IAAI,OAAO,MAAM,GAAG;AACnC,kBAAY,OAAO,OAAO;AAAA,IAC3B,WAAW,YAAY,IAAI,OAAO,GAAG;AACpC,kBAAY,IAAI,SAAS,YAAY,IAAI,OAAO,IAAK,CAAC;AAAA,IACvD,OAAO;AACN,cAAQ,KAAK,yBAAyB;AAAA,IACvC;AACA,YAAQ,KAAK,4CAA4C,SAAS,KAAK;AAAA,EACxE;AACD;AAGO,MAAM,uBAAuB,CAAC,MAAW,EAAE,gBAAgB;AAG3D,MAAM,mBAAmB,CAC/B,KACA,UACA,UACI;AACJ,MAAI,CAAC,IAAK;AACV,MAAI,MAAM,YAAY,UAAU,KAAe;AAChD;",
|
|
4
|
+
"sourcesContent": ["/*\nThis is used to facilitate double clicking and pointer capture on elements.\n\nThe events in this file are possibly set on individual SVG elements, \nsuch as handles or corner handles, rather than on HTML elements or \nSVGSVGElements. Raw SVG elemnets do not support pointerCapture in \nmost cases, meaning that in order for pointer capture to work, we \nneed to crawl up the DOM tree to find the nearest HTML element. Then,\nin order for that element to also call the `onPointerUp` event from\nthis file, we need to manually set that event on that element and\nlater remove it when the pointerup occurs. This is a potential leak\nif the user clicks on a handle but the pointerup does not fire for\nwhatever reason.\n*/\n\nimport React from 'react'\nimport { debugFlags, pointerCaptureTrackingObject } from './debug-flags'\n\n/** @public */\nexport function loopToHtmlElement(elm: Element): HTMLElement {\n\tif (elm instanceof HTMLElement) return elm\n\tif (elm.parentElement) return loopToHtmlElement(elm.parentElement)\n\telse throw Error('Could not find a parent element of an HTML type!')\n}\n\n/**\n * This function calls `event.preventDefault()` for you. Why is that useful?\n *\n * Beacuase if you enable `window.preventDefaultLogging = true` it'll log out a message when it\n * happens. Because we use console.warn rather than (log) you'll get a stack trace in the inspector\n * telling you exactly where it happened. This is important because `e.preventDefault()` is the\n * source of many bugs, but unfortuantly it can't be avoided because it also stops a lot of default\n * behaviour which doesn't make sense in our UI\n *\n * @param event - To prevent default on\n * @public\n */\nexport function preventDefault(event: React.BaseSyntheticEvent | Event) {\n\tevent.preventDefault()\n\tif (debugFlags.logPreventDefaults.get()) {\n\t\tconsole.warn('preventDefault called on event:', event)\n\t}\n}\n\n/** @public */\nexport function setPointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\telement.setPointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\ttrackingObj.set(element, (trackingObj.get(element) ?? 0) + 1)\n\t\tconsole.warn('setPointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport function releasePointerCapture(\n\telement: Element,\n\tevent: React.PointerEvent<Element> | PointerEvent\n) {\n\tif (!element.hasPointerCapture(event.pointerId)) {\n\t\treturn\n\t}\n\n\telement.releasePointerCapture(event.pointerId)\n\tif (debugFlags.logPointerCaptures.get()) {\n\t\tconst trackingObj = pointerCaptureTrackingObject.get()\n\t\tif (trackingObj.get(element) === 1) {\n\t\t\ttrackingObj.delete(element)\n\t\t} else if (trackingObj.has(element)) {\n\t\t\ttrackingObj.set(element, trackingObj.get(element)! - 1)\n\t\t} else {\n\t\t\tconsole.warn('Release without capture')\n\t\t}\n\t\tconsole.warn('releasePointerCapture called on element:', element, event)\n\t}\n}\n\n/** @public */\nexport const stopEventPropagation = (e: any) => e.stopPropagation()\n\n/** @internal */\nexport const setStyleProperty = (\n\telm: HTMLElement | null,\n\tproperty: string,\n\tvalue: string | number\n) => {\n\tif (!elm) return\n\telm.style.setProperty(property, value as string)\n}\n\nconst INPUTS = ['input', 'select', 'button', 'textarea']\n\n/** @internal */\nexport function activeElementShouldCaptureKeys() {\n\tconst { activeElement } = document\n\treturn !!(\n\t\tactiveElement &&\n\t\t(activeElement.getAttribute('contenteditable') ||\n\t\t\tINPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1)\n\t)\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBA,yBAAyD;AAGlD,SAAS,kBAAkB,KAA2B;AAC5D,MAAI,eAAe,YAAa,QAAO;AACvC,MAAI,IAAI,cAAe,QAAO,kBAAkB,IAAI,aAAa;AAAA,MAC5D,OAAM,MAAM,kDAAkD;AACpE;AAcO,SAAS,eAAe,OAAyC;AACvE,QAAM,eAAe;AACrB,MAAI,8BAAW,mBAAmB,IAAI,GAAG;AACxC,YAAQ,KAAK,mCAAmC,KAAK;AAAA,EACtD;AACD;AAGO,SAAS,kBACf,SACA,OACC;AACD,UAAQ,kBAAkB,MAAM,SAAS;AACzC,MAAI,8BAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,gDAA6B,IAAI;AACrD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAC5D,YAAQ,KAAK,wCAAwC,SAAS,KAAK;AAAA,EACpE;AACD;AAGO,SAAS,sBACf,SACA,OACC;AACD,MAAI,CAAC,QAAQ,kBAAkB,MAAM,SAAS,GAAG;AAChD;AAAA,EACD;AAEA,UAAQ,sBAAsB,MAAM,SAAS;AAC7C,MAAI,8BAAW,mBAAmB,IAAI,GAAG;AACxC,UAAM,cAAc,gDAA6B,IAAI;AACrD,QAAI,YAAY,IAAI,OAAO,MAAM,GAAG;AACnC,kBAAY,OAAO,OAAO;AAAA,IAC3B,WAAW,YAAY,IAAI,OAAO,GAAG;AACpC,kBAAY,IAAI,SAAS,YAAY,IAAI,OAAO,IAAK,CAAC;AAAA,IACvD,OAAO;AACN,cAAQ,KAAK,yBAAyB;AAAA,IACvC;AACA,YAAQ,KAAK,4CAA4C,SAAS,KAAK;AAAA,EACxE;AACD;AAGO,MAAM,uBAAuB,CAAC,MAAW,EAAE,gBAAgB;AAG3D,MAAM,mBAAmB,CAC/B,KACA,UACA,UACI;AACJ,MAAI,CAAC,IAAK;AACV,MAAI,MAAM,YAAY,UAAU,KAAe;AAChD;AAEA,MAAM,SAAS,CAAC,SAAS,UAAU,UAAU,UAAU;AAGhD,SAAS,iCAAiC;AAChD,QAAM,EAAE,cAAc,IAAI;AAC1B,SAAO,CAAC,EACP,kBACC,cAAc,aAAa,iBAAiB,KAC5C,OAAO,QAAQ,cAAc,QAAQ,YAAY,CAAC,IAAI;AAEzD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -150,7 +150,9 @@ class TLLocalSyncClient {
|
|
|
150
150
|
});
|
|
151
151
|
}
|
|
152
152
|
if (sessionStateSnapshot) {
|
|
153
|
-
(0, import_TLSessionStateSnapshot.loadSessionStateSnapshotIntoStore)(this.store, sessionStateSnapshot
|
|
153
|
+
(0, import_TLSessionStateSnapshot.loadSessionStateSnapshotIntoStore)(this.store, sessionStateSnapshot, {
|
|
154
|
+
forceOverwrite: true
|
|
155
|
+
});
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
158
|
this.channel.onmessage = ({ data: data2 }) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/utils/sync/TLLocalSyncClient.ts"],
|
|
4
|
-
"sourcesContent": ["import { Signal, transact } from '@tldraw/state'\nimport { RecordsDiff, SerializedSchema, UnknownRecord, squashRecordDiffs } from '@tldraw/store'\nimport { TLStore } from '@tldraw/tlschema'\nimport { assert } from '@tldraw/utils'\nimport {\n\tTAB_ID,\n\tTLSessionStateSnapshot,\n\tcreateSessionStateSnapshotSignal,\n\textractSessionStateFromLegacySnapshot,\n\tloadSessionStateSnapshotIntoStore,\n} from '../../config/TLSessionStateSnapshot'\nimport { LocalIndexedDb } from './LocalIndexedDb'\nimport { showCantReadFromIndexDbAlert, showCantWriteToIndexDbAlert } from './alerts'\n\n/** How should we debounce persists? */\nconst PERSIST_THROTTLE_MS = 350\n/** If we're in an error state, how long should we wait before retrying a write? */\nconst PERSIST_RETRY_THROTTLE_MS = 10_000\n\nconst UPDATE_INSTANCE_STATE = Symbol('UPDATE_INSTANCE_STATE')\n\n/**\n * IMPORTANT!!!\n *\n * This is just a quick-n-dirty temporary solution that will be replaced with the remote sync client\n * once it has the db integrated\n */\n\ninterface SyncMessage {\n\ttype: 'diff'\n\tstoreId: string\n\tchanges: RecordsDiff<UnknownRecord>\n\tschema: SerializedSchema\n}\n\n// Sent by new clients when they connect\n// If another client is on the channel with a newer schema version\n// It will\ninterface AnnounceMessage {\n\ttype: 'announce'\n\tschema: SerializedSchema\n}\n\ntype Message = SyncMessage | AnnounceMessage\n\ntype UnpackPromise<T> = T extends Promise<infer U> ? U : T\n\nconst msg = (msg: Message) => msg\n\n/** @internal */\nexport class BroadcastChannelMock {\n\tonmessage?: (e: MessageEvent) => void\n\tconstructor(_name: string) {\n\t\t// noop\n\t}\n\tpostMessage(_msg: Message) {\n\t\t// noop\n\t}\n\tclose() {\n\t\t// noop\n\t}\n}\n\nconst BC = typeof BroadcastChannel === 'undefined' ? BroadcastChannelMock : BroadcastChannel\n\n/** @internal */\nexport class TLLocalSyncClient {\n\tprivate disposables = new Set<() => void>()\n\tprivate diffQueue: Array<RecordsDiff<UnknownRecord> | typeof UPDATE_INSTANCE_STATE> = []\n\tprivate didDispose = false\n\tprivate shouldDoFullDBWrite = true\n\tprivate isReloading = false\n\treadonly persistenceKey: string\n\treadonly sessionId: string\n\treadonly serializedSchema: SerializedSchema\n\tprivate isDebugging = false\n\tprivate readonly documentTypes: ReadonlySet<string>\n\tprivate readonly $sessionStateSnapshot: Signal<TLSessionStateSnapshot | null>\n\t/** @internal */\n\treadonly db: LocalIndexedDb\n\n\tinitTime = Date.now()\n\tprivate debug(...args: any[]) {\n\t\tif (this.isDebugging) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.debug(...args)\n\t\t}\n\t}\n\tconstructor(\n\t\tpublic readonly store: TLStore,\n\t\t{\n\t\t\tpersistenceKey,\n\t\t\tsessionId = TAB_ID,\n\t\t\tonLoad,\n\t\t\tonLoadError,\n\t\t}: {\n\t\t\tpersistenceKey: string\n\t\t\tsessionId?: string\n\t\t\tonLoad(self: TLLocalSyncClient): void\n\t\t\tonLoadError(error: Error): void\n\t\t},\n\t\tpublic readonly channel = new BC(`tldraw-tab-sync-${persistenceKey}`)\n\t) {\n\t\tif (typeof window !== 'undefined') {\n\t\t\t;(window as any).tlsync = this\n\t\t}\n\t\tthis.persistenceKey = persistenceKey\n\t\tthis.sessionId = sessionId\n\t\tthis.db = new LocalIndexedDb(persistenceKey)\n\t\tthis.disposables.add(() => this.db.close())\n\n\t\tthis.serializedSchema = this.store.schema.serialize()\n\t\tthis.$sessionStateSnapshot = createSessionStateSnapshotSignal(this.store)\n\n\t\tthis.disposables.add(\n\t\t\t// Set up a subscription to changes from the store: When\n\t\t\t// the store changes (and if the change was made by the user)\n\t\t\t// then immediately send the diff to other tabs via postMessage\n\t\t\t// and schedule a persist.\n\t\t\tstore.listen(\n\t\t\t\t({ changes }) => {\n\t\t\t\t\tthis.diffQueue.push(changes)\n\t\t\t\t\tthis.channel.postMessage(\n\t\t\t\t\t\tmsg({\n\t\t\t\t\t\t\ttype: 'diff',\n\t\t\t\t\t\t\tstoreId: this.store.id,\n\t\t\t\t\t\t\tchanges,\n\t\t\t\t\t\t\tschema: this.serializedSchema,\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\tthis.schedulePersist()\n\t\t\t\t},\n\t\t\t\t{ source: 'user', scope: 'document' }\n\t\t\t)\n\t\t)\n\t\tthis.disposables.add(\n\t\t\tstore.listen(\n\t\t\t\t() => {\n\t\t\t\t\tthis.diffQueue.push(UPDATE_INSTANCE_STATE)\n\t\t\t\t\tthis.schedulePersist()\n\t\t\t\t},\n\t\t\t\t{ scope: 'session' }\n\t\t\t)\n\t\t)\n\n\t\tthis.connect(onLoad, onLoadError)\n\n\t\tthis.documentTypes = new Set(\n\t\t\tObject.values(this.store.schema.types)\n\t\t\t\t.filter((t) => t.scope === 'document')\n\t\t\t\t.map((t) => t.typeName)\n\t\t)\n\t}\n\n\tprivate async connect(onLoad: (client: this) => void, onLoadError: (error: Error) => void) {\n\t\tthis.debug('connecting')\n\t\tlet data: UnpackPromise<ReturnType<LocalIndexedDb['load']>> | undefined\n\n\t\ttry {\n\t\t\tdata = await this.db.load({ sessionId: this.sessionId })\n\t\t} catch (error: any) {\n\t\t\tonLoadError(error)\n\t\t\tshowCantReadFromIndexDbAlert()\n\t\t\treturn\n\t\t}\n\n\t\tthis.debug('loaded data from store', data, 'didDispose', this.didDispose)\n\t\tif (this.didDispose) return\n\n\t\ttry {\n\t\t\tif (data) {\n\t\t\t\tconst documentSnapshot = Object.fromEntries(data.records.map((r) => [r.id, r]))\n\t\t\t\tconst sessionStateSnapshot =\n\t\t\t\t\tdata.sessionStateSnapshot ?? extractSessionStateFromLegacySnapshot(documentSnapshot)\n\t\t\t\tconst migrationResult = this.store.schema.migrateStoreSnapshot({\n\t\t\t\t\tstore: documentSnapshot,\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\tschema: data.schema ?? this.store.schema.serializeEarliestVersion(),\n\t\t\t\t})\n\n\t\t\t\tif (migrationResult.type === 'error') {\n\t\t\t\t\tconsole.error('failed to migrate store', migrationResult)\n\t\t\t\t\tonLoadError(new Error(`Failed to migrate store: ${migrationResult.reason}`))\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst records = Object.values(migrationResult.value).filter((r) =>\n\t\t\t\t\tthis.documentTypes.has(r.typeName)\n\t\t\t\t)\n\t\t\t\tif (records.length > 0) {\n\t\t\t\t\t// 3. Merge the changes into the REAL STORE\n\t\t\t\t\tthis.store.mergeRemoteChanges(() => {\n\t\t\t\t\t\t// Calling put will validate the records!\n\t\t\t\t\t\tthis.store.put(records, 'initialize')\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tif (sessionStateSnapshot) {\n\t\t\t\t\tloadSessionStateSnapshotIntoStore(this.store, sessionStateSnapshot)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.channel.onmessage = ({ data }) => {\n\t\t\t\tthis.debug('got message', data)\n\t\t\t\tconst msg = data as Message\n\t\t\t\t// if their schema is earlier than ours, we need to tell them so they can refresh\n\t\t\t\t// if their schema is later than ours, we need to refresh\n\t\t\t\tconst res = this.store.schema.getMigrationsSince(msg.schema)\n\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\t// we are older, refresh\n\t\t\t\t\t// but add a safety check to make sure we don't get in an infinite loop\n\t\t\t\t\tconst timeSinceInit = Date.now() - this.initTime\n\t\t\t\t\tif (timeSinceInit < 5000) {\n\t\t\t\t\t\t// This tab was just reloaded, but is out of date compared to other tabs.\n\t\t\t\t\t\t// Not expecting this to ever happen. It should only happen if we roll back a release that incremented\n\t\t\t\t\t\t// the schema version (which we should never do)\n\t\t\t\t\t\t// Or maybe during development if you have multiple local tabs open running the app on prod mode and you\n\t\t\t\t\t\t// check out an older commit. Dev server should be fine.\n\t\t\t\t\t\tonLoadError(new Error('Schema mismatch, please close other tabs and reload the page'))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.debug('reloading')\n\t\t\t\t\tthis.isReloading = true\n\t\t\t\t\twindow?.location?.reload?.()\n\t\t\t\t\treturn\n\t\t\t\t} else if (res.value.length > 0) {\n\t\t\t\t\t// they are older, tell them to refresh and not write any more data\n\t\t\t\t\tthis.debug('telling them to reload')\n\t\t\t\t\tthis.channel.postMessage({ type: 'announce', schema: this.serializedSchema })\n\t\t\t\t\t// schedule a full db write in case they wrote data anyway\n\t\t\t\t\tthis.shouldDoFullDBWrite = true\n\t\t\t\t\tthis.persistIfNeeded()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// otherwise, all good, same version :)\n\t\t\t\tif (msg.type === 'diff') {\n\t\t\t\t\tthis.debug('applying diff')\n\t\t\t\t\ttransact(() => {\n\t\t\t\t\t\tthis.store.mergeRemoteChanges(() => {\n\t\t\t\t\t\t\tthis.store.applyDiff(msg.changes as any)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.channel.postMessage({ type: 'announce', schema: this.serializedSchema })\n\t\t\tthis.disposables.add(() => {\n\t\t\t\tthis.channel.close()\n\t\t\t})\n\t\t\tonLoad(this)\n\t\t} catch (e: any) {\n\t\t\tthis.debug('error loading data from store', e)\n\t\t\tif (this.didDispose) return\n\t\t\tonLoadError(e)\n\t\t\treturn\n\t\t}\n\t}\n\n\tclose() {\n\t\tthis.debug('closing')\n\t\tthis.didDispose = true\n\t\tthis.disposables.forEach((d) => d())\n\t}\n\n\tprivate isPersisting = false\n\tprivate didLastWriteError = false\n\t// eslint-disable-next-line no-restricted-globals\n\tprivate scheduledPersistTimeout: ReturnType<typeof setTimeout> | null = null\n\n\t/**\n\t * Schedule a persist. Persists don't happen immediately: they are throttled to avoid writing too\n\t * often, and will retry if failed.\n\t *\n\t * @internal\n\t */\n\tprivate schedulePersist() {\n\t\tthis.debug('schedulePersist', this.scheduledPersistTimeout)\n\t\tif (this.scheduledPersistTimeout) return\n\t\t// eslint-disable-next-line no-restricted-globals\n\t\tthis.scheduledPersistTimeout = setTimeout(\n\t\t\t() => {\n\t\t\t\tthis.scheduledPersistTimeout = null\n\t\t\t\tthis.persistIfNeeded()\n\t\t\t},\n\t\t\tthis.didLastWriteError ? PERSIST_RETRY_THROTTLE_MS : PERSIST_THROTTLE_MS\n\t\t)\n\t}\n\n\t/**\n\t * Persist to IndexedDB only under certain circumstances:\n\t *\n\t * - If we're not already persisting\n\t * - If we're not reloading the page\n\t * - And we have something to persist (a full db write scheduled or changes in the diff queue)\n\t *\n\t * @internal\n\t */\n\tprivate persistIfNeeded() {\n\t\tthis.debug('persistIfNeeded', {\n\t\t\tisPersisting: this.isPersisting,\n\t\t\tisReloading: this.isReloading,\n\t\t\tshouldDoFullDBWrite: this.shouldDoFullDBWrite,\n\t\t\tdiffQueueLength: this.diffQueue.length,\n\t\t\tstoreIsPossiblyCorrupt: this.store.isPossiblyCorrupted(),\n\t\t})\n\n\t\t// if we've scheduled a persist for the future, that's no longer needed\n\t\tif (this.scheduledPersistTimeout) {\n\t\t\tclearTimeout(this.scheduledPersistTimeout)\n\t\t\tthis.scheduledPersistTimeout = null\n\t\t}\n\n\t\t// if a persist is already in progress, we don't need to do anything -\n\t\t// if there are still outstanding changes once it's finished, it'll\n\t\t// schedule another persist\n\t\tif (this.isPersisting) return\n\n\t\t// if we're reloading the page, it's because there's a newer client\n\t\t// present so lets not overwrite their changes\n\t\tif (this.isReloading) return\n\n\t\t// if the store is possibly corrupted, we don't want to persist\n\t\tif (this.store.isPossiblyCorrupted()) return\n\n\t\t// if we're scheduled for a full write or if we have changes outstanding, let's persist them!\n\t\tif (this.shouldDoFullDBWrite || this.diffQueue.length > 0) {\n\t\t\tthis.doPersist()\n\t\t}\n\t}\n\n\t/**\n\t * Actually persist to IndexedDB. If the write fails, then we'll retry with a full db write after\n\t * a short delay.\n\t */\n\tprivate async doPersist() {\n\t\tassert(!this.isPersisting, 'persist already in progress')\n\t\tif (this.didDispose) return\n\t\tthis.isPersisting = true\n\n\t\tthis.debug('doPersist start')\n\n\t\t// instantly empty the diff queue, but keep our own copy of it. this way\n\t\t// diffs that come in during the persist will still get tracked\n\t\tconst diffQueue = this.diffQueue\n\t\tthis.diffQueue = []\n\n\t\ttry {\n\t\t\tif (this.shouldDoFullDBWrite) {\n\t\t\t\tthis.shouldDoFullDBWrite = false\n\t\t\t\tawait this.db.storeSnapshot({\n\t\t\t\t\tschema: this.store.schema,\n\t\t\t\t\tsnapshot: this.store.serialize(),\n\t\t\t\t\tsessionId: this.sessionId,\n\t\t\t\t\tsessionStateSnapshot: this.$sessionStateSnapshot.get(),\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tconst diffs = squashRecordDiffs(\n\t\t\t\t\tdiffQueue.filter((d): d is RecordsDiff<UnknownRecord> => d !== UPDATE_INSTANCE_STATE)\n\t\t\t\t)\n\t\t\t\tawait this.db.storeChanges({\n\t\t\t\t\tchanges: diffs,\n\t\t\t\t\tschema: this.store.schema,\n\t\t\t\t\tsessionId: this.sessionId,\n\t\t\t\t\tsessionStateSnapshot: this.$sessionStateSnapshot.get(),\n\t\t\t\t})\n\t\t\t}\n\t\t\tthis.didLastWriteError = false\n\t\t} catch (e) {\n\t\t\t// set this.shouldDoFullDBWrite because we clear the diffQueue no matter what,\n\t\t\t// so if this is just a temporary error, we will still persist all changes\n\t\t\tthis.shouldDoFullDBWrite = true\n\t\t\tthis.didLastWriteError = true\n\t\t\tconsole.error('failed to store changes in indexed db', e)\n\n\t\t\tshowCantWriteToIndexDbAlert()\n\t\t\tif (typeof window !== 'undefined') {\n\t\t\t\t// adios\n\t\t\t\twindow.location.reload()\n\t\t\t}\n\t\t}\n\n\t\tthis.isPersisting = false\n\t\tthis.debug('doPersist end')\n\n\t\t// changes might have come in between when we started the persist and\n\t\t// now. we request another persist so any new changes can get written\n\t\tthis.schedulePersist()\n\t}\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { Signal, transact } from '@tldraw/state'\nimport { RecordsDiff, SerializedSchema, UnknownRecord, squashRecordDiffs } from '@tldraw/store'\nimport { TLStore } from '@tldraw/tlschema'\nimport { assert } from '@tldraw/utils'\nimport {\n\tTAB_ID,\n\tTLSessionStateSnapshot,\n\tcreateSessionStateSnapshotSignal,\n\textractSessionStateFromLegacySnapshot,\n\tloadSessionStateSnapshotIntoStore,\n} from '../../config/TLSessionStateSnapshot'\nimport { LocalIndexedDb } from './LocalIndexedDb'\nimport { showCantReadFromIndexDbAlert, showCantWriteToIndexDbAlert } from './alerts'\n\n/** How should we debounce persists? */\nconst PERSIST_THROTTLE_MS = 350\n/** If we're in an error state, how long should we wait before retrying a write? */\nconst PERSIST_RETRY_THROTTLE_MS = 10_000\n\nconst UPDATE_INSTANCE_STATE = Symbol('UPDATE_INSTANCE_STATE')\n\n/**\n * IMPORTANT!!!\n *\n * This is just a quick-n-dirty temporary solution that will be replaced with the remote sync client\n * once it has the db integrated\n */\n\ninterface SyncMessage {\n\ttype: 'diff'\n\tstoreId: string\n\tchanges: RecordsDiff<UnknownRecord>\n\tschema: SerializedSchema\n}\n\n// Sent by new clients when they connect\n// If another client is on the channel with a newer schema version\n// It will\ninterface AnnounceMessage {\n\ttype: 'announce'\n\tschema: SerializedSchema\n}\n\ntype Message = SyncMessage | AnnounceMessage\n\ntype UnpackPromise<T> = T extends Promise<infer U> ? U : T\n\nconst msg = (msg: Message) => msg\n\n/** @internal */\nexport class BroadcastChannelMock {\n\tonmessage?: (e: MessageEvent) => void\n\tconstructor(_name: string) {\n\t\t// noop\n\t}\n\tpostMessage(_msg: Message) {\n\t\t// noop\n\t}\n\tclose() {\n\t\t// noop\n\t}\n}\n\nconst BC = typeof BroadcastChannel === 'undefined' ? BroadcastChannelMock : BroadcastChannel\n\n/** @internal */\nexport class TLLocalSyncClient {\n\tprivate disposables = new Set<() => void>()\n\tprivate diffQueue: Array<RecordsDiff<UnknownRecord> | typeof UPDATE_INSTANCE_STATE> = []\n\tprivate didDispose = false\n\tprivate shouldDoFullDBWrite = true\n\tprivate isReloading = false\n\treadonly persistenceKey: string\n\treadonly sessionId: string\n\treadonly serializedSchema: SerializedSchema\n\tprivate isDebugging = false\n\tprivate readonly documentTypes: ReadonlySet<string>\n\tprivate readonly $sessionStateSnapshot: Signal<TLSessionStateSnapshot | null>\n\t/** @internal */\n\treadonly db: LocalIndexedDb\n\n\tinitTime = Date.now()\n\tprivate debug(...args: any[]) {\n\t\tif (this.isDebugging) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.debug(...args)\n\t\t}\n\t}\n\tconstructor(\n\t\tpublic readonly store: TLStore,\n\t\t{\n\t\t\tpersistenceKey,\n\t\t\tsessionId = TAB_ID,\n\t\t\tonLoad,\n\t\t\tonLoadError,\n\t\t}: {\n\t\t\tpersistenceKey: string\n\t\t\tsessionId?: string\n\t\t\tonLoad(self: TLLocalSyncClient): void\n\t\t\tonLoadError(error: Error): void\n\t\t},\n\t\tpublic readonly channel = new BC(`tldraw-tab-sync-${persistenceKey}`)\n\t) {\n\t\tif (typeof window !== 'undefined') {\n\t\t\t;(window as any).tlsync = this\n\t\t}\n\t\tthis.persistenceKey = persistenceKey\n\t\tthis.sessionId = sessionId\n\t\tthis.db = new LocalIndexedDb(persistenceKey)\n\t\tthis.disposables.add(() => this.db.close())\n\n\t\tthis.serializedSchema = this.store.schema.serialize()\n\t\tthis.$sessionStateSnapshot = createSessionStateSnapshotSignal(this.store)\n\n\t\tthis.disposables.add(\n\t\t\t// Set up a subscription to changes from the store: When\n\t\t\t// the store changes (and if the change was made by the user)\n\t\t\t// then immediately send the diff to other tabs via postMessage\n\t\t\t// and schedule a persist.\n\t\t\tstore.listen(\n\t\t\t\t({ changes }) => {\n\t\t\t\t\tthis.diffQueue.push(changes)\n\t\t\t\t\tthis.channel.postMessage(\n\t\t\t\t\t\tmsg({\n\t\t\t\t\t\t\ttype: 'diff',\n\t\t\t\t\t\t\tstoreId: this.store.id,\n\t\t\t\t\t\t\tchanges,\n\t\t\t\t\t\t\tschema: this.serializedSchema,\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t\tthis.schedulePersist()\n\t\t\t\t},\n\t\t\t\t{ source: 'user', scope: 'document' }\n\t\t\t)\n\t\t)\n\t\tthis.disposables.add(\n\t\t\tstore.listen(\n\t\t\t\t() => {\n\t\t\t\t\tthis.diffQueue.push(UPDATE_INSTANCE_STATE)\n\t\t\t\t\tthis.schedulePersist()\n\t\t\t\t},\n\t\t\t\t{ scope: 'session' }\n\t\t\t)\n\t\t)\n\n\t\tthis.connect(onLoad, onLoadError)\n\n\t\tthis.documentTypes = new Set(\n\t\t\tObject.values(this.store.schema.types)\n\t\t\t\t.filter((t) => t.scope === 'document')\n\t\t\t\t.map((t) => t.typeName)\n\t\t)\n\t}\n\n\tprivate async connect(onLoad: (client: this) => void, onLoadError: (error: Error) => void) {\n\t\tthis.debug('connecting')\n\t\tlet data: UnpackPromise<ReturnType<LocalIndexedDb['load']>> | undefined\n\n\t\ttry {\n\t\t\tdata = await this.db.load({ sessionId: this.sessionId })\n\t\t} catch (error: any) {\n\t\t\tonLoadError(error)\n\t\t\tshowCantReadFromIndexDbAlert()\n\t\t\treturn\n\t\t}\n\n\t\tthis.debug('loaded data from store', data, 'didDispose', this.didDispose)\n\t\tif (this.didDispose) return\n\n\t\ttry {\n\t\t\tif (data) {\n\t\t\t\tconst documentSnapshot = Object.fromEntries(data.records.map((r) => [r.id, r]))\n\t\t\t\tconst sessionStateSnapshot =\n\t\t\t\t\tdata.sessionStateSnapshot ?? extractSessionStateFromLegacySnapshot(documentSnapshot)\n\t\t\t\tconst migrationResult = this.store.schema.migrateStoreSnapshot({\n\t\t\t\t\tstore: documentSnapshot,\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated\n\t\t\t\t\tschema: data.schema ?? this.store.schema.serializeEarliestVersion(),\n\t\t\t\t})\n\n\t\t\t\tif (migrationResult.type === 'error') {\n\t\t\t\t\tconsole.error('failed to migrate store', migrationResult)\n\t\t\t\t\tonLoadError(new Error(`Failed to migrate store: ${migrationResult.reason}`))\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst records = Object.values(migrationResult.value).filter((r) =>\n\t\t\t\t\tthis.documentTypes.has(r.typeName)\n\t\t\t\t)\n\t\t\t\tif (records.length > 0) {\n\t\t\t\t\t// 3. Merge the changes into the REAL STORE\n\t\t\t\t\tthis.store.mergeRemoteChanges(() => {\n\t\t\t\t\t\t// Calling put will validate the records!\n\t\t\t\t\t\tthis.store.put(records, 'initialize')\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tif (sessionStateSnapshot) {\n\t\t\t\t\tloadSessionStateSnapshotIntoStore(this.store, sessionStateSnapshot, {\n\t\t\t\t\t\tforceOverwrite: true,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.channel.onmessage = ({ data }) => {\n\t\t\t\tthis.debug('got message', data)\n\t\t\t\tconst msg = data as Message\n\t\t\t\t// if their schema is earlier than ours, we need to tell them so they can refresh\n\t\t\t\t// if their schema is later than ours, we need to refresh\n\t\t\t\tconst res = this.store.schema.getMigrationsSince(msg.schema)\n\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\t// we are older, refresh\n\t\t\t\t\t// but add a safety check to make sure we don't get in an infinite loop\n\t\t\t\t\tconst timeSinceInit = Date.now() - this.initTime\n\t\t\t\t\tif (timeSinceInit < 5000) {\n\t\t\t\t\t\t// This tab was just reloaded, but is out of date compared to other tabs.\n\t\t\t\t\t\t// Not expecting this to ever happen. It should only happen if we roll back a release that incremented\n\t\t\t\t\t\t// the schema version (which we should never do)\n\t\t\t\t\t\t// Or maybe during development if you have multiple local tabs open running the app on prod mode and you\n\t\t\t\t\t\t// check out an older commit. Dev server should be fine.\n\t\t\t\t\t\tonLoadError(new Error('Schema mismatch, please close other tabs and reload the page'))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthis.debug('reloading')\n\t\t\t\t\tthis.isReloading = true\n\t\t\t\t\twindow?.location?.reload?.()\n\t\t\t\t\treturn\n\t\t\t\t} else if (res.value.length > 0) {\n\t\t\t\t\t// they are older, tell them to refresh and not write any more data\n\t\t\t\t\tthis.debug('telling them to reload')\n\t\t\t\t\tthis.channel.postMessage({ type: 'announce', schema: this.serializedSchema })\n\t\t\t\t\t// schedule a full db write in case they wrote data anyway\n\t\t\t\t\tthis.shouldDoFullDBWrite = true\n\t\t\t\t\tthis.persistIfNeeded()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t// otherwise, all good, same version :)\n\t\t\t\tif (msg.type === 'diff') {\n\t\t\t\t\tthis.debug('applying diff')\n\t\t\t\t\ttransact(() => {\n\t\t\t\t\t\tthis.store.mergeRemoteChanges(() => {\n\t\t\t\t\t\t\tthis.store.applyDiff(msg.changes as any)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.channel.postMessage({ type: 'announce', schema: this.serializedSchema })\n\t\t\tthis.disposables.add(() => {\n\t\t\t\tthis.channel.close()\n\t\t\t})\n\t\t\tonLoad(this)\n\t\t} catch (e: any) {\n\t\t\tthis.debug('error loading data from store', e)\n\t\t\tif (this.didDispose) return\n\t\t\tonLoadError(e)\n\t\t\treturn\n\t\t}\n\t}\n\n\tclose() {\n\t\tthis.debug('closing')\n\t\tthis.didDispose = true\n\t\tthis.disposables.forEach((d) => d())\n\t}\n\n\tprivate isPersisting = false\n\tprivate didLastWriteError = false\n\t// eslint-disable-next-line no-restricted-globals\n\tprivate scheduledPersistTimeout: ReturnType<typeof setTimeout> | null = null\n\n\t/**\n\t * Schedule a persist. Persists don't happen immediately: they are throttled to avoid writing too\n\t * often, and will retry if failed.\n\t *\n\t * @internal\n\t */\n\tprivate schedulePersist() {\n\t\tthis.debug('schedulePersist', this.scheduledPersistTimeout)\n\t\tif (this.scheduledPersistTimeout) return\n\t\t// eslint-disable-next-line no-restricted-globals\n\t\tthis.scheduledPersistTimeout = setTimeout(\n\t\t\t() => {\n\t\t\t\tthis.scheduledPersistTimeout = null\n\t\t\t\tthis.persistIfNeeded()\n\t\t\t},\n\t\t\tthis.didLastWriteError ? PERSIST_RETRY_THROTTLE_MS : PERSIST_THROTTLE_MS\n\t\t)\n\t}\n\n\t/**\n\t * Persist to IndexedDB only under certain circumstances:\n\t *\n\t * - If we're not already persisting\n\t * - If we're not reloading the page\n\t * - And we have something to persist (a full db write scheduled or changes in the diff queue)\n\t *\n\t * @internal\n\t */\n\tprivate persistIfNeeded() {\n\t\tthis.debug('persistIfNeeded', {\n\t\t\tisPersisting: this.isPersisting,\n\t\t\tisReloading: this.isReloading,\n\t\t\tshouldDoFullDBWrite: this.shouldDoFullDBWrite,\n\t\t\tdiffQueueLength: this.diffQueue.length,\n\t\t\tstoreIsPossiblyCorrupt: this.store.isPossiblyCorrupted(),\n\t\t})\n\n\t\t// if we've scheduled a persist for the future, that's no longer needed\n\t\tif (this.scheduledPersistTimeout) {\n\t\t\tclearTimeout(this.scheduledPersistTimeout)\n\t\t\tthis.scheduledPersistTimeout = null\n\t\t}\n\n\t\t// if a persist is already in progress, we don't need to do anything -\n\t\t// if there are still outstanding changes once it's finished, it'll\n\t\t// schedule another persist\n\t\tif (this.isPersisting) return\n\n\t\t// if we're reloading the page, it's because there's a newer client\n\t\t// present so lets not overwrite their changes\n\t\tif (this.isReloading) return\n\n\t\t// if the store is possibly corrupted, we don't want to persist\n\t\tif (this.store.isPossiblyCorrupted()) return\n\n\t\t// if we're scheduled for a full write or if we have changes outstanding, let's persist them!\n\t\tif (this.shouldDoFullDBWrite || this.diffQueue.length > 0) {\n\t\t\tthis.doPersist()\n\t\t}\n\t}\n\n\t/**\n\t * Actually persist to IndexedDB. If the write fails, then we'll retry with a full db write after\n\t * a short delay.\n\t */\n\tprivate async doPersist() {\n\t\tassert(!this.isPersisting, 'persist already in progress')\n\t\tif (this.didDispose) return\n\t\tthis.isPersisting = true\n\n\t\tthis.debug('doPersist start')\n\n\t\t// instantly empty the diff queue, but keep our own copy of it. this way\n\t\t// diffs that come in during the persist will still get tracked\n\t\tconst diffQueue = this.diffQueue\n\t\tthis.diffQueue = []\n\n\t\ttry {\n\t\t\tif (this.shouldDoFullDBWrite) {\n\t\t\t\tthis.shouldDoFullDBWrite = false\n\t\t\t\tawait this.db.storeSnapshot({\n\t\t\t\t\tschema: this.store.schema,\n\t\t\t\t\tsnapshot: this.store.serialize(),\n\t\t\t\t\tsessionId: this.sessionId,\n\t\t\t\t\tsessionStateSnapshot: this.$sessionStateSnapshot.get(),\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tconst diffs = squashRecordDiffs(\n\t\t\t\t\tdiffQueue.filter((d): d is RecordsDiff<UnknownRecord> => d !== UPDATE_INSTANCE_STATE)\n\t\t\t\t)\n\t\t\t\tawait this.db.storeChanges({\n\t\t\t\t\tchanges: diffs,\n\t\t\t\t\tschema: this.store.schema,\n\t\t\t\t\tsessionId: this.sessionId,\n\t\t\t\t\tsessionStateSnapshot: this.$sessionStateSnapshot.get(),\n\t\t\t\t})\n\t\t\t}\n\t\t\tthis.didLastWriteError = false\n\t\t} catch (e) {\n\t\t\t// set this.shouldDoFullDBWrite because we clear the diffQueue no matter what,\n\t\t\t// so if this is just a temporary error, we will still persist all changes\n\t\t\tthis.shouldDoFullDBWrite = true\n\t\t\tthis.didLastWriteError = true\n\t\t\tconsole.error('failed to store changes in indexed db', e)\n\n\t\t\tshowCantWriteToIndexDbAlert()\n\t\t\tif (typeof window !== 'undefined') {\n\t\t\t\t// adios\n\t\t\t\twindow.location.reload()\n\t\t\t}\n\t\t}\n\n\t\tthis.isPersisting = false\n\t\tthis.debug('doPersist end')\n\n\t\t// changes might have come in between when we started the persist and\n\t\t// now. we request another persist so any new changes can get written\n\t\tthis.schedulePersist()\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["msg", "data"]
|
|
7
7
|
}
|
package/dist-cjs/version.js
CHANGED
|
@@ -22,10 +22,10 @@ __export(version_exports, {
|
|
|
22
22
|
version: () => version
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(version_exports);
|
|
25
|
-
const version = "3.8.0-canary.
|
|
25
|
+
const version = "3.8.0-canary.f50a5a203139";
|
|
26
26
|
const publishDates = {
|
|
27
27
|
major: "2024-09-13T14:36:29.063Z",
|
|
28
|
-
minor: "2025-
|
|
29
|
-
patch: "2025-
|
|
28
|
+
minor: "2025-02-07T11:50:31.889Z",
|
|
29
|
+
patch: "2025-02-07T11:50:31.889Z"
|
|
30
30
|
};
|
|
31
31
|
//# sourceMappingURL=version.js.map
|
package/dist-cjs/version.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.8.0-canary.
|
|
4
|
+
"sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.8.0-canary.f50a5a203139'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-02-07T11:50:31.889Z',\n\tpatch: '2025-02-07T11:50:31.889Z',\n}\n"],
|
|
5
5
|
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|