@tldraw/tlschema 4.6.0-next.5a871ec02ff3 → 4.6.0-next.6594d48ace27
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/TLStore.js +13 -0
- package/dist-cjs/TLStore.js.map +2 -2
- package/dist-cjs/assets/TLBaseAsset.js +4 -3
- package/dist-cjs/assets/TLBaseAsset.js.map +2 -2
- package/dist-cjs/assets/TLBookmarkAsset.js +9 -7
- package/dist-cjs/assets/TLBookmarkAsset.js.map +2 -2
- package/dist-cjs/assets/TLImageAsset.js +12 -10
- package/dist-cjs/assets/TLImageAsset.js.map +2 -2
- package/dist-cjs/assets/TLVideoAsset.js +11 -9
- package/dist-cjs/assets/TLVideoAsset.js.map +2 -2
- package/dist-cjs/createPresenceStateDerivation.js +6 -4
- package/dist-cjs/createPresenceStateDerivation.js.map +2 -2
- package/dist-cjs/createTLSchema.js +19 -6
- package/dist-cjs/createTLSchema.js.map +2 -2
- package/dist-cjs/index.d.ts +826 -371
- package/dist-cjs/index.js +25 -7
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/misc/TLScribble.js +1 -1
- package/dist-cjs/misc/TLScribble.js.map +2 -2
- package/dist-cjs/records/TLAsset.js +29 -14
- package/dist-cjs/records/TLAsset.js.map +2 -2
- package/dist-cjs/records/TLCustomRecord.js.map +1 -1
- package/dist-cjs/records/TLPageState.js.map +2 -2
- package/dist-cjs/records/TLRecord.js.map +1 -1
- package/dist-cjs/records/TLUser.js +101 -0
- package/dist-cjs/records/TLUser.js.map +7 -0
- package/dist-cjs/recordsWithProps.js.map +1 -1
- package/dist-cjs/shapes/TLArrowShape.js +1 -1
- package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
- package/dist-cjs/shapes/TLBaseShape.js +1 -1
- package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
- package/dist-cjs/shapes/TLFrameShape.js +3 -1
- package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
- package/dist-cjs/shapes/TLNoteShape.js +24 -3
- package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
- package/dist-cjs/styles/StyleProp.js +41 -1
- package/dist-cjs/styles/StyleProp.js.map +2 -2
- package/dist-cjs/styles/TLColorStyle.js +36 -487
- package/dist-cjs/styles/TLColorStyle.js.map +2 -2
- package/dist-cjs/styles/TLDashStyle.js +1 -1
- package/dist-cjs/styles/TLDashStyle.js.map +2 -2
- package/dist-cjs/styles/TLFontFace.js +17 -0
- package/dist-cjs/styles/TLFontFace.js.map +7 -0
- package/dist-cjs/styles/TLFontStyle.js +36 -1
- package/dist-cjs/styles/TLFontStyle.js.map +2 -2
- package/dist-cjs/styles/TLTheme.js +17 -0
- package/dist-cjs/styles/TLTheme.js.map +7 -0
- package/dist-esm/TLStore.mjs +13 -0
- package/dist-esm/TLStore.mjs.map +2 -2
- package/dist-esm/assets/TLBaseAsset.mjs +4 -3
- package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
- package/dist-esm/assets/TLBookmarkAsset.mjs +9 -7
- package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
- package/dist-esm/assets/TLImageAsset.mjs +12 -10
- package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
- package/dist-esm/assets/TLVideoAsset.mjs +11 -9
- package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
- package/dist-esm/createPresenceStateDerivation.mjs +6 -4
- package/dist-esm/createPresenceStateDerivation.mjs.map +2 -2
- package/dist-esm/createTLSchema.mjs +26 -10
- package/dist-esm/createTLSchema.mjs.map +2 -2
- package/dist-esm/index.d.mts +826 -371
- package/dist-esm/index.mjs +44 -14
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/misc/TLScribble.mjs +1 -1
- package/dist-esm/misc/TLScribble.mjs.map +2 -2
- package/dist-esm/records/TLAsset.mjs +29 -14
- package/dist-esm/records/TLAsset.mjs.map +2 -2
- package/dist-esm/records/TLCustomRecord.mjs.map +1 -1
- package/dist-esm/records/TLPageState.mjs.map +2 -2
- package/dist-esm/records/TLUser.mjs +85 -0
- package/dist-esm/records/TLUser.mjs.map +7 -0
- package/dist-esm/recordsWithProps.mjs.map +1 -1
- package/dist-esm/shapes/TLArrowShape.mjs +1 -1
- package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
- package/dist-esm/shapes/TLBaseShape.mjs +1 -1
- package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
- package/dist-esm/shapes/TLFrameShape.mjs +3 -1
- package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
- package/dist-esm/shapes/TLNoteShape.mjs +24 -3
- package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
- package/dist-esm/styles/StyleProp.mjs +41 -1
- package/dist-esm/styles/StyleProp.mjs.map +2 -2
- package/dist-esm/styles/TLColorStyle.mjs +36 -487
- package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
- package/dist-esm/styles/TLDashStyle.mjs +1 -1
- package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
- package/dist-esm/styles/TLFontFace.mjs +1 -0
- package/dist-esm/styles/TLFontFace.mjs.map +7 -0
- package/dist-esm/styles/TLFontStyle.mjs +36 -1
- package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
- package/dist-esm/styles/TLTheme.mjs +1 -0
- package/dist-esm/styles/TLTheme.mjs.map +7 -0
- package/package.json +6 -6
- package/src/TLStore.test.ts +5 -0
- package/src/TLStore.ts +95 -1
- package/src/assets/TLBaseAsset.ts +25 -28
- package/src/assets/TLBookmarkAsset.ts +13 -33
- package/src/assets/TLImageAsset.ts +16 -42
- package/src/assets/TLVideoAsset.ts +15 -40
- package/src/createPresenceStateDerivation.test.ts +33 -20
- package/src/createPresenceStateDerivation.ts +21 -26
- package/src/createTLSchema.ts +107 -16
- package/src/index.ts +44 -12
- package/src/migrations.test.ts +44 -0
- package/src/misc/TLScribble.ts +1 -1
- package/src/records/TLAsset.ts +139 -74
- package/src/records/TLCustomRecord.ts +1 -1
- package/src/records/TLPageState.ts +4 -2
- package/src/records/TLRecord.ts +2 -0
- package/src/records/TLUser.ts +148 -0
- package/src/recordsWithProps.ts +1 -1
- package/src/shapes/TLArrowShape.ts +1 -1
- package/src/shapes/TLBaseShape.ts +1 -1
- package/src/shapes/TLFrameShape.ts +3 -1
- package/src/shapes/TLNoteShape.ts +32 -5
- package/src/styles/StyleProp.ts +42 -5
- package/src/styles/TLColorStyle.ts +55 -698
- package/src/styles/TLDashStyle.ts +1 -1
- package/src/styles/TLFontFace.ts +65 -0
- package/src/styles/TLFontStyle.ts +53 -4
- package/src/styles/TLTheme.ts +261 -0
package/dist-cjs/TLStore.js
CHANGED
|
@@ -18,11 +18,13 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var TLStore_exports = {};
|
|
20
20
|
__export(TLStore_exports, {
|
|
21
|
+
createCachedUserResolve: () => createCachedUserResolve,
|
|
21
22
|
createIntegrityChecker: () => createIntegrityChecker,
|
|
22
23
|
onValidationFailure: () => onValidationFailure,
|
|
23
24
|
redactRecordForErrorReporting: () => redactRecordForErrorReporting
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(TLStore_exports);
|
|
27
|
+
var import_state = require("@tldraw/state");
|
|
26
28
|
var import_utils = require("@tldraw/utils");
|
|
27
29
|
var import_TLCamera = require("./records/TLCamera");
|
|
28
30
|
var import_TLDocument = require("./records/TLDocument");
|
|
@@ -40,6 +42,17 @@ function redactRecordForErrorReporting(record) {
|
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
}
|
|
45
|
+
function createCachedUserResolve(resolveFn) {
|
|
46
|
+
const cache = /* @__PURE__ */ new Map();
|
|
47
|
+
return (userId) => {
|
|
48
|
+
let signal = cache.get(userId);
|
|
49
|
+
if (!signal) {
|
|
50
|
+
signal = (0, import_state.computed)("resolve-user-" + userId, () => resolveFn(userId));
|
|
51
|
+
cache.set(userId, signal);
|
|
52
|
+
}
|
|
53
|
+
return signal;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
43
56
|
function onValidationFailure({
|
|
44
57
|
error,
|
|
45
58
|
phase,
|
package/dist-cjs/TLStore.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/TLStore.ts"],
|
|
4
|
-
"sourcesContent": ["import { Signal } from '@tldraw/state'\nimport {\n\tSerializedStore,\n\tStore,\n\tStoreSchema,\n\tStoreSnapshot,\n\tStoreValidationFailure,\n} from '@tldraw/store'\nimport { IndexKey, JsonObject, annotateError, sortByIndex, structuredClone } from '@tldraw/utils'\nimport { TLAsset, TLAssetId } from './records/TLAsset'\nimport { CameraRecordType, TLCameraId } from './records/TLCamera'\nimport { DocumentRecordType, TLDOCUMENT_ID } from './records/TLDocument'\nimport { TLINSTANCE_ID } from './records/TLInstance'\nimport { PageRecordType, TLPageId } from './records/TLPage'\nimport { InstancePageStateRecordType, TLInstancePageStateId } from './records/TLPageState'\nimport { PointerRecordType, TLPOINTER_ID } from './records/TLPointer'\nimport { TLRecord } from './records/TLRecord'\n\n/**\n * Redacts the source of an asset record for error reporting.\n *\n * @param record - The asset record to redact\n * @returns The redacted record\n * @internal\n */\nexport function redactRecordForErrorReporting(record: any) {\n\tif (record.typeName === 'asset') {\n\t\tif ('src' in record) {\n\t\t\trecord.src = '<redacted>'\n\t\t}\n\n\t\tif ('src' in record.props) {\n\t\t\trecord.props.src = '<redacted>'\n\t\t}\n\t}\n}\n\n/**\n * The complete schema type for a tldraw store, defining the structure and validation rules\n * for all tldraw records and store properties.\n *\n * @public\n * @example\n * ```ts\n * import { createTLSchema } from '@tldraw/tlschema'\n *\n * const schema = createTLSchema()\n * const storeSchema: TLStoreSchema = schema\n * ```\n */\nexport type TLStoreSchema = StoreSchema<TLRecord, TLStoreProps>\n\n/**\n * A serialized representation of a tldraw store that can be persisted or transmitted.\n * Contains all store records in a JSON-serializable format.\n *\n * @public\n * @example\n * ```ts\n * // Serialize a store\n * const serializedStore: TLSerializedStore = store.serialize()\n *\n * // Save to localStorage\n * localStorage.setItem('drawing', JSON.stringify(serializedStore))\n * ```\n */\nexport type TLSerializedStore = SerializedStore<TLRecord>\n\n/**\n * A snapshot of a tldraw store at a specific point in time, containing all records\n * and metadata. Used for persistence, synchronization, and creating store backups.\n *\n * @public\n * @example\n * ```ts\n * // Create a snapshot\n * const snapshot: TLStoreSnapshot = store.getSnapshot()\n *\n * // Restore from snapshot\n * store.loadSnapshot(snapshot)\n * ```\n */\nexport type TLStoreSnapshot = StoreSnapshot<TLRecord>\n\n/**\n * Context information provided when resolving asset URLs, containing details about\n * the current rendering environment and user's connection to optimize asset delivery.\n *\n * @public\n * @example\n * ```ts\n * const assetStore: TLAssetStore = {\n * async resolve(asset, context: TLAssetContext) {\n * // Use low resolution for slow connections\n * if (context.networkEffectiveType === 'slow-2g') {\n * return `${asset.props.src}?quality=low`\n * }\n * // Use high DPI version for retina displays\n * if (context.dpr > 1) {\n * return `${asset.props.src}@2x`\n * }\n * return asset.props.src\n * }\n * }\n * ```\n */\nexport interface TLAssetContext {\n\t/**\n\t * The scale at which the asset is being rendered on-screen relative to its native dimensions.\n\t * If the asset is 1000px wide, but it's been resized/zoom so it takes 500px on-screen, this\n\t * will be 0.5.\n\t *\n\t * The scale measures CSS pixels, not device pixels.\n\t */\n\tscreenScale: number\n\t/** The {@link TLAssetContext.screenScale}, stepped to the nearest power-of-2 multiple. */\n\tsteppedScreenScale: number\n\t/** The device pixel ratio - how many CSS pixels are in one device pixel? */\n\tdpr: number\n\t/**\n\t * An alias for\n\t * {@link https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType | `navigator.connection.effectiveType` }\n\t * if it's available in the current browser. Use this to e.g. serve lower-resolution images to\n\t * users on slow connections.\n\t */\n\tnetworkEffectiveType: string | null\n\t/**\n\t * In some circumstances, we need to resolve a URL that points to the original version of a\n\t * particular asset. This is used when the asset will leave the current tldraw instance - e.g.\n\t * for copy/paste, or exports.\n\t */\n\tshouldResolveToOriginal: boolean\n}\n\n/**\n * Interface for storing and managing assets (images, videos, etc.) in tldraw.\n * Provides methods for uploading, resolving, and removing assets from storage.\n *\n * A `TLAssetStore` sits alongside the main {@link TLStore} and is responsible for storing and\n * retrieving large assets such as images. Generally, this should be part of a wider sync system:\n *\n * - By default, the store is in-memory only, so `TLAssetStore` converts images to data URLs\n * - When using\n * {@link @tldraw/editor#TldrawEditorWithoutStoreProps.persistenceKey | `persistenceKey`}, the\n * store is synced to the browser's local IndexedDB, so `TLAssetStore` stores images there too\n * - When using a multiplayer sync server, you would implement `TLAssetStore` to upload images to\n * e.g. an S3 bucket.\n *\n * @public\n * @example\n * ```ts\n * // Simple in-memory asset store\n * const assetStore: TLAssetStore = {\n * async upload(asset, file) {\n * const dataUrl = await fileToDataUrl(file)\n * return { src: dataUrl }\n * },\n *\n * async resolve(asset, context) {\n * return asset.props.src\n * },\n *\n * async remove(assetIds) {\n * // Clean up if needed\n * }\n * }\n * ```\n */\nexport interface TLAssetStore {\n\t/**\n\t * Upload an asset to your storage, returning a URL that can be used to refer to the asset\n\t * long-term.\n\t *\n\t * @param asset - Information & metadata about the asset being uploaded\n\t * @param file - The `File` to be uploaded\n\t * @returns A promise that resolves to the URL of the uploaded asset\n\t */\n\tupload(\n\t\tasset: TLAsset,\n\t\tfile: File,\n\t\tabortSignal?: AbortSignal\n\t): Promise<{ src: string; meta?: JsonObject }>\n\t/**\n\t * Resolve an asset to a URL. This is used when rendering the asset in the editor. By default,\n\t * this will just use `asset.props.src`, the URL returned by `upload()`. This can be used to\n\t * rewrite that URL to add access credentials, or optimized the asset for how it's currently\n\t * being displayed using the {@link TLAssetContext | information provided}.\n\t *\n\t * @param asset - the asset being resolved\n\t * @param ctx - information about the current environment and where the asset is being used\n\t * @returns The URL of the resolved asset, or `null` if the asset is not available\n\t */\n\tresolve?(asset: TLAsset, ctx: TLAssetContext): Promise<string | null> | string | null\n\t/**\n\t * Remove an asset from storage. This is called when the asset is no longer needed, e.g. when\n\t * the user deletes it from the editor.\n\t * @param asset - the asset being removed\n\t * @returns A promise that resolves when the asset has been removed\n\t */\n\tremove?(assetIds: TLAssetId[]): Promise<void>\n}\n\n/**\n * Configuration properties for a tldraw store, defining its behavior and integrations.\n * These props are passed when creating a new store instance.\n *\n * @public\n * @example\n * ```ts\n * const storeProps: TLStoreProps = {\n * defaultName: 'My Drawing',\n * assets: myAssetStore,\n * onMount: (editor) => {\n * console.log('Editor mounted')\n * return () => console.log('Editor unmounted')\n * },\n * collaboration: {\n * status: statusSignal,\n * mode: modeSignal\n * }\n * }\n *\n * const store = new Store({ schema, props: storeProps })\n * ```\n */\nexport interface TLStoreProps {\n\t/** Default name for new documents created in this store */\n\tdefaultName: string\n\t/** Asset store implementation for handling file uploads and storage */\n\tassets: Required<TLAssetStore>\n\t/**\n\t * Called when an {@link @tldraw/editor#Editor} connected to this store is mounted.\n\t * Can optionally return a cleanup function that will be called when unmounted.\n\t *\n\t * @param editor - The editor instance that was mounted\n\t * @returns Optional cleanup function\n\t */\n\tonMount(editor: unknown): void | (() => void)\n\t/** Optional collaboration configuration for multiplayer features */\n\tcollaboration?: {\n\t\t/** Signal indicating online/offline collaboration status */\n\t\tstatus: Signal<'online' | 'offline'> | null\n\t\t/** Signal indicating collaboration mode permissions */\n\t\tmode?: Signal<'readonly' | 'readwrite'> | null\n\t}\n}\n\n/**\n * The main tldraw store type, representing a reactive database of tldraw records\n * with associated store properties. This is the central data structure that holds\n * all shapes, assets, pages, and user state.\n *\n * @public\n * @example\n * ```ts\n * import { Store } from '@tldraw/store'\n * import { createTLSchema } from '@tldraw/tlschema'\n *\n * const schema = createTLSchema()\n * const store: TLStore = new Store({\n * schema,\n * props: {\n * defaultName: 'Untitled',\n * assets: myAssetStore,\n * onMount: () => console.log('Store mounted')\n * }\n * })\n * ```\n */\nexport type TLStore = Store<TLRecord, TLStoreProps>\n\n/**\n * Default validation failure handler for tldraw stores. This function is called\n * when a record fails validation during store operations. It annotates errors\n * with debugging information and determines whether to allow invalid records\n * during store initialization.\n *\n * @param options - The validation failure details\n * - error - The validation error that occurred\n * - phase - The store operation phase when validation failed\n * - record - The invalid record that caused the failure\n * - recordBefore - The previous state of the record (if applicable)\n * @returns The record to use (typically throws the annotated error)\n * @throws The original validation error with additional debugging context\n *\n * @public\n * @example\n * ```ts\n * const store = new Store({\n * schema,\n * props: storeProps,\n * onValidationFailure // Use this as the validation failure handler\n * })\n *\n * // The handler will be called automatically when validation fails\n * try {\n * store.put([invalidRecord])\n * } catch (error) {\n * // Error will contain debugging information added by onValidationFailure\n * }\n * ```\n */\nexport function onValidationFailure({\n\terror,\n\tphase,\n\trecord,\n\trecordBefore,\n}: StoreValidationFailure<TLRecord>): TLRecord {\n\tconst isExistingValidationIssue =\n\t\t// if we're initializing the store for the first time, we should\n\t\t// allow invalid records so people can load old buggy data:\n\t\tphase === 'initialize'\n\n\tannotateError(error, {\n\t\ttags: {\n\t\t\torigin: 'store.validateRecord',\n\t\t\tstorePhase: phase,\n\t\t\tisExistingValidationIssue,\n\t\t},\n\t\textras: {\n\t\t\trecordBefore: recordBefore\n\t\t\t\t? redactRecordForErrorReporting(structuredClone(recordBefore))\n\t\t\t\t: undefined,\n\t\t\trecordAfter: redactRecordForErrorReporting(structuredClone(record)),\n\t\t},\n\t})\n\n\tthrow error\n}\n\nfunction getDefaultPages() {\n\treturn [\n\t\tPageRecordType.create({\n\t\t\tid: 'page:page' as TLPageId,\n\t\t\tname: 'Page 1',\n\t\t\tindex: 'a1' as IndexKey,\n\t\t\tmeta: {},\n\t\t}),\n\t]\n}\n\n/**\n * Creates an integrity checker function that ensures the tldraw store maintains\n * a consistent and usable state. The checker validates that required records exist\n * and relationships between records are maintained.\n *\n * The integrity checker ensures:\n * - Document and pointer records exist\n * - At least one page exists\n * - Instance state references valid pages\n * - Page states and cameras exist for all pages\n * - Shape references in page states are valid\n *\n * @param store - The tldraw store to check for integrity\n * @returns A function that when called, validates and fixes store integrity\n *\n * @internal\n * @example\n * ```ts\n * const checker = createIntegrityChecker(store)\n *\n * // Run integrity check (typically called automatically)\n * checker()\n *\n * // The checker will create missing records and fix invalid references\n * ```\n */\nexport function createIntegrityChecker(store: Store<TLRecord, TLStoreProps>): () => void {\n\tconst $pageIds = store.query.ids('page')\n\tconst $pageStates = store.query.records('instance_page_state')\n\n\tconst ensureStoreIsUsable = (): void => {\n\t\t// make sure we have exactly one document\n\t\tif (!store.has(TLDOCUMENT_ID)) {\n\t\t\tstore.put([DocumentRecordType.create({ id: TLDOCUMENT_ID, name: store.props.defaultName })])\n\t\t\treturn ensureStoreIsUsable()\n\t\t}\n\n\t\tif (!store.has(TLPOINTER_ID)) {\n\t\t\tstore.put([PointerRecordType.create({ id: TLPOINTER_ID })])\n\t\t\treturn ensureStoreIsUsable()\n\t\t}\n\n\t\t// make sure there is at least one page\n\t\tconst pageIds = $pageIds.get()\n\t\tif (pageIds.size === 0) {\n\t\t\tstore.put(getDefaultPages())\n\t\t\treturn ensureStoreIsUsable()\n\t\t}\n\n\t\tconst getFirstPageId = () => [...pageIds].map((id) => store.get(id)!).sort(sortByIndex)[0].id!\n\n\t\t// make sure we have state for the current user's current tab\n\t\tconst instanceState = store.get(TLINSTANCE_ID)\n\t\tif (!instanceState) {\n\t\t\tstore.put([\n\t\t\t\tstore.schema.types.instance.create({\n\t\t\t\t\tid: TLINSTANCE_ID,\n\t\t\t\t\tcurrentPageId: getFirstPageId(),\n\t\t\t\t\texportBackground: true,\n\t\t\t\t}),\n\t\t\t])\n\n\t\t\treturn ensureStoreIsUsable()\n\t\t} else if (!pageIds.has(instanceState.currentPageId)) {\n\t\t\tstore.put([{ ...instanceState, currentPageId: getFirstPageId() }])\n\t\t\treturn ensureStoreIsUsable()\n\t\t}\n\n\t\t// make sure we have page states and cameras for all the pages\n\t\tconst missingPageStateIds = new Set<TLInstancePageStateId>()\n\t\tconst missingCameraIds = new Set<TLCameraId>()\n\t\tfor (const id of pageIds) {\n\t\t\tconst pageStateId = InstancePageStateRecordType.createId(id)\n\t\t\tconst pageState = store.get(pageStateId)\n\t\t\tif (!pageState) {\n\t\t\t\tmissingPageStateIds.add(pageStateId)\n\t\t\t}\n\t\t\tconst cameraId = CameraRecordType.createId(id)\n\t\t\tif (!store.has(cameraId)) {\n\t\t\t\tmissingCameraIds.add(cameraId)\n\t\t\t}\n\t\t}\n\n\t\tif (missingPageStateIds.size > 0) {\n\t\t\tstore.put(\n\t\t\t\t[...missingPageStateIds].map((id) =>\n\t\t\t\t\tInstancePageStateRecordType.create({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tpageId: InstancePageStateRecordType.parseId(id) as TLPageId,\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t)\n\t\t}\n\n\t\tif (missingCameraIds.size > 0) {\n\t\t\tstore.put([...missingCameraIds].map((id) => CameraRecordType.create({ id })))\n\t\t}\n\n\t\tconst pageStates = $pageStates.get()\n\t\tfor (const pageState of pageStates) {\n\t\t\tif (!pageIds.has(pageState.pageId)) {\n\t\t\t\tstore.remove([pageState.id])\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (pageState.croppingShapeId && !store.has(pageState.croppingShapeId)) {\n\t\t\t\tstore.put([{ ...pageState, croppingShapeId: null }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tif (pageState.focusedGroupId && !store.has(pageState.focusedGroupId)) {\n\t\t\t\tstore.put([{ ...pageState, focusedGroupId: null }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tif (pageState.hoveredShapeId && !store.has(pageState.hoveredShapeId)) {\n\t\t\t\tstore.put([{ ...pageState, hoveredShapeId: null }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tconst filteredSelectedIds = pageState.selectedShapeIds.filter((id) => store.has(id))\n\t\t\tif (filteredSelectedIds.length !== pageState.selectedShapeIds.length) {\n\t\t\t\tstore.put([{ ...pageState, selectedShapeIds: filteredSelectedIds }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tconst filteredHintingIds = pageState.hintingShapeIds.filter((id) => store.has(id))\n\t\t\tif (filteredHintingIds.length !== pageState.hintingShapeIds.length) {\n\t\t\t\tstore.put([{ ...pageState, hintingShapeIds: filteredHintingIds }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tconst filteredErasingIds = pageState.erasingShapeIds.filter((id) => store.has(id))\n\t\t\tif (filteredErasingIds.length !== pageState.erasingShapeIds.length) {\n\t\t\t\tstore.put([{ ...pageState, erasingShapeIds: filteredErasingIds }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ensureStoreIsUsable\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["import { Signal, computed } from '@tldraw/state'\nimport {\n\tSerializedStore,\n\tStore,\n\tStoreSchema,\n\tStoreSnapshot,\n\tStoreValidationFailure,\n} from '@tldraw/store'\nimport { IndexKey, JsonObject, annotateError, sortByIndex, structuredClone } from '@tldraw/utils'\nimport { TLAsset, TLAssetId } from './records/TLAsset'\nimport { CameraRecordType, TLCameraId } from './records/TLCamera'\nimport { DocumentRecordType, TLDOCUMENT_ID } from './records/TLDocument'\nimport { TLINSTANCE_ID } from './records/TLInstance'\nimport { PageRecordType, TLPageId } from './records/TLPage'\nimport { InstancePageStateRecordType, TLInstancePageStateId } from './records/TLPageState'\nimport { PointerRecordType, TLPOINTER_ID } from './records/TLPointer'\nimport { TLRecord } from './records/TLRecord'\nimport { TLUser } from './records/TLUser'\n\n/**\n * Redacts the source of an asset record for error reporting.\n *\n * @param record - The asset record to redact\n * @returns The redacted record\n * @internal\n */\nexport function redactRecordForErrorReporting(record: any) {\n\tif (record.typeName === 'asset') {\n\t\tif ('src' in record) {\n\t\t\trecord.src = '<redacted>'\n\t\t}\n\n\t\tif ('src' in record.props) {\n\t\t\trecord.props.src = '<redacted>'\n\t\t}\n\t}\n}\n\n/**\n * The complete schema type for a tldraw store, defining the structure and validation rules\n * for all tldraw records and store properties.\n *\n * @public\n * @example\n * ```ts\n * import { createTLSchema } from '@tldraw/tlschema'\n *\n * const schema = createTLSchema()\n * const storeSchema: TLStoreSchema = schema\n * ```\n */\nexport type TLStoreSchema = StoreSchema<TLRecord, TLStoreProps>\n\n/**\n * A serialized representation of a tldraw store that can be persisted or transmitted.\n * Contains all store records in a JSON-serializable format.\n *\n * @public\n * @example\n * ```ts\n * // Serialize a store\n * const serializedStore: TLSerializedStore = store.serialize()\n *\n * // Save to localStorage\n * localStorage.setItem('drawing', JSON.stringify(serializedStore))\n * ```\n */\nexport type TLSerializedStore = SerializedStore<TLRecord>\n\n/**\n * A snapshot of a tldraw store at a specific point in time, containing all records\n * and metadata. Used for persistence, synchronization, and creating store backups.\n *\n * @public\n * @example\n * ```ts\n * // Create a snapshot\n * const snapshot: TLStoreSnapshot = store.getSnapshot()\n *\n * // Restore from snapshot\n * store.loadSnapshot(snapshot)\n * ```\n */\nexport type TLStoreSnapshot = StoreSnapshot<TLRecord>\n\n/**\n * Context information provided when resolving asset URLs, containing details about\n * the current rendering environment and user's connection to optimize asset delivery.\n *\n * @public\n * @example\n * ```ts\n * const assetStore: TLAssetStore = {\n * async resolve(asset, context: TLAssetContext) {\n * // Use low resolution for slow connections\n * if (context.networkEffectiveType === 'slow-2g') {\n * return `${asset.props.src}?quality=low`\n * }\n * // Use high DPI version for retina displays\n * if (context.dpr > 1) {\n * return `${asset.props.src}@2x`\n * }\n * return asset.props.src\n * }\n * }\n * ```\n */\nexport interface TLAssetContext {\n\t/**\n\t * The scale at which the asset is being rendered on-screen relative to its native dimensions.\n\t * If the asset is 1000px wide, but it's been resized/zoom so it takes 500px on-screen, this\n\t * will be 0.5.\n\t *\n\t * The scale measures CSS pixels, not device pixels.\n\t */\n\tscreenScale: number\n\t/** The {@link TLAssetContext.screenScale}, stepped to the nearest power-of-2 multiple. */\n\tsteppedScreenScale: number\n\t/** The device pixel ratio - how many CSS pixels are in one device pixel? */\n\tdpr: number\n\t/**\n\t * An alias for\n\t * {@link https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation/effectiveType | `navigator.connection.effectiveType` }\n\t * if it's available in the current browser. Use this to e.g. serve lower-resolution images to\n\t * users on slow connections.\n\t */\n\tnetworkEffectiveType: string | null\n\t/**\n\t * In some circumstances, we need to resolve a URL that points to the original version of a\n\t * particular asset. This is used when the asset will leave the current tldraw instance - e.g.\n\t * for copy/paste, or exports.\n\t */\n\tshouldResolveToOriginal: boolean\n}\n\n/**\n * Interface for storing and managing assets (images, videos, etc.) in tldraw.\n * Provides methods for uploading, resolving, and removing assets from storage.\n *\n * A `TLAssetStore` sits alongside the main {@link TLStore} and is responsible for storing and\n * retrieving large assets such as images. Generally, this should be part of a wider sync system:\n *\n * - By default, the store is in-memory only, so `TLAssetStore` converts images to data URLs\n * - When using\n * {@link @tldraw/editor#TldrawEditorWithoutStoreProps.persistenceKey | `persistenceKey`}, the\n * store is synced to the browser's local IndexedDB, so `TLAssetStore` stores images there too\n * - When using a multiplayer sync server, you would implement `TLAssetStore` to upload images to\n * e.g. an S3 bucket.\n *\n * @public\n * @example\n * ```ts\n * // Simple in-memory asset store\n * const assetStore: TLAssetStore = {\n * async upload(asset, file) {\n * const dataUrl = await fileToDataUrl(file)\n * return { src: dataUrl }\n * },\n *\n * async resolve(asset, context) {\n * return asset.props.src\n * },\n *\n * async remove(assetIds) {\n * // Clean up if needed\n * }\n * }\n * ```\n */\nexport interface TLAssetStore {\n\t/**\n\t * Upload an asset to your storage, returning a URL that can be used to refer to the asset\n\t * long-term.\n\t *\n\t * @param asset - Information & metadata about the asset being uploaded\n\t * @param file - The `File` to be uploaded\n\t * @returns A promise that resolves to the URL of the uploaded asset\n\t */\n\tupload(\n\t\tasset: TLAsset,\n\t\tfile: File,\n\t\tabortSignal?: AbortSignal\n\t): Promise<{ src: string; meta?: JsonObject }>\n\t/**\n\t * Resolve an asset to a URL. This is used when rendering the asset in the editor. By default,\n\t * this will just use `asset.props.src`, the URL returned by `upload()`. This can be used to\n\t * rewrite that URL to add access credentials, or optimized the asset for how it's currently\n\t * being displayed using the {@link TLAssetContext | information provided}.\n\t *\n\t * @param asset - the asset being resolved\n\t * @param ctx - information about the current environment and where the asset is being used\n\t * @returns The URL of the resolved asset, or `null` if the asset is not available\n\t */\n\tresolve?(asset: TLAsset, ctx: TLAssetContext): Promise<string | null> | string | null\n\t/**\n\t * Remove an asset from storage. This is called when the asset is no longer needed, e.g. when\n\t * the user deletes it from the editor.\n\t * @param asset - the asset being removed\n\t * @returns A promise that resolves when the asset has been removed\n\t */\n\tremove?(assetIds: TLAssetId[]): Promise<void>\n}\n\n/**\n * Interface for resolving user information in tldraw.\n *\n * A `TLUserStore` sits alongside the main {@link TLStore} and provides user\n * resolution for attribution labels and display names. Implement this interface\n * to connect tldraw to your auth/user system.\n *\n * `currentUser` and `resolve` are reactive {@link @tldraw/state#Signal | Signals}\n * so that the editor can automatically track changes to user data and\n * re-render when a user's name, color, or avatar updates.\n *\n * Implementations should cache signals returned by `resolve` \u2014 e.g. return the\n * same `Signal` for repeated calls with the same `userId` \u2014 to avoid\n * unnecessary re-computation.\n *\n * @public\n * @example\n * ```ts\n * const currentUser = computed('currentUser', () =>\n * UserRecordType.create({\n * id: createUserId(myAuth.userId),\n * name: myAuth.displayName,\n * color: myAuth.color,\n * })\n * )\n *\n * const userStore: TLUserStore = {\n * currentUser,\n * resolve(userId) {\n * return computed('resolve-' + userId, () =>\n * myUserCache.get(userId) ?? null\n * )\n * },\n * }\n * ```\n */\nexport interface TLUserStore {\n\t/**\n\t * A signal resolving to the currently authenticated user,\n\t * or `null` for anonymous / unknown.\n\t * Read when stamping attribution on shape create/update.\n\t */\n\tcurrentUser: Signal<TLUser | null>\n\n\t/**\n\t * Return a signal resolving an arbitrary user ID to display info.\n\t * Called when rendering attribution labels for shapes that may have been\n\t * created or edited by someone else.\n\t * The signal's value should be `null` if the user cannot be resolved.\n\t */\n\tresolve?(userId: string): Signal<TLUser | null>\n}\n\n/**\n * Create a cached {@link TLUserStore.resolve} implementation.\n *\n * Wraps a reactive lookup function so that each `userId` gets a single\n * stable {@link @tldraw/state#Signal | Signal} that is reused across calls.\n * The `resolveFn` is evaluated inside a `computed`, so any `.get()` calls\n * it makes are automatically tracked.\n *\n * @param resolveFn - A function that resolves a raw user-ID string to a\n * {@link TLUser} or `null`. Called reactively inside a `computed`.\n * @returns A function suitable for use as `TLUserStore.resolve`.\n *\n * @example\n * ```ts\n * const users: TLUserStore = {\n * currentUser: currentUserSignal,\n * resolve: createCachedUserResolve(\n * (userId) => usersAtom.get()[createUserId(userId)] ?? null\n * ),\n * }\n * ```\n *\n * @public\n */\nexport function createCachedUserResolve(\n\tresolveFn: (userId: string) => TLUser | null\n): (userId: string) => Signal<TLUser | null> {\n\tconst cache = new Map<string, Signal<TLUser | null>>()\n\treturn (userId: string) => {\n\t\tlet signal = cache.get(userId)\n\t\tif (!signal) {\n\t\t\tsignal = computed('resolve-user-' + userId, () => resolveFn(userId))\n\t\t\tcache.set(userId, signal)\n\t\t}\n\t\treturn signal\n\t}\n}\n\n/**\n * Configuration properties for a tldraw store, defining its behavior and integrations.\n * These props are passed when creating a new store instance.\n *\n * @public\n * @example\n * ```ts\n * const storeProps: TLStoreProps = {\n * defaultName: 'My Drawing',\n * assets: myAssetStore,\n * onMount: (editor) => {\n * console.log('Editor mounted')\n * return () => console.log('Editor unmounted')\n * },\n * collaboration: {\n * status: statusSignal,\n * mode: modeSignal\n * }\n * }\n *\n * const store = new Store({ schema, props: storeProps })\n * ```\n */\nexport interface TLStoreProps {\n\t/** Default name for new documents created in this store */\n\tdefaultName: string\n\t/** Asset store implementation for handling file uploads and storage */\n\tassets: Required<TLAssetStore>\n\t/** User store implementation for user resolution and attribution */\n\tusers: Required<TLUserStore>\n\t/**\n\t * Called when an {@link @tldraw/editor#Editor} connected to this store is mounted.\n\t * Can optionally return a cleanup function that will be called when unmounted.\n\t *\n\t * @param editor - The editor instance that was mounted\n\t * @returns Optional cleanup function\n\t */\n\tonMount(editor: unknown): void | (() => void)\n\t/** Optional collaboration configuration for multiplayer features */\n\tcollaboration?: {\n\t\t/** Signal indicating online/offline collaboration status */\n\t\tstatus: Signal<'online' | 'offline'> | null\n\t\t/** Signal indicating collaboration mode permissions */\n\t\tmode?: Signal<'readonly' | 'readwrite'> | null\n\t}\n}\n\n/**\n * The main tldraw store type, representing a reactive database of tldraw records\n * with associated store properties. This is the central data structure that holds\n * all shapes, assets, pages, and user state.\n *\n * @public\n * @example\n * ```ts\n * import { Store } from '@tldraw/store'\n * import { createTLSchema } from '@tldraw/tlschema'\n *\n * const schema = createTLSchema()\n * const store: TLStore = new Store({\n * schema,\n * props: {\n * defaultName: 'Untitled',\n * assets: myAssetStore,\n * onMount: () => console.log('Store mounted')\n * }\n * })\n * ```\n */\nexport type TLStore = Store<TLRecord, TLStoreProps>\n\n/**\n * Default validation failure handler for tldraw stores. This function is called\n * when a record fails validation during store operations. It annotates errors\n * with debugging information and determines whether to allow invalid records\n * during store initialization.\n *\n * @param options - The validation failure details\n * - error - The validation error that occurred\n * - phase - The store operation phase when validation failed\n * - record - The invalid record that caused the failure\n * - recordBefore - The previous state of the record (if applicable)\n * @returns The record to use (typically throws the annotated error)\n * @throws The original validation error with additional debugging context\n *\n * @public\n * @example\n * ```ts\n * const store = new Store({\n * schema,\n * props: storeProps,\n * onValidationFailure // Use this as the validation failure handler\n * })\n *\n * // The handler will be called automatically when validation fails\n * try {\n * store.put([invalidRecord])\n * } catch (error) {\n * // Error will contain debugging information added by onValidationFailure\n * }\n * ```\n */\nexport function onValidationFailure({\n\terror,\n\tphase,\n\trecord,\n\trecordBefore,\n}: StoreValidationFailure<TLRecord>): TLRecord {\n\tconst isExistingValidationIssue =\n\t\t// if we're initializing the store for the first time, we should\n\t\t// allow invalid records so people can load old buggy data:\n\t\tphase === 'initialize'\n\n\tannotateError(error, {\n\t\ttags: {\n\t\t\torigin: 'store.validateRecord',\n\t\t\tstorePhase: phase,\n\t\t\tisExistingValidationIssue,\n\t\t},\n\t\textras: {\n\t\t\trecordBefore: recordBefore\n\t\t\t\t? redactRecordForErrorReporting(structuredClone(recordBefore))\n\t\t\t\t: undefined,\n\t\t\trecordAfter: redactRecordForErrorReporting(structuredClone(record)),\n\t\t},\n\t})\n\n\tthrow error\n}\n\nfunction getDefaultPages() {\n\treturn [\n\t\tPageRecordType.create({\n\t\t\tid: 'page:page' as TLPageId,\n\t\t\tname: 'Page 1',\n\t\t\tindex: 'a1' as IndexKey,\n\t\t\tmeta: {},\n\t\t}),\n\t]\n}\n\n/**\n * Creates an integrity checker function that ensures the tldraw store maintains\n * a consistent and usable state. The checker validates that required records exist\n * and relationships between records are maintained.\n *\n * The integrity checker ensures:\n * - Document and pointer records exist\n * - At least one page exists\n * - Instance state references valid pages\n * - Page states and cameras exist for all pages\n * - Shape references in page states are valid\n *\n * @param store - The tldraw store to check for integrity\n * @returns A function that when called, validates and fixes store integrity\n *\n * @internal\n * @example\n * ```ts\n * const checker = createIntegrityChecker(store)\n *\n * // Run integrity check (typically called automatically)\n * checker()\n *\n * // The checker will create missing records and fix invalid references\n * ```\n */\nexport function createIntegrityChecker(store: Store<TLRecord, TLStoreProps>): () => void {\n\tconst $pageIds = store.query.ids('page')\n\tconst $pageStates = store.query.records('instance_page_state')\n\n\tconst ensureStoreIsUsable = (): void => {\n\t\t// make sure we have exactly one document\n\t\tif (!store.has(TLDOCUMENT_ID)) {\n\t\t\tstore.put([DocumentRecordType.create({ id: TLDOCUMENT_ID, name: store.props.defaultName })])\n\t\t\treturn ensureStoreIsUsable()\n\t\t}\n\n\t\tif (!store.has(TLPOINTER_ID)) {\n\t\t\tstore.put([PointerRecordType.create({ id: TLPOINTER_ID })])\n\t\t\treturn ensureStoreIsUsable()\n\t\t}\n\n\t\t// make sure there is at least one page\n\t\tconst pageIds = $pageIds.get()\n\t\tif (pageIds.size === 0) {\n\t\t\tstore.put(getDefaultPages())\n\t\t\treturn ensureStoreIsUsable()\n\t\t}\n\n\t\tconst getFirstPageId = () => [...pageIds].map((id) => store.get(id)!).sort(sortByIndex)[0].id!\n\n\t\t// make sure we have state for the current user's current tab\n\t\tconst instanceState = store.get(TLINSTANCE_ID)\n\t\tif (!instanceState) {\n\t\t\tstore.put([\n\t\t\t\tstore.schema.types.instance.create({\n\t\t\t\t\tid: TLINSTANCE_ID,\n\t\t\t\t\tcurrentPageId: getFirstPageId(),\n\t\t\t\t\texportBackground: true,\n\t\t\t\t}),\n\t\t\t])\n\n\t\t\treturn ensureStoreIsUsable()\n\t\t} else if (!pageIds.has(instanceState.currentPageId)) {\n\t\t\tstore.put([{ ...instanceState, currentPageId: getFirstPageId() }])\n\t\t\treturn ensureStoreIsUsable()\n\t\t}\n\n\t\t// make sure we have page states and cameras for all the pages\n\t\tconst missingPageStateIds = new Set<TLInstancePageStateId>()\n\t\tconst missingCameraIds = new Set<TLCameraId>()\n\t\tfor (const id of pageIds) {\n\t\t\tconst pageStateId = InstancePageStateRecordType.createId(id)\n\t\t\tconst pageState = store.get(pageStateId)\n\t\t\tif (!pageState) {\n\t\t\t\tmissingPageStateIds.add(pageStateId)\n\t\t\t}\n\t\t\tconst cameraId = CameraRecordType.createId(id)\n\t\t\tif (!store.has(cameraId)) {\n\t\t\t\tmissingCameraIds.add(cameraId)\n\t\t\t}\n\t\t}\n\n\t\tif (missingPageStateIds.size > 0) {\n\t\t\tstore.put(\n\t\t\t\t[...missingPageStateIds].map((id) =>\n\t\t\t\t\tInstancePageStateRecordType.create({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tpageId: InstancePageStateRecordType.parseId(id) as TLPageId,\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t)\n\t\t}\n\n\t\tif (missingCameraIds.size > 0) {\n\t\t\tstore.put([...missingCameraIds].map((id) => CameraRecordType.create({ id })))\n\t\t}\n\n\t\tconst pageStates = $pageStates.get()\n\t\tfor (const pageState of pageStates) {\n\t\t\tif (!pageIds.has(pageState.pageId)) {\n\t\t\t\tstore.remove([pageState.id])\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (pageState.croppingShapeId && !store.has(pageState.croppingShapeId)) {\n\t\t\t\tstore.put([{ ...pageState, croppingShapeId: null }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tif (pageState.focusedGroupId && !store.has(pageState.focusedGroupId)) {\n\t\t\t\tstore.put([{ ...pageState, focusedGroupId: null }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tif (pageState.hoveredShapeId && !store.has(pageState.hoveredShapeId)) {\n\t\t\t\tstore.put([{ ...pageState, hoveredShapeId: null }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tconst filteredSelectedIds = pageState.selectedShapeIds.filter((id) => store.has(id))\n\t\t\tif (filteredSelectedIds.length !== pageState.selectedShapeIds.length) {\n\t\t\t\tstore.put([{ ...pageState, selectedShapeIds: filteredSelectedIds }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tconst filteredHintingIds = pageState.hintingShapeIds.filter((id) => store.has(id))\n\t\t\tif (filteredHintingIds.length !== pageState.hintingShapeIds.length) {\n\t\t\t\tstore.put([{ ...pageState, hintingShapeIds: filteredHintingIds }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t\tconst filteredErasingIds = pageState.erasingShapeIds.filter((id) => store.has(id))\n\t\t\tif (filteredErasingIds.length !== pageState.erasingShapeIds.length) {\n\t\t\t\tstore.put([{ ...pageState, erasingShapeIds: filteredErasingIds }])\n\t\t\t\treturn ensureStoreIsUsable()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn ensureStoreIsUsable\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAiC;AAQjC,mBAAkF;AAElF,sBAA6C;AAC7C,wBAAkD;AAClD,wBAA8B;AAC9B,oBAAyC;AACzC,yBAAmE;AACnE,uBAAgD;AAWzC,SAAS,8BAA8B,QAAa;AAC1D,MAAI,OAAO,aAAa,SAAS;AAChC,QAAI,SAAS,QAAQ;AACpB,aAAO,MAAM;AAAA,IACd;AAEA,QAAI,SAAS,OAAO,OAAO;AAC1B,aAAO,MAAM,MAAM;AAAA,IACpB;AAAA,EACD;AACD;AAoPO,SAAS,wBACf,WAC4C;AAC5C,QAAM,QAAQ,oBAAI,IAAmC;AACrD,SAAO,CAAC,WAAmB;AAC1B,QAAI,SAAS,MAAM,IAAI,MAAM;AAC7B,QAAI,CAAC,QAAQ;AACZ,mBAAS,uBAAS,kBAAkB,QAAQ,MAAM,UAAU,MAAM,CAAC;AACnE,YAAM,IAAI,QAAQ,MAAM;AAAA,IACzB;AACA,WAAO;AAAA,EACR;AACD;AAwGO,SAAS,oBAAoB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAA+C;AAC9C,QAAM;AAAA;AAAA;AAAA,IAGL,UAAU;AAAA;AAEX,kCAAc,OAAO;AAAA,IACpB,MAAM;AAAA,MACL,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ;AAAA,IACD;AAAA,IACA,QAAQ;AAAA,MACP,cAAc,eACX,kCAA8B,8BAAgB,YAAY,CAAC,IAC3D;AAAA,MACH,aAAa,kCAA8B,8BAAgB,MAAM,CAAC;AAAA,IACnE;AAAA,EACD,CAAC;AAED,QAAM;AACP;AAEA,SAAS,kBAAkB;AAC1B,SAAO;AAAA,IACN,6BAAe,OAAO;AAAA,MACrB,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,MAAM,CAAC;AAAA,IACR,CAAC;AAAA,EACF;AACD;AA4BO,SAAS,uBAAuB,OAAkD;AACxF,QAAM,WAAW,MAAM,MAAM,IAAI,MAAM;AACvC,QAAM,cAAc,MAAM,MAAM,QAAQ,qBAAqB;AAE7D,QAAM,sBAAsB,MAAY;AAEvC,QAAI,CAAC,MAAM,IAAI,+BAAa,GAAG;AAC9B,YAAM,IAAI,CAAC,qCAAmB,OAAO,EAAE,IAAI,iCAAe,MAAM,MAAM,MAAM,YAAY,CAAC,CAAC,CAAC;AAC3F,aAAO,oBAAoB;AAAA,IAC5B;AAEA,QAAI,CAAC,MAAM,IAAI,6BAAY,GAAG;AAC7B,YAAM,IAAI,CAAC,mCAAkB,OAAO,EAAE,IAAI,8BAAa,CAAC,CAAC,CAAC;AAC1D,aAAO,oBAAoB;AAAA,IAC5B;AAGA,UAAM,UAAU,SAAS,IAAI;AAC7B,QAAI,QAAQ,SAAS,GAAG;AACvB,YAAM,IAAI,gBAAgB,CAAC;AAC3B,aAAO,oBAAoB;AAAA,IAC5B;AAEA,UAAM,iBAAiB,MAAM,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,OAAO,MAAM,IAAI,EAAE,CAAE,EAAE,KAAK,wBAAW,EAAE,CAAC,EAAE;AAG3F,UAAM,gBAAgB,MAAM,IAAI,+BAAa;AAC7C,QAAI,CAAC,eAAe;AACnB,YAAM,IAAI;AAAA,QACT,MAAM,OAAO,MAAM,SAAS,OAAO;AAAA,UAClC,IAAI;AAAA,UACJ,eAAe,eAAe;AAAA,UAC9B,kBAAkB;AAAA,QACnB,CAAC;AAAA,MACF,CAAC;AAED,aAAO,oBAAoB;AAAA,IAC5B,WAAW,CAAC,QAAQ,IAAI,cAAc,aAAa,GAAG;AACrD,YAAM,IAAI,CAAC,EAAE,GAAG,eAAe,eAAe,eAAe,EAAE,CAAC,CAAC;AACjE,aAAO,oBAAoB;AAAA,IAC5B;AAGA,UAAM,sBAAsB,oBAAI,IAA2B;AAC3D,UAAM,mBAAmB,oBAAI,IAAgB;AAC7C,eAAW,MAAM,SAAS;AACzB,YAAM,cAAc,+CAA4B,SAAS,EAAE;AAC3D,YAAM,YAAY,MAAM,IAAI,WAAW;AACvC,UAAI,CAAC,WAAW;AACf,4BAAoB,IAAI,WAAW;AAAA,MACpC;AACA,YAAM,WAAW,iCAAiB,SAAS,EAAE;AAC7C,UAAI,CAAC,MAAM,IAAI,QAAQ,GAAG;AACzB,yBAAiB,IAAI,QAAQ;AAAA,MAC9B;AAAA,IACD;AAEA,QAAI,oBAAoB,OAAO,GAAG;AACjC,YAAM;AAAA,QACL,CAAC,GAAG,mBAAmB,EAAE;AAAA,UAAI,CAAC,OAC7B,+CAA4B,OAAO;AAAA,YAClC;AAAA,YACA,QAAQ,+CAA4B,QAAQ,EAAE;AAAA,UAC/C,CAAC;AAAA,QACF;AAAA,MACD;AAAA,IACD;AAEA,QAAI,iBAAiB,OAAO,GAAG;AAC9B,YAAM,IAAI,CAAC,GAAG,gBAAgB,EAAE,IAAI,CAAC,OAAO,iCAAiB,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;AAAA,IAC7E;AAEA,UAAM,aAAa,YAAY,IAAI;AACnC,eAAW,aAAa,YAAY;AACnC,UAAI,CAAC,QAAQ,IAAI,UAAU,MAAM,GAAG;AACnC,cAAM,OAAO,CAAC,UAAU,EAAE,CAAC;AAC3B;AAAA,MACD;AACA,UAAI,UAAU,mBAAmB,CAAC,MAAM,IAAI,UAAU,eAAe,GAAG;AACvE,cAAM,IAAI,CAAC,EAAE,GAAG,WAAW,iBAAiB,KAAK,CAAC,CAAC;AACnD,eAAO,oBAAoB;AAAA,MAC5B;AACA,UAAI,UAAU,kBAAkB,CAAC,MAAM,IAAI,UAAU,cAAc,GAAG;AACrE,cAAM,IAAI,CAAC,EAAE,GAAG,WAAW,gBAAgB,KAAK,CAAC,CAAC;AAClD,eAAO,oBAAoB;AAAA,MAC5B;AACA,UAAI,UAAU,kBAAkB,CAAC,MAAM,IAAI,UAAU,cAAc,GAAG;AACrE,cAAM,IAAI,CAAC,EAAE,GAAG,WAAW,gBAAgB,KAAK,CAAC,CAAC;AAClD,eAAO,oBAAoB;AAAA,MAC5B;AACA,YAAM,sBAAsB,UAAU,iBAAiB,OAAO,CAAC,OAAO,MAAM,IAAI,EAAE,CAAC;AACnF,UAAI,oBAAoB,WAAW,UAAU,iBAAiB,QAAQ;AACrE,cAAM,IAAI,CAAC,EAAE,GAAG,WAAW,kBAAkB,oBAAoB,CAAC,CAAC;AACnE,eAAO,oBAAoB;AAAA,MAC5B;AACA,YAAM,qBAAqB,UAAU,gBAAgB,OAAO,CAAC,OAAO,MAAM,IAAI,EAAE,CAAC;AACjF,UAAI,mBAAmB,WAAW,UAAU,gBAAgB,QAAQ;AACnE,cAAM,IAAI,CAAC,EAAE,GAAG,WAAW,iBAAiB,mBAAmB,CAAC,CAAC;AACjE,eAAO,oBAAoB;AAAA,MAC5B;AACA,YAAM,qBAAqB,UAAU,gBAAgB,OAAO,CAAC,OAAO,MAAM,IAAI,EAAE,CAAC;AACjF,UAAI,mBAAmB,WAAW,UAAU,gBAAgB,QAAQ;AACnE,cAAM,IAAI,CAAC,EAAE,GAAG,WAAW,iBAAiB,mBAAmB,CAAC,CAAC;AACjE,eAAO,oBAAoB;AAAA,MAC5B;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -25,13 +25,14 @@ module.exports = __toCommonJS(TLBaseAsset_exports);
|
|
|
25
25
|
var import_validate = require("@tldraw/validate");
|
|
26
26
|
var import_id_validator = require("../misc/id-validator");
|
|
27
27
|
const assetIdValidator = (0, import_id_validator.idValidator)("asset");
|
|
28
|
-
function createAssetValidator(type, props) {
|
|
28
|
+
function createAssetValidator(type, props, meta) {
|
|
29
|
+
const propsValidator = props instanceof import_validate.T.Validator ? props : props ? import_validate.T.object(props) : import_validate.T.jsonValue;
|
|
29
30
|
return import_validate.T.object({
|
|
30
31
|
id: assetIdValidator,
|
|
31
32
|
typeName: import_validate.T.literal("asset"),
|
|
32
33
|
type: import_validate.T.literal(type),
|
|
33
|
-
props,
|
|
34
|
-
meta: import_validate.T.jsonValue
|
|
34
|
+
props: propsValidator,
|
|
35
|
+
meta: meta ? import_validate.T.object(meta) : import_validate.T.jsonValue
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
//# sourceMappingURL=TLBaseAsset.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/assets/TLBaseAsset.ts"],
|
|
4
|
-
"sourcesContent": ["import { BaseRecord } from '@tldraw/store'\nimport { JsonObject } from '@tldraw/utils'\nimport { T } from '@tldraw/validate'\nimport { idValidator } from '../misc/id-validator'\nimport { TLAssetId } from '../records/TLAsset'\n\n/**\n * Base interface for all asset records in tldraw. Assets represent external resources\n * like images, videos, or bookmarks that shapes can reference. This interface extends\n * the base record system with asset-specific typing.\n *\n * @param Type - The specific asset type identifier (e.g., 'image', 'video', 'bookmark')\n * @param Props - The properties object specific to this asset type\n *\n * @example\n * ```ts\n * // Define a custom asset type\n * interface MyCustomAsset extends TLBaseAsset<'custom', { url: string; title: string }> {}\n *\n * const customAsset: MyCustomAsset = {\n * id: 'asset:custom123',\n * typeName: 'asset',\n * type: 'custom',\n * props: {\n * url: 'https://example.com',\n * title: 'My Custom Asset'\n * },\n * meta: {}\n * }\n * ```\n *\n * @public\n */\nexport interface TLBaseAsset<Type extends string, Props> extends BaseRecord<'asset', TLAssetId> {\n\t/** The specific type of this asset (e.g., 'image', 'video', 'bookmark') */\n\ttype: Type\n\t/** Type-specific properties for this asset */\n\tprops: Props\n\t/** User-defined metadata that can be attached to this asset */\n\tmeta: JsonObject\n}\n\n/**\n * A validator for asset record type IDs. This validator ensures that asset IDs\n * follow the correct format and type structure required by tldraw's asset system.\n * Asset IDs are prefixed with 'asset:' followed by a unique identifier.\n *\n * @example\n * ```ts\n * import { assetIdValidator } from '@tldraw/tlschema'\n *\n * // Valid asset ID\n * const validId = 'asset:abc123'\n * console.log(assetIdValidator.isValid(validId)) // true\n *\n * // Invalid asset ID\n * const invalidId = 'shape:abc123'\n * console.log(assetIdValidator.isValid(invalidId)) // false\n * ```\n *\n * @public\n */\nexport const assetIdValidator = idValidator<TLAssetId>('asset')\n\n/**\n * Creates a validator for a specific asset record type. This factory function generates\n * a complete validator that validates the entire asset record structure including the\n * base properties (id, typeName, type, meta) and the type-specific props.\n *\n * @param type - The asset type identifier (e.g., 'image', 'video', 'bookmark')\n * @param props -
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,sBAAkB;AAClB,0BAA4B;AA2DrB,MAAM,uBAAmB,iCAAuB,OAAO;
|
|
4
|
+
"sourcesContent": ["import { BaseRecord } from '@tldraw/store'\nimport { JsonObject } from '@tldraw/utils'\nimport { T } from '@tldraw/validate'\nimport { idValidator } from '../misc/id-validator'\nimport { TLAssetId } from '../records/TLAsset'\n\n/**\n * Base interface for all asset records in tldraw. Assets represent external resources\n * like images, videos, or bookmarks that shapes can reference. This interface extends\n * the base record system with asset-specific typing.\n *\n * @param Type - The specific asset type identifier (e.g., 'image', 'video', 'bookmark')\n * @param Props - The properties object specific to this asset type\n *\n * @example\n * ```ts\n * // Define a custom asset type\n * interface MyCustomAsset extends TLBaseAsset<'custom', { url: string; title: string }> {}\n *\n * const customAsset: MyCustomAsset = {\n * id: 'asset:custom123',\n * typeName: 'asset',\n * type: 'custom',\n * props: {\n * url: 'https://example.com',\n * title: 'My Custom Asset'\n * },\n * meta: {}\n * }\n * ```\n *\n * @public\n */\nexport interface TLBaseAsset<Type extends string, Props> extends BaseRecord<'asset', TLAssetId> {\n\t/** The specific type of this asset (e.g., 'image', 'video', 'bookmark') */\n\ttype: Type\n\t/** Type-specific properties for this asset */\n\tprops: Props\n\t/** User-defined metadata that can be attached to this asset */\n\tmeta: JsonObject\n}\n\n/**\n * A validator for asset record type IDs. This validator ensures that asset IDs\n * follow the correct format and type structure required by tldraw's asset system.\n * Asset IDs are prefixed with 'asset:' followed by a unique identifier.\n *\n * @example\n * ```ts\n * import { assetIdValidator } from '@tldraw/tlschema'\n *\n * // Valid asset ID\n * const validId = 'asset:abc123'\n * console.log(assetIdValidator.isValid(validId)) // true\n *\n * // Invalid asset ID\n * const invalidId = 'shape:abc123'\n * console.log(assetIdValidator.isValid(invalidId)) // false\n * ```\n *\n * @public\n */\nexport const assetIdValidator = idValidator<TLAssetId>('asset')\n\n/**\n * Creates a validator for a specific asset record type. This factory function generates\n * a complete validator that validates the entire asset record structure including the\n * base properties (id, typeName, type, meta) and the type-specific props.\n *\n * @param type - The asset type identifier (e.g., 'image', 'video', 'bookmark')\n * @param props - A validator or per-key validator record for the asset's type-specific properties\n * @param meta - An optional per-key validator record for the asset's meta properties\n * @returns A complete validator for the asset record type\n *\n * @example\n * ```ts\n * import { createAssetValidator, TLBaseAsset } from '@tldraw/tlschema'\n * import { T } from '@tldraw/validate'\n *\n * // Define a custom asset type\n * type TLCustomAsset = TLBaseAsset<'custom', {\n * url: string\n * title: string\n * description?: string\n * }>\n *\n * // Create validator using a per-key record (recommended)\n * const customAssetValidator = createAssetValidator('custom', {\n * url: T.string,\n * title: T.string,\n * description: T.string.optional()\n * })\n *\n * // Or using a T.object validator\n * const customAssetValidator2 = createAssetValidator('custom', T.object({\n * url: T.string,\n * title: T.string,\n * description: T.string.optional()\n * }))\n * ```\n *\n * @public\n */\nexport function createAssetValidator<\n\tType extends string,\n\tProps extends JsonObject,\n\tMeta extends JsonObject = JsonObject,\n>(\n\ttype: Type,\n\tprops?: T.Validator<Props> | { [K in keyof Props]: T.Validatable<Props[K]> },\n\tmeta?: { [K in keyof Meta]: T.Validatable<Meta[K]> }\n) {\n\t// Determine if props is a Validator instance or a per-key record\n\tconst propsValidator =\n\t\tprops instanceof T.Validator ? props : props ? T.object(props) : (T.jsonValue as any)\n\n\treturn T.object<TLBaseAsset<Type, Props>>({\n\t\tid: assetIdValidator,\n\t\ttypeName: T.literal('asset'),\n\t\ttype: T.literal(type),\n\t\tprops: propsValidator,\n\t\tmeta: meta ? T.object(meta) : (T.jsonValue as T.ObjectValidator<JsonObject>),\n\t})\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,sBAAkB;AAClB,0BAA4B;AA2DrB,MAAM,uBAAmB,iCAAuB,OAAO;AAyCvD,SAAS,qBAKf,MACA,OACA,MACC;AAED,QAAM,iBACL,iBAAiB,kBAAE,YAAY,QAAQ,QAAQ,kBAAE,OAAO,KAAK,IAAK,kBAAE;AAErE,SAAO,kBAAE,OAAiC;AAAA,IACzC,IAAI;AAAA,IACJ,UAAU,kBAAE,QAAQ,OAAO;AAAA,IAC3B,MAAM,kBAAE,QAAQ,IAAI;AAAA,IACpB,OAAO;AAAA,IACP,MAAM,OAAO,kBAAE,OAAO,IAAI,IAAK,kBAAE;AAAA,EAClC,CAAC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var TLBookmarkAsset_exports = {};
|
|
20
20
|
__export(TLBookmarkAsset_exports, {
|
|
21
21
|
bookmarkAssetMigrations: () => bookmarkAssetMigrations,
|
|
22
|
+
bookmarkAssetProps: () => bookmarkAssetProps,
|
|
22
23
|
bookmarkAssetValidator: () => bookmarkAssetValidator,
|
|
23
24
|
bookmarkAssetVersions: () => Versions
|
|
24
25
|
});
|
|
@@ -26,15 +27,16 @@ module.exports = __toCommonJS(TLBookmarkAsset_exports);
|
|
|
26
27
|
var import_store = require("@tldraw/store");
|
|
27
28
|
var import_validate = require("@tldraw/validate");
|
|
28
29
|
var import_TLBaseAsset = require("./TLBaseAsset");
|
|
30
|
+
const bookmarkAssetProps = {
|
|
31
|
+
title: import_validate.T.string,
|
|
32
|
+
description: import_validate.T.string,
|
|
33
|
+
image: import_validate.T.string,
|
|
34
|
+
favicon: import_validate.T.string,
|
|
35
|
+
src: import_validate.T.srcUrl.nullable()
|
|
36
|
+
};
|
|
29
37
|
const bookmarkAssetValidator = (0, import_TLBaseAsset.createAssetValidator)(
|
|
30
38
|
"bookmark",
|
|
31
|
-
import_validate.T.object(
|
|
32
|
-
title: import_validate.T.string,
|
|
33
|
-
description: import_validate.T.string,
|
|
34
|
-
image: import_validate.T.string,
|
|
35
|
-
favicon: import_validate.T.string,
|
|
36
|
-
src: import_validate.T.srcUrl.nullable()
|
|
37
|
-
})
|
|
39
|
+
import_validate.T.object(bookmarkAssetProps)
|
|
38
40
|
);
|
|
39
41
|
const Versions = (0, import_store.createMigrationIds)("com.tldraw.asset.bookmark", {
|
|
40
42
|
MakeUrlsValid: 1,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/assets/TLBookmarkAsset.ts"],
|
|
4
|
-
"sourcesContent": ["import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'\nimport { T } from '@tldraw/validate'\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkE;AAClE,sBAAkB;AAElB,yBAAkD;
|
|
4
|
+
"sourcesContent": ["import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'\nimport { T } from '@tldraw/validate'\nimport { RecordProps } from '../recordsWithProps'\nimport { TLBaseAsset, createAssetValidator } from './TLBaseAsset'\n\n/**\n * An asset used for URL bookmarks, used by the TLBookmarkShape.\n *\n * @public */\nexport type TLBookmarkAsset = TLBaseAsset<\n\t'bookmark',\n\t{\n\t\ttitle: string\n\t\tdescription: string\n\t\timage: string\n\t\tfavicon: string\n\t\tsrc: string | null\n\t}\n>\n\n/** @public */\nexport const bookmarkAssetProps = {\n\ttitle: T.string,\n\tdescription: T.string,\n\timage: T.string,\n\tfavicon: T.string,\n\tsrc: T.srcUrl.nullable(),\n} satisfies RecordProps<TLBookmarkAsset>\n\n/** Validator for bookmark assets. @public */\nexport const bookmarkAssetValidator: T.Validator<TLBookmarkAsset> = createAssetValidator(\n\t'bookmark',\n\tT.object(bookmarkAssetProps)\n)\n\nconst Versions = createMigrationIds('com.tldraw.asset.bookmark', {\n\tMakeUrlsValid: 1,\n\tAddFavicon: 2,\n} as const)\n\n/**\n * Migration version identifiers for bookmark assets. These versions track\n * the evolution of the bookmark asset schema over time.\n *\n * Available versions:\n * - `MakeUrlsValid` (v1): Ensures src URLs are valid or empty\n * - `AddFavicon` (v2): Adds favicon property to bookmark assets\n *\n * @example\n * ```ts\n * import { bookmarkAssetVersions } from '@tldraw/tlschema'\n *\n * // Check if a migration exists\n * console.log(bookmarkAssetVersions.AddFavicon) // 2\n * ```\n *\n * @public\n */\nexport { Versions as bookmarkAssetVersions }\n\n/**\n * Migration sequence for bookmark assets. Handles the evolution of bookmark asset\n * data structure over time, ensuring backward and forward compatibility.\n *\n * The migration sequence includes:\n * 1. **MakeUrlsValid** (v1): Validates and cleans up src URLs, setting invalid URLs to empty string\n * 2. **AddFavicon** (v2): Adds the favicon property and validates it, setting invalid favicons to empty string\n *\n * @public\n */\nexport const bookmarkAssetMigrations = createRecordMigrationSequence({\n\tsequenceId: 'com.tldraw.asset.bookmark',\n\trecordType: 'asset',\n\tfilter: (asset) => (asset as TLBookmarkAsset).type === 'bookmark',\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.MakeUrlsValid,\n\t\t\tup: (asset: any) => {\n\t\t\t\tif (!T.srcUrl.isValid(asset.props.src)) {\n\t\t\t\t\tasset.props.src = ''\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: (_asset) => {\n\t\t\t\t// noop\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddFavicon,\n\t\t\tup: (asset: any) => {\n\t\t\t\tif (!T.srcUrl.isValid(asset.props.favicon)) {\n\t\t\t\t\tasset.props.favicon = ''\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tdelete asset.props.favicon\n\t\t\t},\n\t\t},\n\t],\n})\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkE;AAClE,sBAAkB;AAElB,yBAAkD;AAkB3C,MAAM,qBAAqB;AAAA,EACjC,OAAO,kBAAE;AAAA,EACT,aAAa,kBAAE;AAAA,EACf,OAAO,kBAAE;AAAA,EACT,SAAS,kBAAE;AAAA,EACX,KAAK,kBAAE,OAAO,SAAS;AACxB;AAGO,MAAM,6BAAuD;AAAA,EACnE;AAAA,EACA,kBAAE,OAAO,kBAAkB;AAC5B;AAEA,MAAM,eAAW,iCAAmB,6BAA6B;AAAA,EAChE,eAAe;AAAA,EACf,YAAY;AACb,CAAU;AAgCH,MAAM,8BAA0B,4CAA8B;AAAA,EACpE,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ,CAAC,UAAW,MAA0B,SAAS;AAAA,EACvD,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,YAAI,CAAC,kBAAE,OAAO,QAAQ,MAAM,MAAM,GAAG,GAAG;AACvC,gBAAM,MAAM,MAAM;AAAA,QACnB;AAAA,MACD;AAAA,MACA,MAAM,CAAC,WAAW;AAAA,MAElB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,YAAI,CAAC,kBAAE,OAAO,QAAQ,MAAM,MAAM,OAAO,GAAG;AAC3C,gBAAM,MAAM,UAAU;AAAA,QACvB;AAAA,MACD;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var TLImageAsset_exports = {};
|
|
20
20
|
__export(TLImageAsset_exports, {
|
|
21
21
|
imageAssetMigrations: () => imageAssetMigrations,
|
|
22
|
+
imageAssetProps: () => imageAssetProps,
|
|
22
23
|
imageAssetValidator: () => imageAssetValidator,
|
|
23
24
|
imageAssetVersions: () => Versions
|
|
24
25
|
});
|
|
@@ -26,18 +27,19 @@ module.exports = __toCommonJS(TLImageAsset_exports);
|
|
|
26
27
|
var import_store = require("@tldraw/store");
|
|
27
28
|
var import_validate = require("@tldraw/validate");
|
|
28
29
|
var import_TLBaseAsset = require("./TLBaseAsset");
|
|
30
|
+
const imageAssetProps = {
|
|
31
|
+
w: import_validate.T.number,
|
|
32
|
+
h: import_validate.T.number,
|
|
33
|
+
name: import_validate.T.string,
|
|
34
|
+
isAnimated: import_validate.T.boolean,
|
|
35
|
+
mimeType: import_validate.T.string.nullable(),
|
|
36
|
+
src: import_validate.T.srcUrl.nullable(),
|
|
37
|
+
fileSize: import_validate.T.nonZeroNumber.optional(),
|
|
38
|
+
pixelRatio: import_validate.T.positiveNumber.optional()
|
|
39
|
+
};
|
|
29
40
|
const imageAssetValidator = (0, import_TLBaseAsset.createAssetValidator)(
|
|
30
41
|
"image",
|
|
31
|
-
import_validate.T.object(
|
|
32
|
-
w: import_validate.T.number,
|
|
33
|
-
h: import_validate.T.number,
|
|
34
|
-
name: import_validate.T.string,
|
|
35
|
-
isAnimated: import_validate.T.boolean,
|
|
36
|
-
mimeType: import_validate.T.string.nullable(),
|
|
37
|
-
src: import_validate.T.srcUrl.nullable(),
|
|
38
|
-
fileSize: import_validate.T.nonZeroNumber.optional(),
|
|
39
|
-
pixelRatio: import_validate.T.positiveNumber.optional()
|
|
40
|
-
})
|
|
42
|
+
import_validate.T.object(imageAssetProps)
|
|
41
43
|
);
|
|
42
44
|
const Versions = (0, import_store.createMigrationIds)("com.tldraw.asset.image", {
|
|
43
45
|
AddIsAnimated: 1,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/assets/TLImageAsset.ts"],
|
|
4
|
-
"sourcesContent": ["import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'\nimport { T } from '@tldraw/validate'\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkE;AAClE,sBAAkB;AAElB,yBAAkD;
|
|
4
|
+
"sourcesContent": ["import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'\nimport { T } from '@tldraw/validate'\nimport { RecordProps } from '../recordsWithProps'\nimport { TLBaseAsset, createAssetValidator } from './TLBaseAsset'\n\n/**\n * An asset for images such as PNGs and JPEGs, used by the TLImageShape.\n *\n * @public */\nexport type TLImageAsset = TLBaseAsset<\n\t'image',\n\t{\n\t\tw: number\n\t\th: number\n\t\tname: string\n\t\tisAnimated: boolean\n\t\tmimeType: string | null\n\t\tsrc: string | null\n\t\tfileSize?: number\n\t\tpixelRatio?: number\n\t}\n>\n\n/** @public */\nexport const imageAssetProps = {\n\tw: T.number,\n\th: T.number,\n\tname: T.string,\n\tisAnimated: T.boolean,\n\tmimeType: T.string.nullable(),\n\tsrc: T.srcUrl.nullable(),\n\tfileSize: T.nonZeroNumber.optional(),\n\tpixelRatio: T.positiveNumber.optional(),\n} satisfies RecordProps<TLImageAsset>\n\n/** Validator for image assets. @public */\nexport const imageAssetValidator: T.Validator<TLImageAsset> = createAssetValidator(\n\t'image',\n\tT.object(imageAssetProps)\n)\n\nconst Versions = createMigrationIds('com.tldraw.asset.image', {\n\tAddIsAnimated: 1,\n\tRenameWidthHeight: 2,\n\tMakeUrlsValid: 3,\n\tAddFileSize: 4,\n\tMakeFileSizeOptional: 5,\n\tAddPixelRatio: 6,\n} as const)\n\n/**\n * Migration version identifiers for image assets. These define the different schema versions\n * that image assets have gone through during the evolution of the tldraw data model.\n *\n * @example\n * ```ts\n * import { imageAssetVersions } from '@tldraw/tlschema'\n *\n * // Access specific version IDs\n * console.log(imageAssetVersions.AddIsAnimated) // Version when isAnimated was added\n * console.log(imageAssetVersions.RenameWidthHeight) // Version when width/height became w/h\n * ```\n *\n * @public\n */\nexport { Versions as imageAssetVersions }\n\n/**\n * Migration sequence for image assets. Handles the evolution of the image asset schema\n * over time, providing both forward (up) and backward (down) migration functions to\n * maintain compatibility across different versions of the tldraw data model.\n *\n * The sequence includes migrations for:\n * - Adding the `isAnimated` property to track animated images\n * - Renaming `width`/`height` properties to shorter `w`/`h` names\n * - Ensuring URLs are valid format\n * - Adding file size tracking\n * - Making file size optional\n *\n *\n * @public\n */\nexport const imageAssetMigrations = createRecordMigrationSequence({\n\tsequenceId: 'com.tldraw.asset.image',\n\trecordType: 'asset',\n\tfilter: (asset) => (asset as TLImageAsset).type === 'image',\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.AddIsAnimated,\n\t\t\tup: (asset: any) => {\n\t\t\t\tasset.props.isAnimated = false\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tdelete asset.props.isAnimated\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.RenameWidthHeight,\n\t\t\tup: (asset: any) => {\n\t\t\t\tasset.props.w = asset.props.width\n\t\t\t\tasset.props.h = asset.props.height\n\t\t\t\tdelete asset.props.width\n\t\t\t\tdelete asset.props.height\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tasset.props.width = asset.props.w\n\t\t\t\tasset.props.height = asset.props.h\n\t\t\t\tdelete asset.props.w\n\t\t\t\tdelete asset.props.h\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.MakeUrlsValid,\n\t\t\tup: (asset: any) => {\n\t\t\t\tif (!T.srcUrl.isValid(asset.props.src)) {\n\t\t\t\t\tasset.props.src = ''\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: (_asset) => {\n\t\t\t\t// noop\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddFileSize,\n\t\t\tup: (asset: any) => {\n\t\t\t\tasset.props.fileSize = -1\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tdelete asset.props.fileSize\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.MakeFileSizeOptional,\n\t\t\tup: (asset: any) => {\n\t\t\t\tif (asset.props.fileSize === -1) {\n\t\t\t\t\tasset.props.fileSize = undefined\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tif (asset.props.fileSize === undefined) {\n\t\t\t\t\tasset.props.fileSize = -1\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddPixelRatio,\n\t\t\tup: (_asset: any) => {\n\t\t\t\t// noop \u2014 pixelRatio is optional and undefined by default\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tdelete asset.props.pixelRatio\n\t\t\t},\n\t\t},\n\t],\n})\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkE;AAClE,sBAAkB;AAElB,yBAAkD;AAqB3C,MAAM,kBAAkB;AAAA,EAC9B,GAAG,kBAAE;AAAA,EACL,GAAG,kBAAE;AAAA,EACL,MAAM,kBAAE;AAAA,EACR,YAAY,kBAAE;AAAA,EACd,UAAU,kBAAE,OAAO,SAAS;AAAA,EAC5B,KAAK,kBAAE,OAAO,SAAS;AAAA,EACvB,UAAU,kBAAE,cAAc,SAAS;AAAA,EACnC,YAAY,kBAAE,eAAe,SAAS;AACvC;AAGO,MAAM,0BAAiD;AAAA,EAC7D;AAAA,EACA,kBAAE,OAAO,eAAe;AACzB;AAEA,MAAM,eAAW,iCAAmB,0BAA0B;AAAA,EAC7D,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,aAAa;AAAA,EACb,sBAAsB;AAAA,EACtB,eAAe;AAChB,CAAU;AAkCH,MAAM,2BAAuB,4CAA8B;AAAA,EACjE,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ,CAAC,UAAW,MAAuB,SAAS;AAAA,EACpD,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,cAAM,MAAM,aAAa;AAAA,MAC1B;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,cAAM,MAAM,IAAI,MAAM,MAAM;AAC5B,cAAM,MAAM,IAAI,MAAM,MAAM;AAC5B,eAAO,MAAM,MAAM;AACnB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,cAAM,MAAM,QAAQ,MAAM,MAAM;AAChC,cAAM,MAAM,SAAS,MAAM,MAAM;AACjC,eAAO,MAAM,MAAM;AACnB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,YAAI,CAAC,kBAAE,OAAO,QAAQ,MAAM,MAAM,GAAG,GAAG;AACvC,gBAAM,MAAM,MAAM;AAAA,QACnB;AAAA,MACD;AAAA,MACA,MAAM,CAAC,WAAW;AAAA,MAElB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,cAAM,MAAM,WAAW;AAAA,MACxB;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,YAAI,MAAM,MAAM,aAAa,IAAI;AAChC,gBAAM,MAAM,WAAW;AAAA,QACxB;AAAA,MACD;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,YAAI,MAAM,MAAM,aAAa,QAAW;AACvC,gBAAM,MAAM,WAAW;AAAA,QACxB;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,WAAgB;AAAA,MAErB;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var TLVideoAsset_exports = {};
|
|
20
20
|
__export(TLVideoAsset_exports, {
|
|
21
21
|
videoAssetMigrations: () => videoAssetMigrations,
|
|
22
|
+
videoAssetProps: () => videoAssetProps,
|
|
22
23
|
videoAssetValidator: () => videoAssetValidator,
|
|
23
24
|
videoAssetVersions: () => Versions
|
|
24
25
|
});
|
|
@@ -26,17 +27,18 @@ module.exports = __toCommonJS(TLVideoAsset_exports);
|
|
|
26
27
|
var import_store = require("@tldraw/store");
|
|
27
28
|
var import_validate = require("@tldraw/validate");
|
|
28
29
|
var import_TLBaseAsset = require("./TLBaseAsset");
|
|
30
|
+
const videoAssetProps = {
|
|
31
|
+
w: import_validate.T.number,
|
|
32
|
+
h: import_validate.T.number,
|
|
33
|
+
name: import_validate.T.string,
|
|
34
|
+
isAnimated: import_validate.T.boolean,
|
|
35
|
+
mimeType: import_validate.T.string.nullable(),
|
|
36
|
+
src: import_validate.T.srcUrl.nullable(),
|
|
37
|
+
fileSize: import_validate.T.number.optional()
|
|
38
|
+
};
|
|
29
39
|
const videoAssetValidator = (0, import_TLBaseAsset.createAssetValidator)(
|
|
30
40
|
"video",
|
|
31
|
-
import_validate.T.object(
|
|
32
|
-
w: import_validate.T.number,
|
|
33
|
-
h: import_validate.T.number,
|
|
34
|
-
name: import_validate.T.string,
|
|
35
|
-
isAnimated: import_validate.T.boolean,
|
|
36
|
-
mimeType: import_validate.T.string.nullable(),
|
|
37
|
-
src: import_validate.T.srcUrl.nullable(),
|
|
38
|
-
fileSize: import_validate.T.number.optional()
|
|
39
|
-
})
|
|
41
|
+
import_validate.T.object(videoAssetProps)
|
|
40
42
|
);
|
|
41
43
|
const Versions = (0, import_store.createMigrationIds)("com.tldraw.asset.video", {
|
|
42
44
|
AddIsAnimated: 1,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/assets/TLVideoAsset.ts"],
|
|
4
|
-
"sourcesContent": ["import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'\nimport { T } from '@tldraw/validate'\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkE;AAClE,sBAAkB;AAElB,yBAAkD;
|
|
4
|
+
"sourcesContent": ["import { createMigrationIds, createRecordMigrationSequence } from '@tldraw/store'\nimport { T } from '@tldraw/validate'\nimport { RecordProps } from '../recordsWithProps'\nimport { TLBaseAsset, createAssetValidator } from './TLBaseAsset'\n\n/**\n * An asset record representing video files that can be displayed in video shapes.\n * Video assets store metadata about video files including dimensions, MIME type,\n * animation status, and file source information. They are referenced by TLVideoShape\n * instances to display video content on the canvas.\n *\n * @example\n * ```ts\n * import { TLVideoAsset } from '@tldraw/tlschema'\n *\n * const videoAsset: TLVideoAsset = {\n * id: 'asset:video123',\n * typeName: 'asset',\n * type: 'video',\n * props: {\n * w: 1920,\n * h: 1080,\n * name: 'my-video.mp4',\n * isAnimated: true,\n * mimeType: 'video/mp4',\n * src: 'https://example.com/video.mp4',\n * fileSize: 5242880\n * },\n * meta: {}\n * }\n * ```\n *\n * @public\n */\nexport type TLVideoAsset = TLBaseAsset<\n\t'video',\n\t{\n\t\t/** The width of the video in pixels */\n\t\tw: number\n\t\t/** The height of the video in pixels */\n\t\th: number\n\t\t/** The original filename or display name of the video */\n\t\tname: string\n\t\t/** Whether the video contains animation/motion (true for most videos) */\n\t\tisAnimated: boolean\n\t\t/** The MIME type of the video file (e.g., 'video/mp4', 'video/webm'), null if unknown */\n\t\tmimeType: string | null\n\t\t/** The source URL or data URI for the video file, null if not yet available */\n\t\tsrc: string | null\n\t\t/** The file size in bytes, optional for backward compatibility */\n\t\tfileSize?: number\n\t}\n>\n\n/** @public */\nexport const videoAssetProps = {\n\tw: T.number,\n\th: T.number,\n\tname: T.string,\n\tisAnimated: T.boolean,\n\tmimeType: T.string.nullable(),\n\tsrc: T.srcUrl.nullable(),\n\tfileSize: T.number.optional(),\n} satisfies RecordProps<TLVideoAsset>\n\n/** Validator for video assets. @public */\nexport const videoAssetValidator: T.Validator<TLVideoAsset> = createAssetValidator(\n\t'video',\n\tT.object(videoAssetProps)\n)\n\nconst Versions = createMigrationIds('com.tldraw.asset.video', {\n\tAddIsAnimated: 1,\n\tRenameWidthHeight: 2,\n\tMakeUrlsValid: 3,\n\tAddFileSize: 4,\n\tMakeFileSizeOptional: 5,\n} as const)\n\n/**\n * Version identifiers for video asset migration sequences. These versions track\n * the evolution of the video asset schema over time, enabling proper data migration\n * when the asset structure changes.\n *\n * @example\n * ```ts\n * import { videoAssetVersions } from '@tldraw/tlschema'\n *\n * // Check the current version of a specific migration\n * console.log(videoAssetVersions.AddFileSize) // 4\n * ```\n *\n * @public\n */\nexport { Versions as videoAssetVersions }\n\n/**\n * Migration sequence for video assets that handles schema evolution over time.\n * This sequence defines how video asset data should be transformed when upgrading\n * or downgrading between different schema versions. Each migration step handles\n * specific changes like adding properties, renaming fields, or changing data formats.\n *\n * The migrations handle:\n * - Adding animation detection (isAnimated property)\n * - Renaming width/height properties to w/h for consistency\n * - Ensuring URL validity for src properties\n * - Adding file size tracking\n * - Making file size optional for backward compatibility\n *\n * @public\n */\nexport const videoAssetMigrations = createRecordMigrationSequence({\n\tsequenceId: 'com.tldraw.asset.video',\n\trecordType: 'asset',\n\tfilter: (asset) => (asset as TLVideoAsset).type === 'video',\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.AddIsAnimated,\n\t\t\tup: (asset: any) => {\n\t\t\t\tasset.props.isAnimated = false\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tdelete asset.props.isAnimated\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.RenameWidthHeight,\n\t\t\tup: (asset: any) => {\n\t\t\t\tasset.props.w = asset.props.width\n\t\t\t\tasset.props.h = asset.props.height\n\t\t\t\tdelete asset.props.width\n\t\t\t\tdelete asset.props.height\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tasset.props.width = asset.props.w\n\t\t\t\tasset.props.height = asset.props.h\n\t\t\t\tdelete asset.props.w\n\t\t\t\tdelete asset.props.h\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.MakeUrlsValid,\n\t\t\tup: (asset: any) => {\n\t\t\t\tif (!T.srcUrl.isValid(asset.props.src)) {\n\t\t\t\t\tasset.props.src = ''\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: (_asset) => {\n\t\t\t\t// noop\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddFileSize,\n\t\t\tup: (asset: any) => {\n\t\t\t\tasset.props.fileSize = -1\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tdelete asset.props.fileSize\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.MakeFileSizeOptional,\n\t\t\tup: (asset: any) => {\n\t\t\t\tif (asset.props.fileSize === -1) {\n\t\t\t\t\tasset.props.fileSize = undefined\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: (asset: any) => {\n\t\t\t\tif (asset.props.fileSize === undefined) {\n\t\t\t\t\tasset.props.fileSize = -1\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t],\n})\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkE;AAClE,sBAAkB;AAElB,yBAAkD;AAoD3C,MAAM,kBAAkB;AAAA,EAC9B,GAAG,kBAAE;AAAA,EACL,GAAG,kBAAE;AAAA,EACL,MAAM,kBAAE;AAAA,EACR,YAAY,kBAAE;AAAA,EACd,UAAU,kBAAE,OAAO,SAAS;AAAA,EAC5B,KAAK,kBAAE,OAAO,SAAS;AAAA,EACvB,UAAU,kBAAE,OAAO,SAAS;AAC7B;AAGO,MAAM,0BAAiD;AAAA,EAC7D;AAAA,EACA,kBAAE,OAAO,eAAe;AACzB;AAEA,MAAM,eAAW,iCAAmB,0BAA0B;AAAA,EAC7D,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,aAAa;AAAA,EACb,sBAAsB;AACvB,CAAU;AAkCH,MAAM,2BAAuB,4CAA8B;AAAA,EACjE,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ,CAAC,UAAW,MAAuB,SAAS;AAAA,EACpD,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,cAAM,MAAM,aAAa;AAAA,MAC1B;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,cAAM,MAAM,IAAI,MAAM,MAAM;AAC5B,cAAM,MAAM,IAAI,MAAM,MAAM;AAC5B,eAAO,MAAM,MAAM;AACnB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,cAAM,MAAM,QAAQ,MAAM,MAAM;AAChC,cAAM,MAAM,SAAS,MAAM,MAAM;AACjC,eAAO,MAAM,MAAM;AACnB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,YAAI,CAAC,kBAAE,OAAO,QAAQ,MAAM,MAAM,GAAG,GAAG;AACvC,gBAAM,MAAM,MAAM;AAAA,QACnB;AAAA,MACD;AAAA,MACA,MAAM,CAAC,WAAW;AAAA,MAElB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,cAAM,MAAM,WAAW;AAAA,MACxB;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,eAAO,MAAM,MAAM;AAAA,MACpB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAe;AACnB,YAAI,MAAM,MAAM,aAAa,IAAI;AAChC,gBAAM,MAAM,WAAW;AAAA,QACxB;AAAA,MACD;AAAA,MACA,MAAM,CAAC,UAAe;AACrB,YAAI,MAAM,MAAM,aAAa,QAAW;AACvC,gBAAM,MAAM,WAAW;AAAA,QACxB;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -28,12 +28,14 @@ var import_TLInstance = require("./records/TLInstance");
|
|
|
28
28
|
var import_TLPageState = require("./records/TLPageState");
|
|
29
29
|
var import_TLPointer = require("./records/TLPointer");
|
|
30
30
|
var import_TLPresence = require("./records/TLPresence");
|
|
31
|
-
function createPresenceStateDerivation($user,
|
|
31
|
+
function createPresenceStateDerivation($user, opts) {
|
|
32
|
+
const { instanceId, getUserPresence: _getUserPresence } = opts ?? {};
|
|
33
|
+
const getUserPresence = _getUserPresence ?? getDefaultUserPresence;
|
|
32
34
|
return (store) => {
|
|
33
35
|
return (0, import_state.computed)("instancePresence", () => {
|
|
34
36
|
const user = $user.get();
|
|
35
37
|
if (!user) return null;
|
|
36
|
-
const state =
|
|
38
|
+
const state = getUserPresence(store, user);
|
|
37
39
|
if (!state) return null;
|
|
38
40
|
return import_TLPresence.InstancePresenceRecordType.create({
|
|
39
41
|
...state,
|
|
@@ -55,14 +57,14 @@ function getDefaultUserPresence(store, user) {
|
|
|
55
57
|
brush: instance.brush,
|
|
56
58
|
scribbles: instance.scribbles,
|
|
57
59
|
userId: user.id,
|
|
58
|
-
userName: user.name
|
|
60
|
+
userName: user.name,
|
|
59
61
|
followingUserId: instance.followingUserId,
|
|
60
62
|
camera: {
|
|
61
63
|
x: camera.x,
|
|
62
64
|
y: camera.y,
|
|
63
65
|
z: camera.z
|
|
64
66
|
},
|
|
65
|
-
color: user.color
|
|
67
|
+
color: user.color || "#FF0000",
|
|
66
68
|
currentPageId: instance.currentPageId,
|
|
67
69
|
cursor: {
|
|
68
70
|
x: pointer.x,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/createPresenceStateDerivation.ts"],
|
|
4
|
-
"sourcesContent": ["import { Signal, computed } from '@tldraw/state'\nimport {
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAiC;
|
|
4
|
+
"sourcesContent": ["import { Signal, computed } from '@tldraw/state'\nimport { CameraRecordType } from './records/TLCamera'\nimport { TLINSTANCE_ID } from './records/TLInstance'\nimport { InstancePageStateRecordType } from './records/TLPageState'\nimport { TLPOINTER_ID } from './records/TLPointer'\nimport { InstancePresenceRecordType, TLInstancePresence } from './records/TLPresence'\nimport { TLUser } from './records/TLUser'\nimport { TLStore } from './TLStore'\n\n/** @public */\nexport interface CreatePresenceStateDerivationOpts {\n\t/** Custom instance ID. If not provided, one is generated from the store ID. */\n\tinstanceId?: TLInstancePresence['id']\n\t/**\n\t * Override how presence state is built from the store and current user.\n\t * Defaults to {@link getDefaultUserPresence}.\n\t */\n\tgetUserPresence?(store: TLStore, user: TLUser): TLPresenceStateInfo | null\n}\n\n/**\n * Creates a derivation that represents the current presence state of the current user.\n *\n * This function returns a derivation factory that, when given a store, creates a computed signal\n * containing the user's current presence state. The presence state includes information like cursor\n * position, selected shapes, camera position, and user metadata that gets synchronized in\n * multiplayer scenarios.\n *\n * @param $user - A reactive signal containing the user information, or `null` when anonymous\n * @param opts - Optional configuration for instance ID and presence derivation\n * @returns A function that takes a store and returns a computed signal of the user's presence state\n *\n * @example\n * ```ts\n * import { createPresenceStateDerivation } from '@tldraw/tlschema'\n * import { atom } from '@tldraw/state'\n *\n * const userSignal = atom('user', { id: 'user-123', name: 'Alice', color: '#ff0000', meta: {} })\n * const presenceDerivation = createPresenceStateDerivation(userSignal)\n *\n * // Use with a store to get reactive presence state\n * const presenceState = presenceDerivation(store)\n * console.log(presenceState.get()) // Current user presence or null\n * ```\n *\n * @public\n */\nexport function createPresenceStateDerivation(\n\t$user: Signal<TLUser | null>,\n\topts?: CreatePresenceStateDerivationOpts\n) {\n\tconst { instanceId, getUserPresence: _getUserPresence } = opts ?? {}\n\tconst getUserPresence = _getUserPresence ?? getDefaultUserPresence\n\treturn (store: TLStore): Signal<TLInstancePresence | null> => {\n\t\treturn computed('instancePresence', () => {\n\t\t\tconst user = $user.get()\n\t\t\tif (!user) return null\n\n\t\t\tconst state = getUserPresence(store, user)\n\t\t\tif (!state) return null\n\n\t\t\treturn InstancePresenceRecordType.create({\n\t\t\t\t...state,\n\t\t\t\tid: instanceId ?? InstancePresenceRecordType.createId(store.id),\n\t\t\t})\n\t\t})\n\t}\n}\n\n/**\n * The shape of data used to create a presence record.\n *\n * This type represents all the properties needed to construct a TLInstancePresence record.\n * It includes user information, cursor state, camera position, selected shapes, and other\n * presence-related data that gets synchronized across multiplayer clients.\n *\n * @public\n */\nexport type TLPresenceStateInfo = Parameters<(typeof InstancePresenceRecordType)['create']>[0]\n\n/**\n * Creates default presence state information for a user based on the current store state.\n *\n * This function extracts the current state from various store records (instance, page state,\n * camera, pointer) and combines them with user information to create a complete presence\n * state object. This is commonly used as a starting point for custom presence implementations.\n *\n * @param store - The tldraw store containing the current editor state\n * @param user - The user information to include in the presence state\n * @returns The default presence state info, or null if required store records are missing\n *\n * @example\n * ```ts\n * import { getDefaultUserPresence } from '@tldraw/tlschema'\n *\n * const user = { id: 'user-123', name: 'Alice', color: '#ff0000', meta: {} }\n * const presenceInfo = getDefaultUserPresence(store, user)\n *\n * if (presenceInfo) {\n * console.log('Current cursor:', presenceInfo.cursor)\n * console.log('Selected shapes:', presenceInfo.selectedShapeIds)\n * console.log('Camera position:', presenceInfo.camera)\n * }\n * ```\n *\n * @example\n * ```ts\n * // Common pattern: customize default presence\n * const customPresence = {\n * ...getDefaultUserPresence(store, user),\n * // Remove camera for privacy\n * camera: undefined,\n * // Add custom metadata\n * customField: 'my-data'\n * }\n * ```\n *\n * @public\n */\nexport function getDefaultUserPresence(store: TLStore, user: TLUser) {\n\tconst instance = store.get(TLINSTANCE_ID)\n\tconst pageState = store.get(InstancePageStateRecordType.createId(instance?.currentPageId))\n\tconst camera = store.get(CameraRecordType.createId(instance?.currentPageId))\n\tconst pointer = store.get(TLPOINTER_ID)\n\tif (!pageState || !instance || !camera || !pointer) {\n\t\treturn null\n\t}\n\n\treturn {\n\t\tselectedShapeIds: pageState.selectedShapeIds,\n\t\tbrush: instance.brush,\n\t\tscribbles: instance.scribbles,\n\t\tuserId: user.id,\n\t\tuserName: user.name,\n\t\tfollowingUserId: instance.followingUserId,\n\t\tcamera: {\n\t\t\tx: camera.x,\n\t\t\ty: camera.y,\n\t\t\tz: camera.z,\n\t\t},\n\t\tcolor: user.color || '#FF0000',\n\t\tcurrentPageId: instance.currentPageId,\n\t\tcursor: {\n\t\t\tx: pointer.x,\n\t\t\ty: pointer.y,\n\t\t\trotation: instance.cursor.rotation,\n\t\t\ttype: instance.cursor.type,\n\t\t},\n\t\tlastActivityTimestamp: pointer.lastActivityTimestamp,\n\t\tscreenBounds: instance.screenBounds,\n\t\tchatMessage: instance.chatMessage,\n\t\tmeta: {},\n\t} satisfies TLPresenceStateInfo\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAiC;AACjC,sBAAiC;AACjC,wBAA8B;AAC9B,yBAA4C;AAC5C,uBAA6B;AAC7B,wBAA+D;AA0CxD,SAAS,8BACf,OACA,MACC;AACD,QAAM,EAAE,YAAY,iBAAiB,iBAAiB,IAAI,QAAQ,CAAC;AACnE,QAAM,kBAAkB,oBAAoB;AAC5C,SAAO,CAAC,UAAsD;AAC7D,eAAO,uBAAS,oBAAoB,MAAM;AACzC,YAAM,OAAO,MAAM,IAAI;AACvB,UAAI,CAAC,KAAM,QAAO;AAElB,YAAM,QAAQ,gBAAgB,OAAO,IAAI;AACzC,UAAI,CAAC,MAAO,QAAO;AAEnB,aAAO,6CAA2B,OAAO;AAAA,QACxC,GAAG;AAAA,QACH,IAAI,cAAc,6CAA2B,SAAS,MAAM,EAAE;AAAA,MAC/D,CAAC;AAAA,IACF,CAAC;AAAA,EACF;AACD;AAoDO,SAAS,uBAAuB,OAAgB,MAAc;AACpE,QAAM,WAAW,MAAM,IAAI,+BAAa;AACxC,QAAM,YAAY,MAAM,IAAI,+CAA4B,SAAS,UAAU,aAAa,CAAC;AACzF,QAAM,SAAS,MAAM,IAAI,iCAAiB,SAAS,UAAU,aAAa,CAAC;AAC3E,QAAM,UAAU,MAAM,IAAI,6BAAY;AACtC,MAAI,CAAC,aAAa,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS;AACnD,WAAO;AAAA,EACR;AAEA,SAAO;AAAA,IACN,kBAAkB,UAAU;AAAA,IAC5B,OAAO,SAAS;AAAA,IAChB,WAAW,SAAS;AAAA,IACpB,QAAQ,KAAK;AAAA,IACb,UAAU,KAAK;AAAA,IACf,iBAAiB,SAAS;AAAA,IAC1B,QAAQ;AAAA,MACP,GAAG,OAAO;AAAA,MACV,GAAG,OAAO;AAAA,MACV,GAAG,OAAO;AAAA,IACX;AAAA,IACA,OAAO,KAAK,SAAS;AAAA,IACrB,eAAe,SAAS;AAAA,IACxB,QAAQ;AAAA,MACP,GAAG,QAAQ;AAAA,MACX,GAAG,QAAQ;AAAA,MACX,UAAU,SAAS,OAAO;AAAA,MAC1B,MAAM,SAAS,OAAO;AAAA,IACvB;AAAA,IACA,uBAAuB,QAAQ;AAAA,IAC/B,cAAc,SAAS;AAAA,IACvB,aAAa,SAAS;AAAA,IACtB,MAAM,CAAC;AAAA,EACR;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,13 +19,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
var createTLSchema_exports = {};
|
|
20
20
|
__export(createTLSchema_exports, {
|
|
21
21
|
createTLSchema: () => createTLSchema,
|
|
22
|
+
defaultAssetSchemas: () => defaultAssetSchemas,
|
|
22
23
|
defaultBindingSchemas: () => defaultBindingSchemas,
|
|
23
24
|
defaultShapeSchemas: () => defaultShapeSchemas
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(createTLSchema_exports);
|
|
26
27
|
var import_store = require("@tldraw/store");
|
|
27
28
|
var import_utils = require("@tldraw/utils");
|
|
28
|
-
var import_TLStore = require("./TLStore");
|
|
29
29
|
var import_TLBookmarkAsset = require("./assets/TLBookmarkAsset");
|
|
30
30
|
var import_TLImageAsset = require("./assets/TLImageAsset");
|
|
31
31
|
var import_TLVideoAsset = require("./assets/TLVideoAsset");
|
|
@@ -41,6 +41,7 @@ var import_TLPageState = require("./records/TLPageState");
|
|
|
41
41
|
var import_TLPointer = require("./records/TLPointer");
|
|
42
42
|
var import_TLPresence = require("./records/TLPresence");
|
|
43
43
|
var import_TLShape = require("./records/TLShape");
|
|
44
|
+
var import_TLUser = require("./records/TLUser");
|
|
44
45
|
var import_recordsWithProps = require("./recordsWithProps");
|
|
45
46
|
var import_TLArrowShape = require("./shapes/TLArrowShape");
|
|
46
47
|
var import_TLBookmarkShape = require("./shapes/TLBookmarkShape");
|
|
@@ -56,6 +57,7 @@ var import_TLNoteShape = require("./shapes/TLNoteShape");
|
|
|
56
57
|
var import_TLTextShape = require("./shapes/TLTextShape");
|
|
57
58
|
var import_TLVideoShape = require("./shapes/TLVideoShape");
|
|
58
59
|
var import_store_migrations = require("./store-migrations");
|
|
60
|
+
var import_TLStore = require("./TLStore");
|
|
59
61
|
const defaultShapeSchemas = {
|
|
60
62
|
arrow: { migrations: import_TLArrowShape.arrowShapeMigrations, props: import_TLArrowShape.arrowShapeProps },
|
|
61
63
|
bookmark: { migrations: import_TLBookmarkShape.bookmarkShapeMigrations, props: import_TLBookmarkShape.bookmarkShapeProps },
|
|
@@ -74,9 +76,16 @@ const defaultShapeSchemas = {
|
|
|
74
76
|
const defaultBindingSchemas = {
|
|
75
77
|
arrow: { migrations: import_TLArrowBinding.arrowBindingMigrations, props: import_TLArrowBinding.arrowBindingProps }
|
|
76
78
|
};
|
|
79
|
+
const defaultAssetSchemas = {
|
|
80
|
+
image: { migrations: import_TLImageAsset.imageAssetMigrations, props: import_TLImageAsset.imageAssetProps },
|
|
81
|
+
video: { migrations: import_TLVideoAsset.videoAssetMigrations, props: import_TLVideoAsset.videoAssetProps },
|
|
82
|
+
bookmark: { migrations: import_TLBookmarkAsset.bookmarkAssetMigrations, props: import_TLBookmarkAsset.bookmarkAssetProps }
|
|
83
|
+
};
|
|
77
84
|
function createTLSchema({
|
|
78
85
|
shapes = defaultShapeSchemas,
|
|
79
86
|
bindings = defaultBindingSchemas,
|
|
87
|
+
assets = defaultAssetSchemas,
|
|
88
|
+
user,
|
|
80
89
|
records = {},
|
|
81
90
|
migrations
|
|
82
91
|
} = {}) {
|
|
@@ -91,7 +100,9 @@ function createTLSchema({
|
|
|
91
100
|
}
|
|
92
101
|
const ShapeRecordType = (0, import_TLShape.createShapeRecordType)(shapes);
|
|
93
102
|
const BindingRecordType = (0, import_TLBinding.createBindingRecordType)(bindings);
|
|
103
|
+
const _AssetRecordType = (0, import_TLAsset.createAssetRecordType)(assets);
|
|
94
104
|
const InstanceRecordType = (0, import_TLInstance.createInstanceRecordType)(stylesById);
|
|
105
|
+
const CustomUserRecordType = user ? (0, import_TLUser.createUserRecordType)(user) : import_TLUser.UserRecordType;
|
|
95
106
|
const builtInTypeNames = /* @__PURE__ */ new Set([
|
|
96
107
|
"asset",
|
|
97
108
|
"binding",
|
|
@@ -103,7 +114,8 @@ function createTLSchema({
|
|
|
103
114
|
"instance_presence",
|
|
104
115
|
"pointer",
|
|
105
116
|
"shape",
|
|
106
|
-
"store"
|
|
117
|
+
"store",
|
|
118
|
+
"user"
|
|
107
119
|
]);
|
|
108
120
|
const customRecordTypes = {};
|
|
109
121
|
for (const [typeName, config] of Object.entries(records)) {
|
|
@@ -116,7 +128,7 @@ function createTLSchema({
|
|
|
116
128
|
}
|
|
117
129
|
return import_store.StoreSchema.create(
|
|
118
130
|
{
|
|
119
|
-
asset:
|
|
131
|
+
asset: _AssetRecordType,
|
|
120
132
|
binding: BindingRecordType,
|
|
121
133
|
camera: import_TLCamera.CameraRecordType,
|
|
122
134
|
document: import_TLDocument.DocumentRecordType,
|
|
@@ -126,6 +138,7 @@ function createTLSchema({
|
|
|
126
138
|
instance_presence: import_TLPresence.InstancePresenceRecordType,
|
|
127
139
|
pointer: import_TLPointer.PointerRecordType,
|
|
128
140
|
shape: ShapeRecordType,
|
|
141
|
+
user: CustomUserRecordType,
|
|
129
142
|
...customRecordTypes
|
|
130
143
|
},
|
|
131
144
|
{
|
|
@@ -140,12 +153,12 @@ function createTLSchema({
|
|
|
140
153
|
import_TLPresence.instancePresenceMigrations,
|
|
141
154
|
import_TLPointer.pointerMigrations,
|
|
142
155
|
import_TLShape.rootShapeMigrations,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
import_TLVideoAsset.videoAssetMigrations,
|
|
156
|
+
import_TLUser.userMigrations,
|
|
157
|
+
...(0, import_recordsWithProps.processPropsMigrations)("asset", assets),
|
|
146
158
|
...(0, import_recordsWithProps.processPropsMigrations)("shape", shapes),
|
|
147
159
|
...(0, import_recordsWithProps.processPropsMigrations)("binding", bindings),
|
|
148
160
|
...(0, import_TLCustomRecord.processCustomRecordMigrations)(records),
|
|
161
|
+
...user?.migrations ?? [],
|
|
149
162
|
...migrations ?? []
|
|
150
163
|
],
|
|
151
164
|
onValidationFailure: import_TLStore.onValidationFailure,
|