@tldraw/tlschema
Version:
tldraw infinite canvas SDK (schema).
1,602 lines (1,545 loc) • 200 kB
TypeScript
import { BaseRecord } from '@tldraw/store';
import { Expand } from '@tldraw/utils';
import { IndexKey } from '@tldraw/utils';
import { JsonObject } from '@tldraw/utils';
import { LegacyMigrations } from '@tldraw/store';
import { MakeUndefinedOptional } from '@tldraw/utils';
import { MigrationId } from '@tldraw/store';
import { MigrationSequence } from '@tldraw/store';
import { RecordId } from '@tldraw/store';
import { RecordScope } from '@tldraw/store';
import { RecordType } from '@tldraw/store';
import { SerializedStore } from '@tldraw/store';
import { Signal } from '@tldraw/state';
import { StandaloneDependsOn } from '@tldraw/store';
import { Store } from '@tldraw/store';
import { StoreSchema } from '@tldraw/store';
import { StoreSnapshot } from '@tldraw/store';
import { StoreValidator } from '@tldraw/store';
import { T } from '@tldraw/validate';
import { UnknownRecord } from '@tldraw/store';
/**
* Migration sequence for arrow binding properties.
* Handles schema evolution over time by defining how to migrate data between versions.
*
* The sequence includes:
* - **AddSnap (v1)**: Adds the `snap` property with default value 'none'
*
* @example
* ```ts
* import { arrowBindingMigrations } from '@tldraw/tlschema'
*
* // Apply migrations when loading older data
* const migratedBinding = arrowBindingMigrations.migrate(oldBinding)
* ```
*
* @public
*/
export declare const arrowBindingMigrations: TLPropsMigrations;
/**
* Validation schema for arrow binding properties.
* Defines the runtime validation rules for each property in TLArrowBindingProps.
*
* @example
* ```ts
* import { arrowBindingProps } from '@tldraw/tlschema'
*
* // Use in custom shape schema
* const customSchema = createTLSchema({
* bindings: {
* arrow: {
* props: arrowBindingProps,
* migrations: arrowBindingMigrations
* }
* }
* })
* ```
*
* @public
*/
export declare const arrowBindingProps: RecordProps<TLArrowBinding>;
/**
* Version identifiers for arrow binding property migrations.
* Each version represents a schema change that requires data migration.
*
* @example
* ```ts
* // Check if migration is needed
* if (bindingVersion < arrowBindingVersions.AddSnap) {
* // Apply AddSnap migration
* }
* ```
*
* @public
*/
export declare const arrowBindingVersions: {
AddSnap: `com.tldraw.binding.arrow/${number}`;
};
/**
* Style property for the arrowhead at the end of an arrow.
*
* Defines the visual style of the arrowhead at the end of the arrow path.
* Defaults to 'arrow' style, giving arrows their characteristic pointed appearance.
*
* @example
* ```ts
* // Arrow with different start and end arrowheads
* const doubleArrow: TLArrowShape = {
* // ... other properties
* props: {
* arrowheadStart: 'triangle',
* arrowheadEnd: 'diamond',
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const ArrowShapeArrowheadEndStyle: EnumStyleProp<"arrow" | "bar" | "diamond" | "dot" | "inverted" | "none" | "pipe" | "square" | "triangle">;
/**
* Style property for the arrowhead at the start of an arrow.
*
* Defines the visual style of the arrowhead at the beginning of the arrow path.
* Can be one of several predefined styles or none for no arrowhead.
*
* @example
* ```ts
* // Arrow with no start arrowhead but triangle end arrowhead
* const arrow: TLArrowShape = {
* // ... other properties
* props: {
* arrowheadStart: 'none',
* arrowheadEnd: 'triangle',
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const ArrowShapeArrowheadStartStyle: EnumStyleProp<"arrow" | "bar" | "diamond" | "dot" | "inverted" | "none" | "pipe" | "square" | "triangle">;
/**
* Style property for arrow shape kind, determining how the arrow is drawn.
*
* Arrows can be drawn as arcs (curved) or elbows (angled with straight segments).
* This affects the visual appearance and behavior of arrow shapes.
*
* @example
* ```ts
* // Create an arrow with arc style (curved)
* const arcArrow: TLArrowShape = {
* // ... other properties
* props: {
* kind: 'arc',
* // ... other props
* }
* }
*
* // Create an arrow with elbow style (angled)
* const elbowArrow: TLArrowShape = {
* // ... other properties
* props: {
* kind: 'elbow',
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const ArrowShapeKindStyle: EnumStyleProp<"arc" | "elbow">;
/**
* Complete migration sequence for arrow shapes.
*
* Defines all the migrations needed to transform arrow shape data from older
* versions to the current version. Each migration handles a specific schema change,
* ensuring backward compatibility and smooth data evolution.
*
* @public
*/
export declare const arrowShapeMigrations: MigrationSequence;
/**
* Validation configuration for arrow shape properties.
*
* Defines the validators for each property of an arrow shape, ensuring that
* arrow shape data is valid and conforms to the expected types and constraints.
*
* @example
* ```ts
* // The validators ensure proper typing and validation
* const validator = T.object(arrowShapeProps)
* const validArrowProps = validator.validate({
* kind: 'arc',
* start: { x: 0, y: 0 },
* end: { x: 100, y: 50 },
* // ... other required properties
* })
* ```
*
* @public
*/
export declare const arrowShapeProps: RecordProps<TLArrowShape>;
/**
* Migration version identifiers for arrow shape properties.
*
* These track the evolution of the arrow shape schema over time, with each
* version representing a specific change to the arrow shape structure or properties.
*
* @example
* ```ts
* // Used internally for migration system
* if (version < arrowShapeVersions.AddLabelColor) {
* // Apply label color migration
* }
* ```
*
* @public
*/
export declare const arrowShapeVersions: {
readonly AddElbow: "com.tldraw.shape.arrow/6";
readonly AddIsPrecise: "com.tldraw.shape.arrow/2";
readonly AddLabelColor: "com.tldraw.shape.arrow/1";
readonly AddLabelPosition: "com.tldraw.shape.arrow/3";
readonly AddRichText: "com.tldraw.shape.arrow/7";
readonly AddRichTextAttrs: "com.tldraw.shape.arrow/8";
readonly AddScale: "com.tldraw.shape.arrow/5";
readonly ExtractBindings: "com.tldraw.shape.arrow/4";
};
/**
* A validator for asset record type IDs. This validator ensures that asset IDs
* follow the correct format and type structure required by tldraw's asset system.
* Asset IDs are prefixed with 'asset:' followed by a unique identifier.
*
* @example
* ```ts
* import { assetIdValidator } from '@tldraw/tlschema'
*
* // Valid asset ID
* const validId = 'asset:abc123'
* console.log(assetIdValidator.isValid(validId)) // true
*
* // Invalid asset ID
* const invalidId = 'shape:abc123'
* console.log(assetIdValidator.isValid(invalidId)) // false
* ```
*
* @public
*/
export declare const assetIdValidator: T.Validator<TLAssetId>;
/**
* Migration sequence for evolving asset record structure over time.
* Handles converting asset records from older schema versions to current format.
*
* @example
* ```ts
* // Migration is applied automatically when loading old documents
* const migratedStore = migrator.migrateStoreSnapshot({
* schema: oldSchema,
* store: oldStoreSnapshot,
* })
* ```
*
* @public
*/
export declare const assetMigrations: MigrationSequence;
/**
* Record type definition for default TLAsset records with document scope and default metadata.
*
* @public
*/
export declare const AssetRecordType: RecordType<TLAsset<"bookmark" | "image" | "video">, "props" | "type">;
/**
* Utilities for encoding and decoding points using base64 and Float16 encoding.
* Provides functions for converting between VecModel arrays and compact base64 strings,
* as well as individual point encoding/decoding operations.
*
* @public
*/
export declare class b64Vecs {
/* Excluded from this release type: _legacyEncodePoint */
/* Excluded from this release type: _legacyEncodePoints */
/* Excluded from this release type: _legacyDecodePoints */
/**
* Encode an array of VecModels using delta encoding for improved precision.
* The first point is stored as Float32 (high precision for absolute position),
* subsequent points are stored as Float16 deltas from the previous point.
* This provides full precision for the starting position and excellent precision
* for deltas between consecutive points (which are typically small values).
*
* Format:
* - First point: 3 Float32 values = 12 bytes = 16 base64 chars
* - Delta points: 3 Float16 values each = 6 bytes = 8 base64 chars each
*
* @param points - An array of VecModel objects to encode
* @returns A base64-encoded string containing delta-encoded points
* @public
*/
static encodePoints(points: VecModel[]): string;
/**
* Decode a delta-encoded base64 string back to an array of absolute VecModels.
* The first point is stored as Float32 (high precision), subsequent points are
* Float16 deltas that are accumulated to reconstruct absolute positions.
*
* @param base64 - The base64-encoded string containing delta-encoded point data
* @returns An array of VecModel objects with absolute coordinates
* @public
*/
static decodePoints(base64: string): VecModel[];
/**
* Get the first point from a delta-encoded base64 string.
* The first point is stored as Float32 for full precision.
*
* @param b64Points - The delta-encoded base64 string
* @returns The first point as a VecModel, or null if the string is too short
* @public
*/
static decodeFirstPoint(b64Points: string): null | VecModel;
/**
* Get the last point from a delta-encoded base64 string.
* Requires decoding all points to accumulate deltas.
*
* @param b64Points - The delta-encoded base64 string
* @returns The last point as a VecModel, or null if the string is too short
* @public
*/
static decodeLastPoint(b64Points: string): null | VecModel;
}
/**
* Validator for binding IDs. Ensures that binding identifiers follow the correct
* format and type constraints required by the tldraw schema system.
*
* Used internally by the schema validation system to verify binding IDs when
* records are created or modified. All binding IDs must be prefixed with 'binding:'.
*
* @example
* ```ts
* import { bindingIdValidator } from '@tldraw/tlschema'
*
* // Validate a binding ID
* const isValid = bindingIdValidator.isValid('binding:abc123') // true
* const isInvalid = bindingIdValidator.isValid('shape:abc123') // false
*
* // Use in custom validation schema
* const customBindingValidator = T.object({
* id: bindingIdValidator,
* // ... other properties
* })
* ```
*
* @public
*/
export declare const bindingIdValidator: T.Validator<TLBindingId>;
/**
* Migration sequence for bookmark assets. Handles the evolution of bookmark asset
* data structure over time, ensuring backward and forward compatibility.
*
* The migration sequence includes:
* 1. **MakeUrlsValid** (v1): Validates and cleans up src URLs, setting invalid URLs to empty string
* 2. **AddFavicon** (v2): Adds the favicon property and validates it, setting invalid favicons to empty string
*
* @public
*/
export declare const bookmarkAssetMigrations: MigrationSequence;
/** @public */
export declare const bookmarkAssetProps: {
description: T.Validator<string>;
favicon: T.Validator<string>;
image: T.Validator<string>;
src: T.Validator<null | string>;
title: T.Validator<string>;
};
/**
* Migration sequence for bookmark shape properties across different schema versions.
* Handles backwards compatibility when bookmark shape structure changes.
*
* @public
*/
export declare const bookmarkShapeMigrations: TLPropsMigrations;
/**
* Validation schema for bookmark shape properties.
*
* @public
* @example
* ```ts
* // Validates bookmark shape properties
* const isValid = bookmarkShapeProps.url.isValid('https://example.com')
* ```
*/
export declare const bookmarkShapeProps: RecordProps<TLBookmarkShape>;
/**
* A serializable model for 2D boxes.
*
* @public */
export declare interface BoxModel {
x: number;
y: number;
w: number;
h: number;
}
/**
* Validator for BoxModel objects that ensures they have numeric x, y coordinates
* for position and w, h values for width and height. Used throughout the schema
* to validate bounding box and rectangular area data structures.
*
* @public
* @example
* ```ts
* const box = { x: 10, y: 20, w: 100, h: 50 }
* const isValid = boxModelValidator.check(box) // true
*
* const invalidBox = { x: 10, y: 20, w: -5, h: 50 }
* const isValidNegative = boxModelValidator.check(invalidBox) // true (validator allows negative values)
* ```
*/
export declare const boxModelValidator: T.ObjectValidator<BoxModel>;
/**
* Record type definition for TLCamera with validation and default properties.
* Configures cameras as session-scoped records that don't persist across sessions.
*
* @example
* ```ts
* // Create a new camera record with defaults
* const cameraRecord = CameraRecordType.create({
* id: 'camera:main'
* // x: 0, y: 0, z: 1, meta: {} are applied as defaults
* })
*
* // Create with custom position and zoom
* const customCamera = CameraRecordType.create({
* id: 'camera:user1',
* x: -100,
* y: -50,
* z: 1.5,
* meta: { userId: 'user123' }
* })
*
* // Store the camera
* store.put([cameraRecord])
* ```
*
* @public
*/
export declare const CameraRecordType: RecordType<TLCamera, never>;
/**
* A validator for canvas UI color types.
*
* This validator ensures that color values are one of the valid canvas UI
* color types defined in {@link TL_CANVAS_UI_COLOR_TYPES}. It provides
* runtime type checking for canvas UI color properties.
*
* @example
* ```ts
* import { canvasUiColorTypeValidator } from '@tldraw/tlschema'
*
* // Validate a color value
* try {
* const validColor = canvasUiColorTypeValidator.validate('accent')
* console.log('Valid color:', validColor)
* } catch (error) {
* console.error('Invalid color:', error.message)
* }
* ```
*
* @public
*/
export declare const canvasUiColorTypeValidator: T.Validator<"accent" | "black" | "laser" | "muted-1" | "selection-fill" | "selection-stroke" | "white">;
/**
* Compress legacy draw shape segments by converting VecModel[] points to delta-encoded base64 format.
* This function is useful for converting old draw shape data to the new compressed format.
* Uses delta encoding for improved Float16 precision.
*
* @public
*/
export declare function compressLegacySegments(segments: {
points: VecModel[];
type: 'free' | 'straight';
}[]): TLDrawShapeSegment[];
/**
* Creates properly formatted migration IDs for asset properties.
*
* @example
* ```ts
* const assetPropsVersions = createAssetPropsMigrationIds('file', {
* AddFoo: 1,
* RenameBar: 2,
* })
* // => { AddFoo: 'com.tldraw.asset.file/1', RenameBar: 'com.tldraw.asset.file/2' }
* ```
*
* @public
*/
export declare function createAssetPropsMigrationIds<S extends string, T extends Record<string, number>>(assetType: S, ids: T): {
[k in keyof T]: `com.tldraw.asset.${S}/${T[k]}`;
};
/**
* Creates a migration sequence for asset properties.
*
* @example
* ```ts
* const migrations = createAssetPropsMigrationSequence({
* sequence: [
* { id: 'com.myapp.asset.custom/1', up: (props) => { props.newField = '' } },
* ],
* })
* ```
*
* @public
*/
export declare function createAssetPropsMigrationSequence(migrations: TLPropsMigrations): TLPropsMigrations;
/* Excluded from this release type: createAssetRecordType */
/**
* Creates a validator for a specific asset record type. This factory function generates
* a complete validator that validates the entire asset record structure including the
* base properties (id, typeName, type, meta) and the type-specific props.
*
* @param type - The asset type identifier (e.g., 'image', 'video', 'bookmark')
* @param props - A validator or per-key validator record for the asset's type-specific properties
* @param meta - An optional per-key validator record for the asset's meta properties
* @returns A complete validator for the asset record type
*
* @example
* ```ts
* import { createAssetValidator, TLBaseAsset } from '@tldraw/tlschema'
* import { T } from '@tldraw/validate'
*
* // Define a custom asset type
* type TLCustomAsset = TLBaseAsset<'custom', {
* url: string
* title: string
* description?: string
* }>
*
* // Create validator using a per-key record (recommended)
* const customAssetValidator = createAssetValidator('custom', {
* url: T.string,
* title: T.string,
* description: T.string.optional()
* })
*
* // Or using a T.object validator
* const customAssetValidator2 = createAssetValidator('custom', T.object({
* url: T.string,
* title: T.string,
* description: T.string.optional()
* }))
* ```
*
* @public
*/
export declare function createAssetValidator<Type extends string, Props extends JsonObject, Meta extends JsonObject = JsonObject>(type: Type, props?: {
[K in keyof Props]: T.Validatable<Props[K]>;
} | T.Validator<Props>, meta?: {
[K in keyof Meta]: T.Validatable<Meta[K]>;
}): T.ObjectValidator<Expand< { [P in "id" | "meta" | "typeName" | (undefined extends Props ? never : "props") | (undefined extends Type ? never : "type")]: TLBaseAsset<Type, Props>[P]; } & { [P in (undefined extends Props ? "props" : never) | (undefined extends Type ? "type" : never)]?: TLBaseAsset<Type, Props>[P] | undefined; }>>;
/**
* Creates a new TLBindingId with proper formatting.
* Generates a unique ID if none is provided, or formats a provided ID correctly.
*
* @param id - Optional custom ID suffix. If not provided, a unique ID is generated
* @returns A properly formatted binding ID
*
* @example
* ```ts
* // Create with auto-generated ID
* const bindingId1 = createBindingId() // 'binding:abc123'
*
* // Create with custom ID
* const bindingId2 = createBindingId('myCustomBinding') // 'binding:myCustomBinding'
*
* // Use in binding creation
* const binding: TLBinding = {
* id: createBindingId(),
* type: 'arrow',
* fromId: 'shape:arrow1',
* toId: 'shape:rectangle1',
* // ... other properties
* }
* ```
*
* @public
*/
export declare function createBindingId(id?: string): TLBindingId;
/**
* Creates properly formatted migration IDs for binding property migrations.
* Follows the convention: 'com.tldraw.binding.\{bindingType\}/\{version\}'
*
* @param bindingType - The type of binding these migrations apply to
* @param ids - Object mapping migration names to version numbers
* @returns Object with formatted migration IDs
*
* @example
* ```ts
* // Create migration IDs for custom binding
* const myBindingVersions = createBindingPropsMigrationIds('myCustomBinding', {
* AddNewProperty: 1,
* UpdateProperty: 2
* })
*
* // Result:
* // {
* // AddNewProperty: 'com.tldraw.binding.myCustomBinding/1',
* // UpdateProperty: 'com.tldraw.binding.myCustomBinding/2'
* // }
* ```
*
* @public
*/
export declare function createBindingPropsMigrationIds<S extends string, T extends Record<string, number>>(bindingType: S, ids: T): {
[k in keyof T]: `com.tldraw.binding.${S}/${T[k]}`;
};
/**
* Creates a migration sequence for binding properties.
* This is a pass-through function that validates and returns the provided migrations.
*
* @param migrations - The migration sequence for binding properties
* @returns The validated migration sequence
*
* @example
* ```ts
* // Define migrations for custom binding properties
* const myBindingMigrations = createBindingPropsMigrationSequence({
* sequence: [
* {
* id: 'com.myapp.binding.custom/1.0.0',
* up: (props) => ({ ...props, newProperty: 'default' }),
* down: ({ newProperty, ...props }) => props
* }
* ]
* })
* ```
*
* @public
*/
export declare function createBindingPropsMigrationSequence(migrations: TLPropsMigrations): TLPropsMigrations;
/**
* Creates a runtime validator for a specific binding type. This factory function
* generates a complete validation schema for custom bindings that extends TLBaseBinding.
*
* The validator ensures all binding records conform to the expected structure with
* proper type safety and runtime validation. It validates the base binding properties
* (id, type, fromId, toId) along with custom props and meta fields.
*
* @param type - The string literal type identifier for this binding (e.g., 'arrow', 'custom')
* @param props - Optional validation schema for binding-specific properties
* @param meta - Optional validation schema for metadata fields
*
* @returns A validator object that can validate complete binding records
*
* @example
* ```ts
* import { createBindingValidator } from '@tldraw/tlschema'
* import { T } from '@tldraw/validate'
*
* // Create validator for a custom binding type
* const myBindingValidator = createBindingValidator(
* 'myBinding',
* {
* strength: T.number,
* color: T.string,
* enabled: T.boolean
* },
* {
* createdAt: T.number,
* author: T.string
* }
* )
*
* // Validate a binding instance
* const bindingData = {
* id: 'binding:123',
* typeName: 'binding',
* type: 'myBinding',
* fromId: 'shape:abc',
* toId: 'shape:def',
* props: {
* strength: 0.8,
* color: 'red',
* enabled: true
* },
* meta: {
* createdAt: Date.now(),
* author: 'user123'
* }
* }
*
* const isValid = myBindingValidator.isValid(bindingData) // true
* ```
*
* @example
* ```ts
* // Simple binding without custom props or meta
* const simpleBindingValidator = createBindingValidator('simple')
*
* // This will use JsonValue validation for props and meta
* const binding = {
* id: 'binding:456',
* typeName: 'binding',
* type: 'simple',
* fromId: 'shape:start',
* toId: 'shape:end',
* props: {}, // Any JSON value allowed
* meta: {} // Any JSON value allowed
* }
* ```
*
* @public
*/
export declare function createBindingValidator<Type extends string, Props extends JsonObject, Meta extends JsonObject>(type: Type, props?: {
[K in keyof Props]: T.Validatable<Props[K]>;
}, meta?: {
[K in keyof Meta]: T.Validatable<Meta[K]>;
}): T.ObjectValidator<Expand< { [P in "fromId" | "id" | "meta" | "toId" | "typeName" | (undefined extends Props ? never : "props") | (undefined extends Type ? never : "type")]: TLBaseBinding<Type, Props>[P]; } & { [P in (undefined extends Props ? "props" : never) | (undefined extends Type ? "type" : never)]?: TLBaseBinding<Type, Props>[P] | undefined; }>>;
/**
* Create a cached {@link TLUserStore.resolve} implementation.
*
* Wraps a reactive lookup function so that each `userId` gets a single
* stable {@link @tldraw/state#Signal | Signal} that is reused across calls.
* The `resolveFn` is evaluated inside a `computed`, so any `.get()` calls
* it makes are automatically tracked.
*
* @param resolveFn - A function that resolves a raw user-ID string to a
* {@link TLUser} or `null`. Called reactively inside a `computed`.
* @returns A function suitable for use as `TLUserStore.resolve`.
*
* @example
* ```ts
* const users: TLUserStore = {
* currentUser: currentUserSignal,
* resolve: createCachedUserResolve(
* (userId) => usersAtom.get()[createUserId(userId)] ?? null
* ),
* }
* ```
*
* @public
*/
export declare function createCachedUserResolve(resolveFn: (userId: string) => null | TLUser): (userId: string) => Signal<null | TLUser>;
/**
* Creates a unique ID for a custom record type.
*
* @param typeName - The type name of the custom record
* @param id - Optional custom ID suffix. If not provided, a unique ID will be generated
* @returns A properly formatted record ID
*
* @example
* ```ts
* // Create with auto-generated ID
* const commentId = createCustomRecordId('comment') // 'comment:abc123'
*
* // Create with custom ID
* const customId = createCustomRecordId('comment', 'my-comment') // 'comment:my-comment'
* ```
*
* @public
*/
export declare function createCustomRecordId<T extends string>(typeName: T, id?: string): RecordId<UnknownRecord> & `${T}:${string}`;
/**
* Creates properly formatted migration IDs for custom record migrations.
*
* Generates standardized migration IDs following the convention:
* `com.tldraw.{recordType}/{version}`
*
* @param recordType - The type name of the custom record
* @param ids - Record mapping migration names to version numbers
* @returns Record with the same keys but formatted migration ID values
*
* @example
* ```ts
* const commentVersions = createCustomRecordMigrationIds('comment', {
* AddAuthorId: 1,
* AddCreatedAt: 2,
* RefactorReactions: 3
* })
* // Result: {
* // AddAuthorId: 'com.tldraw.comment/1',
* // AddCreatedAt: 'com.tldraw.comment/2',
* // RefactorReactions: 'com.tldraw.comment/3'
* // }
* ```
*
* @public
*/
export declare function createCustomRecordMigrationIds<const S extends string, const T extends Record<string, number>>(recordType: S, ids: T): {
[k in keyof T]: `com.tldraw.${S}/${T[k]}`;
};
/**
* Creates a migration sequence for custom record types.
*
* This is a pass-through function that maintains the same structure as the input.
* It's used for consistency and to provide a clear API for defining custom record migrations.
*
* @param migrations - The migration sequence to create
* @returns The same migration sequence (pass-through)
*
* @example
* ```ts
* const commentMigrations = createCustomRecordMigrationSequence({
* sequence: [
* {
* id: 'com.myapp.comment/1',
* up: (record) => ({ ...record, authorId: record.authorId ?? 'unknown' }),
* down: ({ authorId, ...record }) => record
* }
* ]
* })
* ```
*
* @public
*/
export declare function createCustomRecordMigrationSequence(migrations: TLPropsMigrations): TLPropsMigrations;
/**
* Creates a derivation that represents the current presence state of the current user.
*
* This function returns a derivation factory that, when given a store, creates a computed signal
* containing the user's current presence state. The presence state includes information like cursor
* position, selected shapes, camera position, and user metadata that gets synchronized in
* multiplayer scenarios.
*
* @param $user - A reactive signal containing the user information, or `null` when anonymous
* @param opts - Optional configuration for instance ID and presence derivation
* @returns A function that takes a store and returns a computed signal of the user's presence state
*
* @example
* ```ts
* import { createPresenceStateDerivation } from '@tldraw/tlschema'
* import { atom } from '@tldraw/state'
*
* const userSignal = atom('user', { id: 'user-123', name: 'Alice', color: '#ff0000', meta: {} })
* const presenceDerivation = createPresenceStateDerivation(userSignal)
*
* // Use with a store to get reactive presence state
* const presenceState = presenceDerivation(store)
* console.log(presenceState.get()) // Current user presence or null
* ```
*
* @public
*/
export declare function createPresenceStateDerivation($user: Signal<null | TLUser>, opts?: CreatePresenceStateDerivationOpts): (store: TLStore) => Signal<null | TLInstancePresence, unknown>;
/** @public */
export declare interface CreatePresenceStateDerivationOpts {
/** Custom instance ID. If not provided, one is generated from the store ID. */
instanceId?: TLInstancePresence['id'];
/**
* Override how presence state is built from the store and current user.
* Defaults to {@link getDefaultUserPresence}.
*/
getUserPresence?(store: TLStore, user: TLUser): null | TLPresenceStateInfo;
}
/**
* Creates a new shape ID.
*
* @param id - Optional custom ID suffix. If not provided, a unique ID will be generated
* @returns A new shape ID with the "shape:" prefix
*
* @example
* ```ts
* // Create a shape with auto-generated ID
* const shapeId = createShapeId() // "shape:abc123"
*
* // Create a shape with custom ID
* const customShapeId = createShapeId('my-rectangle') // "shape:my-rectangle"
*
* // Use in shape creation
* const newShape: TLGeoShape = {
* id: createShapeId(),
* type: 'geo',
* x: 100,
* y: 200,
* // ... other properties
* }
* ```
*
* @public
*/
export declare function createShapeId(id?: string): TLShapeId;
/**
* Creates properly formatted migration IDs for shape properties.
*
* Generates standardized migration IDs following the convention:
* `com.tldraw.shape.{shapeType}/{version}`
*
* @param shapeType - The type of shape these migrations apply to
* @param ids - Record mapping migration names to version numbers
* @returns Record with the same keys but formatted migration ID values
*
* @example
* ```ts
* const myShapeVersions = createShapePropsMigrationIds('custom', {
* AddColor: 1,
* AddSize: 2,
* RefactorProps: 3
* })
* // Result: {
* // AddColor: 'com.tldraw.shape.custom/1',
* // AddSize: 'com.tldraw.shape.custom/2',
* // RefactorProps: 'com.tldraw.shape.custom/3'
* // }
* ```
*
* @public
*/
export declare function createShapePropsMigrationIds<const S extends string, const T extends Record<string, number>>(shapeType: S, ids: T): {
[k in keyof T]: `com.tldraw.shape.${S}/${T[k]}`;
};
/**
* Creates a migration sequence for shape properties.
*
* This is a pass-through function that maintains the same structure as the input.
* It's used for consistency and to provide a clear API for defining shape property migrations.
*
* @param migrations - The migration sequence to create
* @returns The same migration sequence (pass-through)
*
* @example
* ```ts
* const myShapeMigrations = createShapePropsMigrationSequence({
* sequence: [
* {
* id: 'com.myapp.shape.custom/1.0.0',
* up: (props) => ({ ...props, newProperty: 'default' }),
* down: ({ newProperty, ...props }) => props
* }
* ]
* })
* ```
*
* @public
*/
export declare function createShapePropsMigrationSequence(migrations: TLPropsMigrations): TLPropsMigrations;
/**
* Creates a validator for a specific shape type.
*
* This function generates a complete validator that can validate shape records
* of the specified type, including both the base shape properties and any
* custom properties and metadata specific to that shape type.
*
* @param type - The string literal type for this shape (e.g., 'geo', 'arrow')
* @param props - Optional validator configuration for shape-specific properties
* @param meta - Optional validator configuration for shape-specific metadata
* @returns A validator that can validate complete shape records of the specified type
*
* @example
* ```ts
* // Create a validator for a custom shape type
* const customShapeValidator = createShapeValidator('custom', {
* width: T.number,
* height: T.number,
* color: T.string
* })
*
* // Use the validator to validate shape data
* const shapeData = {
* id: 'shape:abc123',
* typeName: 'shape',
* type: 'custom',
* x: 100,
* y: 200,
* // ... other base properties
* props: {
* width: 150,
* height: 100,
* color: 'red'
* }
* }
*
* const validatedShape = customShapeValidator.validate(shapeData)
* ```
*
* @public
*/
export declare function createShapeValidator<Type extends string, Props extends JsonObject, Meta extends JsonObject>(type: Type, props?: {
[K in keyof Props]: T.Validatable<Props[K]>;
}, meta?: {
[K in keyof Meta]: T.Validatable<Meta[K]>;
}): T.ObjectValidator<Expand< { [P in "id" | "index" | "isLocked" | "meta" | "opacity" | "parentId" | "rotation" | "typeName" | "x" | "y" | (undefined extends Props ? never : "props") | (undefined extends Type ? never : "type")]: TLBaseShape<Type, Props>[P]; } & { [P in (undefined extends Props ? "props" : never) | (undefined extends Type ? "type" : never)]?: TLBaseShape<Type, Props>[P] | undefined; }>>;
/**
* Creates a complete TLSchema for use with tldraw stores. This schema defines the structure,
* validation, and migration sequences for all record types in a tldraw application.
*
* The schema includes all core record types (pages, cameras, instances, etc.) plus the
* shape, binding, asset, and custom record types you specify. Style properties are
* automatically collected from all shapes to ensure consistency across the application.
*
* @param options - Configuration options for the schema
* - shapes - Shape schema configurations. Defaults to defaultShapeSchemas if not provided
* - bindings - Binding schema configurations. Defaults to defaultBindingSchemas if not provided
* - assets - Asset schema configurations. Defaults to defaultAssetSchemas if not provided
* - user - Custom user record configuration with meta validators and migrations
* - records - Custom record type configurations. These are additional record types beyond
* the built-in shapes, bindings, assets, etc.
* - migrations - Additional migration sequences to include in the schema
* @returns A complete TLSchema ready for use with Store creation
*
* @public
* @example
* ```ts
* import {
* createTLSchema,
* defaultShapeSchemas,
* defaultBindingSchemas,
* defaultAssetSchemas,
* } from '@tldraw/tlschema'
* import { Store } from '@tldraw/store'
*
* // Create schema with all default shapes, bindings, and assets
* const schema = createTLSchema()
*
* // Create schema with custom shapes added
* const customSchema = createTLSchema({
* shapes: {
* ...defaultShapeSchemas,
* myCustomShape: {
* props: myCustomShapeProps,
* migrations: myCustomShapeMigrations,
* },
* },
* bindings: defaultBindingSchemas,
* assets: defaultAssetSchemas,
* })
*
* // Create schema with custom user metadata
* const schemaWithCustomUser = createTLSchema({
* user: {
* meta: {
* isAdmin: T.boolean,
* department: T.string,
* },
* },
* })
*
* // Create schema with custom record types
* const schemaWithCustomRecords = createTLSchema({
* records: {
* comment: {
* scope: 'document',
* validator: T.object({
* id: T.string,
* typeName: T.literal('comment'),
* text: T.string,
* shapeId: T.string,
* }),
* },
* },
* })
*
* // Use the schema with a store
* const store = new Store({
* schema: customSchema,
* props: {
* defaultName: 'My Drawing',
* },
* })
* ```
*/
export declare function createTLSchema({ shapes, bindings, assets, user, records, migrations }?: {
assets?: Record<string, SchemaPropsInfo>;
bindings?: Record<string, SchemaPropsInfo>;
migrations?: readonly MigrationSequence[];
records?: Record<string, CustomRecordInfo>;
shapes?: Record<string, SchemaPropsInfo>;
user?: UserSchemaInfo;
}): TLSchema;
/** @public */
export declare function createUserId(id: string): TLUserId;
/**
* Creates a user record type with optional custom meta validation.
*
* When `meta` validators are provided, the user record's `meta` field will
* validate those specific fields (when present) while still allowing
* arbitrary additional JSON properties. Custom meta fields are treated as
* optional so that user records created without them remain valid.
*
* @param config - Optional configuration for custom meta validators
* @returns A configured user record type
*
* @example
* ```ts
* import { createUserRecordType } from '@tldraw/tlschema'
* import { T } from '@tldraw/validate'
*
* const CustomUserRecordType = createUserRecordType({
* meta: {
* isAdmin: T.boolean,
* department: T.string,
* },
* })
* ```
*
* @public
*/
export declare function createUserRecordType(config?: {
meta?: Record<string, T.Validatable<any>>;
}): RecordType<TLUser, never>;
/**
* Configuration for a custom record type in the schema.
*
* Custom record types allow you to add entirely new data types to the tldraw store
* that don't fit into the existing shape, binding, or asset categories. This is useful
* for storing domain-specific data like comments, annotations, or application state
* that needs to participate in persistence and synchronization.
*
* @example
* ```ts
* const commentRecordConfig: CustomRecordInfo = {
* scope: 'document',
* validator: T.object({
* id: T.string,
* typeName: T.literal('comment'),
* text: T.string,
* shapeId: T.string,
* authorId: T.string,
* createdAt: T.number,
* }),
* migrations: createRecordMigrationSequence({
* sequenceId: 'com.myapp.comment',
* recordType: 'comment',
* sequence: [],
* }),
* }
* ```
*
* @public
*/
export declare interface CustomRecordInfo {
/**
* The scope determines how records of this type are persisted and synchronized:
* - **document**: Persisted and synced across all clients
* - **session**: Local to current session, not synced
* - **presence**: Ephemeral presence data, may be synced but not persisted
*/
scope: RecordScope;
/**
* Validator for the complete record structure.
*
* Should validate the entire record including `id` and `typeName` fields.
* Use validators like T.object, T.string, etc.
*/
validator: T.Validatable<any>;
/**
* Optional migration sequence for handling schema evolution over time.
*
* Can be a full MigrationSequence or a simplified TLPropsMigrations format.
* If not provided, an empty migration sequence will be created automatically.
*/
migrations?: MigrationSequence | TLPropsMigrations;
/**
* Optional factory function that returns default property values for new records.
*
* Called when creating new records to provide initial values for any properties
* not explicitly provided during creation.
*/
createDefaultProperties?: () => Record<string, unknown>;
}
/**
* Default asset schema configurations for all built-in tldraw asset types.
*
* @public
* @example
* ```ts
* import { createTLSchema, defaultAssetSchemas } from '@tldraw/tlschema'
*
* const schema = createTLSchema({
* assets: defaultAssetSchemas,
* })
* ```
*/
export declare const defaultAssetSchemas: {
bookmark: {
migrations: MigrationSequence;
props: {
description: T.Validator<string>;
favicon: T.Validator<string>;
image: T.Validator<string>;
src: T.Validator<null | string>;
title: T.Validator<string>;
};
};
image: {
migrations: MigrationSequence;
props: {
fileSize: T.Validator<number | undefined>;
h: T.Validator<number>;
isAnimated: T.Validator<boolean>;
mimeType: T.Validator<null | string>;
name: T.Validator<string>;
pixelRatio: T.Validator<number | undefined>;
src: T.Validator<null | string>;
w: T.Validator<number>;
};
};
video: {
migrations: MigrationSequence;
props: {
fileSize: T.Validator<number | undefined>;
h: T.Validator<number>;
isAnimated: T.Validator<boolean>;
mimeType: T.Validator<null | string>;
name: T.Validator<string>;
src: T.Validator<null | string>;
w: T.Validator<number>;
};
};
};
/**
* Default binding schema configurations for all built-in tldraw binding types.
* Bindings represent relationships between shapes, such as arrows connected to shapes.
*
* Currently includes:
* - arrow: Bindings that connect arrow shapes to other shapes at specific anchor points
*
* @public
* @example
* ```ts
* import { createTLSchema, defaultBindingSchemas } from '@tldraw/tlschema'
*
* // Use default bindings
* const schema = createTLSchema({
* bindings: defaultBindingSchemas,
* })
*
* // Add custom binding alongside defaults
* const customSchema = createTLSchema({
* bindings: {
* ...defaultBindingSchemas,
* myCustomBinding: {
* props: myCustomBindingProps,
* migrations: myCustomBindingMigrations,
* },
* },
* })
* ```
*/
export declare const defaultBindingSchemas: {
arrow: {
migrations: TLPropsMigrations;
props: RecordProps<TLArrowBinding>;
};
};
/**
* @public
*/
export declare const DefaultColorStyle: EnumStyleProp<TLDefaultColorStyle>;
/**
* Default dash style property used by tldraw shapes for line styling.
* Controls how shape outlines and lines are rendered with different dash patterns.
*
* Available values:
* - `draw` - Hand-drawn, sketchy line style
* - `solid` - Continuous solid line
* - `dashed` - Evenly spaced dashes
* - `dotted` - Evenly spaced dots
*
* @example
* ```ts
* import { DefaultDashStyle } from '@tldraw/tlschema'
*
* // Use in shape props definition
* interface MyShapeProps {
* dash: typeof DefaultDashStyle
* // other props...
* }
*
* // Create a shape with dashed outline
* const shape = {
* // ... other properties
* props: {
* dash: 'dashed' as const,
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const DefaultDashStyle: EnumStyleProp<"dashed" | "dotted" | "draw" | "none" | "solid">;
/**
* Default fill style property used by tldraw shapes for interior styling.
* Controls how the inside of shapes are filled or left empty.
*
* Available values:
* - `none` - No fill, shape interior is transparent
* - `semi` - Semi-transparent fill using the shape's color
* - `solid` - Solid fill using the shape's color
* - `pattern` - Crosshatch pattern fill using the shape's color
* - `fill` - Alternative solid fill variant
*
* @example
* ```ts
* import { DefaultFillStyle } from '@tldraw/tlschema'
*
* // Use in shape props definition
* interface MyShapeProps {
* fill: typeof DefaultFillStyle
* // other props...
* }
*
* // Create a shape with solid fill
* const shape = {
* // ... other properties
* props: {
* fill: 'solid' as const,
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const DefaultFillStyle: EnumStyleProp<"fill" | "lined-fill" | "none" | "pattern" | "semi" | "solid">;
/**
* Mapping of font style names to their corresponding CSS font-family declarations.
* These are the actual CSS font families used when rendering text with each font style.
*
* @example
* ```ts
* import { DefaultFontFamilies, TLDefaultFontStyle } from '@tldraw/tlschema'
*
* // Get CSS font family for a font style
* const fontStyle: TLDefaultFontStyle = 'mono'
* const cssFamily = DefaultFontFamilies[fontStyle] // "'tldraw_mono', monospace"
*
* // Apply to DOM element
* element.style.fontFamily = DefaultFontFamilies.sans
* ```
*
* @public
*/
export declare const DefaultFontFamilies: {
draw: string;
mono: string;
sans: string;
serif: string;
};
/**
* Default font style property used by tldraw shapes for text styling.
* Controls which typeface is used for text content within shapes.
*
* Available values:
* - `draw` - Hand-drawn, sketchy font style
* - `sans` - Clean sans-serif font
* - `serif` - Traditional serif font
* - `mono` - Monospace font for code-like text
*
* @example
* ```ts
* import { DefaultFontStyle } from '@tldraw/tlschema'
*
* // Use in shape props definition
* interface MyTextShapeProps {
* font: typeof DefaultFontStyle
* // other props...
* }
*
* // Create a text shape with monospace font
* const textShape = {
* // ... other properties
* props: {
* font: 'mono' as const,
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const DefaultFontStyle: EnumStyleProp<"draw" | "mono" | "sans" | "serif">;
/**
* Default horizontal alignment style property used by tldraw shapes for text positioning.
* Controls how text content is horizontally aligned within shape boundaries.
*
* Available values:
* - `start` - Align text to the start (left in LTR, right in RTL)
* - `middle` - Center text horizontally
* - `end` - Align text to the end (right in LTR, left in RTL)
* - `start-legacy` - Legacy start alignment (deprecated)
* - `end-legacy` - Legacy end alignment (deprecated)
* - `middle-legacy` - Legacy middle alignment (deprecated)
*
* @example
* ```ts
* import { DefaultHorizontalAlignStyle } from '@tldraw/tlschema'
*
* // Use in shape props definition
* interface MyTextShapeProps {
* align: typeof DefaultHorizontalAlignStyle
* // other props...
* }
*
* // Create a shape with center-aligned text
* const textShape = {
* // ... other properties
* props: {
* align: 'middle' as const,
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const DefaultHorizontalAlignStyle: EnumStyleProp<"end-legacy" | "end" | "middle-legacy" | "middle" | "start-legacy" | "start">;
/**
* Default shape schema configurations for all built-in tldraw shape types.
* Each shape type includes its validation props and migration sequences.
*
* This object contains schema information for:
* - arrow: Directional lines that can bind to other shapes
* - bookmark: Website bookmark cards with preview information
* - draw: Freehand drawing paths created with drawing tools
* - embed: Embedded content from external services (YouTube, Figma, etc.)
* - frame: Container shapes for organizing content
* - geo: Geometric shapes (rectangles, ellipses, triangles, etc.)
* - group: Logical groupings of multiple shapes
* - highlight: Highlighting strokes from the highlighter tool
* - image: Raster image shapes referencing image assets
* - line: Multi-point lines and splines
* - note: Sticky note shapes with text content
* - text: Rich text shapes with formatting support
* - video: Video shapes referencing video assets
*
* @public
* @example
* ```ts
* import { createTLSchema, defaultShapeSchemas } from '@tldraw/tlschema'
*
* // Use all default shapes
* const schema = createTLSchema({
* shapes: defaultShapeSchemas,
* })
*
* // Use only specific default shapes
* const minimalSchema = createTLSchema({
* shapes: {
* geo: defaultShapeSchemas.geo,
* text: defaultShapeSchemas.text,
* },
* })
* ```
*/
export declare const defaultShapeSchemas: {
arrow: {
migrations: MigrationSequence;
props: RecordProps<TLArrowShape>;
};
bookmark: {
migrations: TLPropsMigrations;
props: RecordProps<TLBookmarkShape>;
};
draw: {
migrations: TLPropsMigrations;
props: RecordProps<TLDrawShape>;
};
embed: {
migrations: TLPropsMigrations;
props: RecordProps<TLEmbedShape>;
};
frame: {
migrations: TLPropsMigrations;
props: RecordProps<TLFrameShape>;
};
geo: {
migrations: TLPropsMigrations;
props: RecordProps<TLGeoShape>;
};
group: {
migrations: TLPropsMigrations;
props: RecordProps<TLGroupShape>;
};
highlight: {
migrations: TLPropsMigrations;
props: RecordProps<TLHighlightShape>;
};
image: {
migrations: TLPropsMigrations;
props: RecordProps<TLImageShape>;
};
line: {
migrations: TLPropsMigrations;
props: RecordProps<TLLineShape>;
};
note: {
migrations: TLPropsMigrations;
props: RecordProps<TLNoteShape>;
};
text: {
migrations: TLPropsMigrations;
props: RecordProps<TLTextShape>;
};
video: {
migrations: TLPropsMigrations;
props: RecordProps<TLVideoShape>;
};
};
/**
* Default size style property used by tldraw shapes for scaling visual elements.
* Controls the relative size of shape elements like stroke width, text size, and other proportional features.
*
* Available values:
* - `s` - Small size
* - `m` - Medium size (default)
* - `l` - Large size
* - `xl` - Extra large size
*
* @example
* ```ts
* import { DefaultSizeStyle } from '@tldraw/tlschema'
*
* // Use in shape props definition
* interface MyShapeProps {
* size: typeof DefaultSizeStyle
* // other props...
* }
*
* // Create a shape with large size
* const shape = {
* // ... other properties
* props: {
* size: 'l' as const,
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const DefaultSizeStyle: EnumStyleProp<"l" | "m" | "s" | "xl">;
/**
* Default text alignment style property used by tldraw text shapes.
* Controls how text content is aligned within text-based shapes like text boxes and notes.
*
* Available values:
* - `start` - Align text to the start (left in LTR, right in RTL)
* - `middle` - Center text horizontally
* - `end` - Align text to the end (right in LTR, left in RTL)
*
* @example
* ```ts
* import { DefaultTextAlignStyle } from '@tldraw/tlschema'
*
* // Use in text shape props definition
* interface MyTextShapeProps {
* textAlign: typeof DefaultTextAlignStyle
* // other props...
* }
*
* // Create a text shape with center alignment
* const textShape = {
* // ... other properties
* props: {
* textAlign: 'middle' as const,
* // ... other props
* }
* }
* ```
*
* @public
*/
export declare const DefaultTextAlignStyle: EnumStyleProp<"end" | "middle" | "start">;
/**
* Default vertical alignment style property used by tldraw shapes for text positioning.
* Controls how text content is vertically aligned within shape boundaries.
*
* Available values:
* - `start` - Align text to the top
* - `middl