UNPKG

@tldraw/editor

Version:

tldraw infinite canvas SDK (editor).

1,462 lines (1,390 loc) 322 kB
import { Atom } from '@tldraw/state'; import { AtomSet } from '@tldraw/store'; import { Awaitable } from '@tldraw/utils'; import { BoxModel } from '@tldraw/tlschema'; import { ComponentType } from 'react'; import { Computed } from '@tldraw/state'; import { CustomRecordInfo } from '@tldraw/tlschema'; import { Dispatch } from 'react'; import { Editor as Editor_2 } from '@tiptap/core'; import { EditorProviderProps as EditorProviderProps_2 } from '@tiptap/react'; import EventEmitter from 'eventemitter3'; import { ExoticComponent } from 'react'; import { ExtractShapeByProps } from '@tldraw/tlschema'; import { ForwardRefExoticComponent } from 'react'; import { FragmentProps } from 'react'; import { HistoryEntry } from '@tldraw/store'; import { IndexKey } from '@tldraw/utils'; import { JsonObject } from '@tldraw/utils'; import { JSX } from 'react/jsx-runtime'; import { LegacyMigrations } from '@tldraw/store'; import { MigrationSequence } from '@tldraw/store'; import { Node as Node_2 } from '@tiptap/pm/model'; import { PerformanceTracker } from '@tldraw/utils'; import * as React_2 from 'react'; import { default as React_3 } from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { RecordProps } from '@tldraw/tlschema'; import { RecordsDiff } from '@tldraw/store'; import { RefAttributes } from 'react'; import { RefObject } from 'react'; import { SerializedSchema } from '@tldraw/store'; import { SerializedStore } from '@tldraw/store'; import { SetStateAction } from 'react'; import { Signal } from '@tldraw/state'; import { Store } from '@tldraw/store'; import { StoreSchema } from '@tldraw/store'; import { StoreSideEffects } from '@tldraw/store'; import { StyleProp } from '@tldraw/tlschema'; import { StylePropValue } from '@tldraw/tlschema'; import { T } from '@tldraw/validate'; import { Timers } from '@tldraw/utils'; import { TLAsset } from '@tldraw/tlschema'; import { TLAssetId } from '@tldraw/tlschema'; import { TLAssetPartial } from '@tldraw/tlschema'; import { TLAssetStore } from '@tldraw/tlschema'; import { TLBaseShape } from '@tldraw/tlschema'; import { TLBinding } from '@tldraw/tlschema'; import { TLBindingCreate } from '@tldraw/tlschema'; import { TLBindingId } from '@tldraw/tlschema'; import { TLBindingUpdate } from '@tldraw/tlschema'; import { TLBookmarkAsset } from '@tldraw/tlschema'; import { TLCamera } from '@tldraw/tlschema'; import { TLCreateShapePartial } from '@tldraw/tlschema'; import { TLCursor } from '@tldraw/tlschema'; import { TLCursorType } from '@tldraw/tlschema'; import { TLDefaultColor } from '@tldraw/tlschema'; import { TLDefaultColorStyle } from '@tldraw/tlschema'; import { TLDefaultDashStyle } from '@tldraw/tlschema'; import { TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'; import { TLDocument } from '@tldraw/tlschema'; import { TLFontFace } from '@tldraw/tlschema'; import { TLGroupShape } from '@tldraw/tlschema'; import { TLHandle } from '@tldraw/tlschema'; import { TLImageAsset } from '@tldraw/tlschema'; import { TLInstance } from '@tldraw/tlschema'; import { TLInstancePageState } from '@tldraw/tlschema'; import { TLInstancePresence } from '@tldraw/tlschema'; import { TLPage } from '@tldraw/tlschema'; import { TLPageId } from '@tldraw/tlschema'; import { TLParentId } from '@tldraw/tlschema'; import { TLPropsMigrations } from '@tldraw/tlschema'; import { TLRecord } from '@tldraw/tlschema'; import { TLRichText } from '@tldraw/tlschema'; import { TLScribble } from '@tldraw/tlschema'; import { TLShape } from '@tldraw/tlschema'; import { TLShapeCrop } from '@tldraw/tlschema'; import { TLShapeId } from '@tldraw/tlschema'; import { TLShapePartial } from '@tldraw/tlschema'; import { TLStore } from '@tldraw/tlschema'; import { TLStoreProps } from '@tldraw/tlschema'; import { TLStoreSchema } from '@tldraw/tlschema'; import { TLStoreSnapshot } from '@tldraw/tlschema'; import { TLTheme } from '@tldraw/tlschema'; import { TLThemeColors } from '@tldraw/tlschema'; import { TLThemeId } from '@tldraw/tlschema'; import { TLThemes } from '@tldraw/tlschema'; import { TLUnknownAsset } from '@tldraw/tlschema'; import { TLUnknownBinding } from '@tldraw/tlschema'; import { TLUnknownShape } from '@tldraw/tlschema'; import { TLUser } from '@tldraw/tlschema'; import { TLUserStore } from '@tldraw/tlschema'; import { TLVideoAsset } from '@tldraw/tlschema'; import { UnknownRecord } from '@tldraw/store'; import { VecModel } from '@tldraw/tlschema'; /* Excluded from this release type: activeElementShouldCaptureKeys */ /** * Get the angle of a point on an arc. * @param fromAngle - The angle from center to arc's start point (A) on the circle * @param toAngle - The angle from center to arc's end point (B) on the circle * @param direction - The direction of the arc (1 = counter-clockwise, -1 = clockwise) * @returns The distance in radians between the two angles according to the direction * @public */ export declare function angleDistance(fromAngle: number, toAngle: number, direction: number): number; /* Excluded from this release type: applyRotationToSnapshotShapes */ /** * Whether two numbers numbers a and b are approximately equal. * * @param a - The first point. * @param b - The second point. * @public */ export declare function approximately(a: number, b: number, precision?: number): boolean; /** @public */ export declare class Arc2d extends Geometry2d { private _center; private _radius; private _start; private _end; private _largeArcFlag; private _sweepFlag; private _measure; private _angleStart; private _angleEnd; constructor(config: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & { center: Vec; end: Vec; largeArcFlag: number; start: Vec; sweepFlag: number; }); nearestPoint(point: VecLike): Vec; hitTestLineSegment(A: VecLike, B: VecLike): boolean; getVertices(): Vec[]; getSvgPathData(first?: boolean): string; getLength(): number; } /** * Checks whether two angles are approximately at right-angles or parallel to each other * * @param a - Angle a (radians) * @param b - Angle b (radians) * @returns True iff the angles are approximately at right-angles or parallel to each other * @public */ export declare function areAnglesCompatible(a: number, b: number): boolean; /** * Abstract base class for defining asset-type-specific behavior. * * Each asset type (image, video, bookmark, etc.) has a corresponding AssetUtil that handles * type-specific operations like determining supported MIME types and creating assets from files. * * @public */ export declare abstract class AssetUtil<Asset extends TLAsset = TLAsset> { editor: Editor; /** Configure this asset util's {@link AssetUtil.options | `options`}. */ static configure<T extends TLAssetUtilConstructor<any, any>>(this: T, options: T extends new (...args: any[]) => { options: infer Options; } ? Partial<Options> : never): T; constructor(editor: Editor); /** * Options for this asset util. Override this to provide customization options for your asset. * Use {@link AssetUtil.configure} to customize existing asset utils. */ options: {}; static props?: RecordProps<TLUnknownAsset>; static migrations?: LegacyMigrations | MigrationSequence | TLPropsMigrations; /** * The type of the asset util, which should match the asset's type. */ static type: string; /** * Get the default props for an asset of this type. */ abstract getDefaultProps(): Asset['props']; /** * Get the MIME types that this asset type supports. * Return an empty array if this asset type doesn't support files (e.g. bookmarks). */ getSupportedMimeTypes(): readonly string[]; /** * Check whether this asset type accepts a given MIME type. */ acceptsMimeType(mimeType: string): boolean; /** * Create an asset from a file. Return null if this asset type can't handle the file. */ getAssetFromFile(_file: File, _assetId: TLAssetId): Promise<Asset | null>; } /** @public */ export declare function average(A: VecLike, B: VecLike): string; /** @public */ export declare abstract class BaseBoxShapeTool extends StateNode { static id: string; static initial: string; static children(): TLStateNodeConstructor[]; abstract shapeType: TLBaseBoxShape['type']; onCreate?(_shape: null | TLShape): null | void; } /** @public */ export declare abstract class BaseBoxShapeUtil<Shape extends TLBaseBoxShape> extends ShapeUtil<Shape> { getGeometry(shape: Shape): Geometry2d; onResize(shape: any, info: TLResizeInfo<any>): any; getHandleSnapGeometry(shape: Shape): HandleSnapGeometry; getInterpolatedProps(startShape: Shape, endShape: Shape, t: number): Shape['props']; } /** * A base class for frame-like shapes — containers that clip their children, * require full-brush selection, block erasure from inside, and support * drag-and-drop reparenting. * * Extending this class is the easiest way to create a custom frame-like shape. * It provides sensible defaults for all frame-like behaviors: * * - `isFrameLike()` returns `true` * - `providesBackgroundForChildren()` returns `true` * - `canReceiveNewChildrenOfType()` returns `true` unless the container is locked * - `canRemoveChildrenOfType()` returns `true` unless the container is locked * - `getClipPath()` returns the shape geometry's vertices * - `onDragShapesIn()` reparents shapes into the frame (with index restoration) * - `onDragShapesOut()` reparents shapes back to the page * * All methods can be overridden for custom behavior. * * @example * ```ts * class MyContainerUtil extends BaseFrameLikeShapeUtil<MyContainerShape> { * static override type = 'my-container' as const * static override props = myContainerShapeProps * * override getDefaultProps() { * return { w: 300, h: 200 } * } * * override component(shape: MyContainerShape) { * return <SVGContainer>...</SVGContainer> * } * * override getIndicatorPath(shape: MyContainerShape) { * const path = new Path2D() * path.rect(0, 0, shape.props.w, shape.props.h) * return path * } * } * ``` * * @public */ export declare abstract class BaseFrameLikeShapeUtil<Shape extends TLBaseBoxShape> extends BaseBoxShapeUtil<Shape> { isFrameLike(_shape: Shape): boolean; providesBackgroundForChildren(): boolean; canReceiveNewChildrenOfType(shape: Shape, _type: TLShape['type']): boolean; canRemoveChildrenOfType(shape: Shape, _type: TLShape['type']): boolean; getClipPath(shape: Shape): undefined | Vec[]; onDragShapesIn(shape: Shape, draggingShapes: TLShape[], { initialParentIds, initialIndices }: TLDragShapesInInfo): void; onDragShapesOut(shape: Shape, draggingShapes: TLShape[], info: TLDragShapesOutInfo): void; } /** @public */ export declare interface BatchMeasurementRequest { html: string; opts: TLMeasureTextOpts; } /** * Options passed to {@link BindingUtil.onBeforeChange} and {@link BindingUtil.onAfterChange}, * describing the data associated with a binding being changed. * * @public */ export declare interface BindingOnChangeOptions<Binding extends TLBinding = TLBinding> { /** The binding record before the change is made. */ bindingBefore: Binding; /** The binding record after the change is made. */ bindingAfter: Binding; } /** * Options passed to {@link BindingUtil.onBeforeCreate} and {@link BindingUtil.onAfterCreate}, * describing a the creating a binding. * * @public */ export declare interface BindingOnCreateOptions<Binding extends TLBinding = TLBinding> { /** The binding being created. */ binding: Binding; } /** * Options passed to {@link BindingUtil.onBeforeDelete} and {@link BindingUtil.onAfterDelete}, * describing a binding being deleted. * * @public */ export declare interface BindingOnDeleteOptions<Binding extends TLBinding = TLBinding> { /** The binding being deleted. */ binding: Binding; } /** * Options passed to {@link BindingUtil.onAfterChangeFromShape} and * {@link BindingUtil.onAfterChangeToShape}, describing a bound shape being changed. * * @public */ export declare interface BindingOnShapeChangeOptions<Binding extends TLBinding = TLBinding> { /** The binding record linking these two shapes. */ binding: Binding; /** The shape record before the change is made. */ shapeBefore: TLShape; /** The shape record after the change is made. */ shapeAfter: TLShape; /** * Why did this shape change? * - 'self': the shape itself changed * - 'ancestry': the ancestry of the shape changed, but the shape itself may not have done */ reason: 'ancestry' | 'self'; } /** * Options passed to {@link BindingUtil.onBeforeDeleteFromShape} and * {@link BindingUtil.onBeforeDeleteToShape}, describing a bound shape that is about to be deleted. * * See {@link BindingOnShapeIsolateOptions} for discussion on when to use the delete vs. the isolate * callbacks. * * @public */ export declare interface BindingOnShapeDeleteOptions<Binding extends TLBinding = TLBinding> { /** The binding record that refers to the shape in question. */ binding: Binding; /** The shape that is about to be deleted. */ shape: TLShape; } /** * Options passed to {@link BindingUtil.onBeforeIsolateFromShape} and * {@link BindingUtil.onBeforeIsolateToShape}, describing a shape that is about to be isolated from * the one that it's bound to. * * Isolation happens whenever two bound shapes are separated. For example * 1. One is deleted, but the other is not. * 1. One is copied, but the other is not. * 1. One is duplicated, but the other is not. * * In each of these cases, if the remaining shape depends on the binding for its rendering, it may * now be in an inconsistent state. For example, tldraw's arrow shape depends on the binding to know * where the end of the arrow is. If we removed the binding without doing anything else, the arrow * would suddenly be pointing to the wrong location. Instead, when the shape the arrow is pointing * to is deleted, or the arrow is copied/duplicated, we use an isolation callback. The callback * updates the arrow based on the binding that's about to be removed, so it doesn't end up pointing * to the wrong place. * * For this style of consistency update, use isolation callbacks. For actions specific to deletion * (like deleting a sticker when the shape it's bound to is removed), use the delete callbacks * ({@link BindingUtil.onBeforeDeleteFromShape} and {@link BindingUtil.onBeforeDeleteToShape}) * instead. * * @public */ export declare interface BindingOnShapeIsolateOptions<Binding extends TLBinding = TLBinding> { /** The binding record that refers to the shape in question. */ binding: Binding; /** * The shape being removed. For deletion, this is the deleted shape. For copy/duplicate, this is * the shape that _isn't_ being copied/duplicated and is getting left behind. */ removedShape: TLShape; } /** @public */ export declare abstract class BindingUtil<Binding extends TLBinding = TLBinding> { editor: Editor; constructor(editor: Editor); static props?: RecordProps<TLUnknownBinding>; static migrations?: TLPropsMigrations; /** * The type of the binding util, which should match the binding's type. * * @public */ static type: string; /** * Get the default props for a binding. * * @public */ abstract getDefaultProps(): Partial<Binding['props']>; /** * Called whenever a store operation involving this binding type has completed. This is useful * for working with networks of related bindings that may need to update together. * * @example * ```ts * class MyBindingUtil extends BindingUtil<MyBinding> { * changedBindingIds = new Set<TLBindingId>() * * onOperationComplete() { * doSomethingWithChangedBindings(this.changedBindingIds) * this.changedBindingIds.clear() * } * * onAfterChange({ bindingAfter }: BindingOnChangeOptions<MyBinding>) { * this.changedBindingIds.add(bindingAfter.id) * } * } * ``` * * @public */ onOperationComplete?(): void; /** * Called when a binding is about to be created. See {@link BindingOnCreateOptions} for details. * * You can optionally return a new binding to replace the one being created - for example, to * set different initial props. * * @public */ onBeforeCreate?(options: BindingOnCreateOptions<Binding>): Binding | void; /** * Called after a binding has been created. See {@link BindingOnCreateOptions} for details. * * @public */ onAfterCreate?(options: BindingOnCreateOptions<Binding>): void; /** * Called when a binding is about to be changed. See {@link BindingOnChangeOptions} for details. * * Note that this only fires when the binding record is changing, not when the shapes * associated change. Use {@link BindingUtil.onAfterChangeFromShape} and * {@link BindingUtil.onAfterChangeToShape} for that. * * You can optionally return a new binding to replace the one being changed - for example, to * enforce constraints on the binding's props. * * @public */ onBeforeChange?(options: BindingOnChangeOptions<Binding>): Binding | void; /** * Called after a binding has been changed. See {@link BindingOnChangeOptions} for details. * * Note that this only fires when the binding record is changing, not when the shapes * associated change. Use {@link BindingUtil.onAfterChangeFromShape} and * {@link BindingUtil.onAfterChangeToShape} for that. * * @public */ onAfterChange?(options: BindingOnChangeOptions<Binding>): void; /** * Called when a binding is about to be deleted. See {@link BindingOnDeleteOptions} for details. * * @public */ onBeforeDelete?(options: BindingOnDeleteOptions<Binding>): void; /** * Called after a binding has been deleted. See {@link BindingOnDeleteOptions} for details. * * @public */ onAfterDelete?(options: BindingOnDeleteOptions<Binding>): void; /** * Called after the shape referenced in a binding's `fromId` is changed. Use this to propagate * any changes to the binding itself or the other shape as needed. See * {@link BindingOnShapeChangeOptions} for details. * * @public */ onAfterChangeFromShape?(options: BindingOnShapeChangeOptions<Binding>): void; /** * Called after the shape referenced in a binding's `toId` is changed. Use this to propagate any * changes to the binding itself or the other shape as needed. See * {@link BindingOnShapeChangeOptions} for details. * * @public */ onAfterChangeToShape?(options: BindingOnShapeChangeOptions<Binding>): void; /** * Called before the shape referenced in a binding's `fromId` is about to be deleted. Use this * with care - you may want to use {@link BindingUtil.onBeforeIsolateToShape} instead. See * {@link BindingOnShapeDeleteOptions} for details. * * @public */ onBeforeDeleteFromShape?(options: BindingOnShapeDeleteOptions<Binding>): void; /** * Called before the shape referenced in a binding's `toId` is about to be deleted. Use this * with care - you may want to use {@link BindingUtil.onBeforeIsolateFromShape} instead. See * {@link BindingOnShapeDeleteOptions} for details. * * @public */ onBeforeDeleteToShape?(options: BindingOnShapeDeleteOptions<Binding>): void; /** * Called before the shape referenced in a binding's `fromId` is about to be isolated from the * shape referenced in `toId`. See {@link BindingOnShapeIsolateOptions} for discussion on what * isolation means, and when/how to use this callback. */ onBeforeIsolateFromShape?(options: BindingOnShapeIsolateOptions<Binding>): void; /** * Called before the shape referenced in a binding's `toId` is about to be isolated from the * shape referenced in `fromId`. See {@link BindingOnShapeIsolateOptions} for discussion on what * isolation means, and when/how to use this callback. */ onBeforeIsolateToShape?(options: BindingOnShapeIsolateOptions<Binding>): void; } /** * When moving or resizing shapes, the bounds of the shape can snap to key geometry on other nearby * shapes. Customize how a shape snaps to others with {@link ShapeUtil.getBoundsSnapGeometry}. * * @public */ export declare interface BoundsSnapGeometry { /** * Points that this shape will snap to. By default, this will be the corners and center of the * shapes bounding box. To disable snapping to a specific point, use an empty array. */ points?: VecModel[]; } /** @public */ export declare interface BoundsSnapPoint { id: string; x: number; y: number; handle?: SelectionCorner; } /** @public */ export declare class BoundsSnaps { readonly manager: SnapManager; readonly editor: Editor; constructor(manager: SnapManager); private getSnapPointsCache; getSnapPoints(shapeId: TLShapeId): BoundsSnapPoint[]; private getSnappablePoints; private getSnappableGapNodes; private getVisibleGaps; snapTranslateShapes({ lockedAxis, initialSelectionPageBounds, initialSelectionSnapPoints, dragDelta }: { dragDelta: Vec; initialSelectionPageBounds: Box; initialSelectionSnapPoints: BoundsSnapPoint[]; lockedAxis: 'x' | 'y' | null; }): SnapData; snapResizeShapes({ initialSelectionPageBounds, dragDelta, handle: originalHandle, isAspectRatioLocked, isResizingFromCenter }: { dragDelta: Vec; handle: SelectionCorner | SelectionEdge; initialSelectionPageBounds: Box; isAspectRatioLocked: boolean; isResizingFromCenter: boolean; }): SnapData; private collectPointSnaps; private collectGapSnaps; private getPointSnapLines; private getGapSnapLines; } /** @public */ export declare class Box { constructor(x?: number, y?: number, w?: number, h?: number); x: number; y: number; w: number; h: number; get point(): Vec; set point(val: Vec); get minX(): number; set minX(n: number); get left(): number; get midX(): number; get maxX(): number; get right(): number; get minY(): number; set minY(n: number); get top(): number; get midY(): number; get maxY(): number; get bottom(): number; get width(): number; set width(n: number); get height(): number; set height(n: number); get aspectRatio(): number; get center(): Vec; set center(v: Vec); get corners(): Vec[]; get cornersAndCenter(): Vec[]; get sides(): Array<[Vec, Vec]>; get size(): Vec; isValid(): boolean; toFixed(): this; setTo(B: Box): this; set(x?: number, y?: number, w?: number, h?: number): this; expand(A: Box): this; expandBy(n: number): this; scale(n: number): this; clone(): Box; translate(delta: VecLike): this; snapToGrid(size: number): void; collides(B: Box): boolean; contains(B: Box): boolean; includes(B: Box): boolean; containsPoint(V: VecLike, margin?: number): boolean; getHandlePoint(handle: SelectionCorner | SelectionEdge): Vec; toJson(): BoxModel; resize(handle: SelectionCorner | SelectionEdge | string, dx: number, dy: number): void; union(box: BoxModel): this; static From(box: BoxModel): Box; static FromCenter(center: VecLike, size: VecLike): Box; static FromPoints(points: VecLike[]): Box; static Expand(A: Box, B: Box): Box; static ExpandBy(A: Box, n: number): Box; static Collides(A: Box, B: Box): boolean; static Contains(A: Box, B: Box): boolean; static ContainsApproximately(A: Box, B: Box, precision?: number): boolean; static Includes(A: Box, B: Box): boolean; static ContainsPoint(A: Box, B: VecLike, margin?: number): boolean; static Common(boxes: Box[]): Box; static Sides(A: Box, inset?: number): Vec[][]; static Resize(box: Box, handle: SelectionCorner | SelectionEdge | string, dx: number, dy: number, isAspectRatioLocked?: boolean): { box: Box; scaleX: number; scaleY: number; }; equals(other: Box | BoxModel): boolean; static Equals(a: Box | BoxModel, b: Box | BoxModel): boolean; zeroFix(): this; static ZeroFix(other: Box | BoxModel): Box; } /** @public */ export declare type BoxLike = Box | BoxModel; /** * @param a - Any angle in radians * @returns A number between 0 and 2 * PI * @public */ export declare function canonicalizeRotation(a: number): number; /* Excluded from this release type: CanvasMaxSize */ /** * Get the center of a circle from three points. * * @param a - The first point * @param b - The second point * @param c - The third point * * @returns The center of the circle or null if the points are collinear * * @public */ export declare function centerOfCircleFromThreePoints(a: VecLike, b: VecLike, c: VecLike): null | Vec; /** @public */ export declare class Circle2d extends Geometry2d { config: Omit<Geometry2dOptions, 'isClosed'> & { isFilled: boolean; radius: number; x?: number; y?: number; }; private _center; private _radius; private _x; private _y; constructor(config: Omit<Geometry2dOptions, 'isClosed'> & { isFilled: boolean; radius: number; x?: number; y?: number; }); getBounds(): Box; getVertices(): Vec[]; nearestPoint(point: VecLike): Vec; distanceToPoint(point: VecLike, hitInside?: boolean): number; hitTestPoint(point: VecLike, margin?: number, hitInside?: boolean): boolean; hitTestLineSegment(A: VecLike, B: VecLike, distance?: number): boolean; getSvgPathData(): string; } /** * Clamp a value into a range. * * @example * * ```ts * const A = clamp(0, 1) // 1 * ``` * * @param n - The number to clamp. * @param min - The minimum value. * @public */ export declare function clamp(n: number, min: number): number; /** * Clamp a value into a range. * * @example * * ```ts * const A = clamp(0, 1, 10) // 1 * const B = clamp(11, 1, 10) // 10 * const C = clamp(5, 1, 10) // 5 * ``` * * @param n - The number to clamp. * @param min - The minimum value. * @param max - The maximum value. * @public */ export declare function clamp(n: number, min: number, max: number): number; /** * Clamp radians within 0 and 2PI * * @param r - The radian value. * @public */ export declare function clampRadians(r: number): number; /* Excluded from this release type: clampToBrowserMaxCanvasSize */ /** @public */ export declare class ClickManager { editor: Editor; constructor(editor: Editor); private _clickId; private _clickTimeout?; private _clickScreenPoint?; private _previousScreenPoint?; _getClickTimeout(state: TLClickState, id?: string): void; /* Excluded from this release type: _clickState */ /** * The current click state. * * @public */ get clickState(): TLClickState | undefined; lastPointerInfo: TLPointerEventInfo; handlePointerEvent(info: TLPointerEventInfo): TLClickEventInfo | TLPointerEventInfo; /* Excluded from this release type: cancelDoubleClickTimeout */ } /** * Get the clockwise angle distance between two angles. * * @param a0 - The first angle. * @param a1 - The second angle. * @public */ export declare function clockwiseAngleDist(a0: number, a1: number): number; /** * Tracks remote peers and exposes the collaborator-related queries used by the * editor and its overlays. Encapsulates the visibility clock that periodically * re-evaluates which collaborators should be visible based on activity. * * Accessed via {@link Editor.collaborators}. * * @public */ export declare class CollaboratorsManager { private readonly editor; constructor(editor: Editor); /** * Drives reactive re-evaluation of {@link CollaboratorsManager.getVisibleCollaborators}. * Ticked on a fixed interval so callers don't need to manage their own activity timers. */ private readonly _visibilityClock; private _getCollaboratorsQuery; /** * Returns a list of presence records for all peer collaborators. * This will return the latest presence record for each connected user. */ getCollaborators(): TLInstancePresence[]; /** * Returns a list of presence records for all peer collaborators on the current page. * This will return the latest presence record for each connected user. */ getCollaboratorsOnCurrentPage(): TLInstancePresence[]; /** * Returns a list of presence records for peer collaborators who should currently be * shown in the UI. Filters {@link CollaboratorsManager.getCollaborators} by activity * state (active / idle / inactive) and visibility rules such as following and * highlighted users. Re-evaluates on the visibility clock, so callers don't need to * drive their own activity timer. */ getVisibleCollaborators(): TLInstancePresence[]; /** * Returns a list of presence records for peer collaborators who should currently be * shown in the UI, filtered to those on the current page. */ getVisibleCollaboratorsOnCurrentPage(): TLInstancePresence[]; } /** * @public * @react */ export declare function ContainerProvider({ container, children }: ContainerProviderProps): JSX.Element; /** @public */ export declare interface ContainerProviderProps { container: HTMLElement; children: React.ReactNode; } /** @public */ export declare const coreShapes: readonly [typeof GroupShapeUtil]; /** * Get the counter-clockwise angle distance between two angles. * * @param a0 - The first angle. * @param a1 - The second angle. * @public */ export declare function counterClockwiseAngleDist(a0: number, a1: number): number; /** @public */ export declare function createDebugValue<T>(name: string, { defaults, shouldStoreForSession }: { defaults: DebugFlagDefaults<T>; shouldStoreForSession?: boolean; }): DebugFlag<T>; /** * Converts a deep link descriptor to a url-safe string * * @example * ```ts * const url = `https://example.com?d=${createDeepLinkString({ type: 'shapes', shapeIds: ['shape:1', 'shape:2'] })}` * navigator.clipboard.writeText(url) * ``` * * @param deepLink - the deep link descriptor * @returns a url-safe string * * @public */ export declare function createDeepLinkString(deepLink: TLDeepLink): string; /** * Creates a signal of the instance state for a given store. * @public * @param store - The store to create the instance state snapshot signal for * @returns */ export declare function createSessionStateSnapshotSignal(store: TLStore): Signal<null | TLSessionStateSnapshot>; /** @public */ export declare function createTLCurrentUser(opts?: { setUserPreferences?: ((userPreferences: TLUserPreferences) => void) | undefined; userPreferences?: Signal<TLUserPreferences, unknown> | undefined; }): TLCurrentUser; /** * A helper for creating a TLStore schema from either an object with shapeUtils, bindingUtils, and * migrations, or a schema. * * @param opts - Options for creating the schema. * * @public */ export declare function createTLSchemaFromUtils(opts: TLStoreSchemaOptions): StoreSchema<TLRecord, TLStoreProps>; /** * A helper for creating a TLStore. * * @param opts - Options for creating the store. * * @public */ export declare function createTLStore({ initialData, defaultName, id, assets, users, onMount, collaboration, themes, ...rest }?: TLStoreOptions): TLStore; /** @public */ export declare class CubicBezier2d extends Polyline2d { private _a; private _b; private _c; private _d; private _resolution; constructor(config: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & { cp1: Vec; cp2: Vec; end: Vec; resolution?: number; start: Vec; }); getVertices(): Vec[]; nearestPoint(A: VecLike): Vec; distanceToPoint(point: VecLike, _hitInside?: boolean): number; getSvgPathData(first?: boolean): string; static GetAtT(segment: CubicBezier2d, t: number): Vec; getLength(_filters?: Geometry2dFilters, precision?: number): number; } /** @public */ export declare class CubicSpline2d extends Geometry2d { private _points; constructor(config: Omit<Geometry2dOptions, 'isClosed' | 'isFilled'> & { points: Vec[]; }); private _segments?; get segments(): CubicBezier2d[]; getLength(): number; getVertices(): Vec[]; nearestPoint(A: VecLike): Vec; distanceToPoint(point: VecLike, _hitInside?: boolean): number; hitTestLineSegment(A: VecLike, B: VecLike): boolean; getSvgPathData(): string; } /** * Converts a data URL to a file. * @param url - The data URL to convert. * @param filename - The name of the file. * @param mimeType - The MIME type of the file. * @returns A promise that resolves to a file. * @public */ export declare function dataUrlToFile(url: string, filename: string, mimeType: string): Promise<File>; /** @public */ export declare interface DebugFlag<T> extends DebugFlagDef<T>, Atom<T> { reset(): void; } /** @public */ export declare interface DebugFlagDef<T> { name: string; defaults: DebugFlagDefaults<T>; shouldStoreForSession: boolean; } /** @public */ export declare interface DebugFlagDefaults<T> { development?: T; staging?: T; production?: T; all: T; } /* Excluded from this release type: debugFlags */ /* Excluded from this release type: DEFAULT_ANIMATION_OPTIONS */ /* Excluded from this release type: DEFAULT_CAMERA_OPTIONS */ /** * The default theme definition containing color palettes for both light and dark modes. * * @public */ export declare const DEFAULT_THEME: TLTheme; /** @public @react */ export declare function DefaultBackground(): JSX.Element; /** @public @react */ export declare function DefaultCanvas({ className }: TLCanvasComponentProps): JSX.Element; /** @public @react */ export declare const DefaultErrorFallback: TLErrorFallbackComponent; /** @public @react */ export declare function DefaultGrid({ x, y, z, size }: TLGridProps): JSX.Element; /** @public @react */ export declare function DefaultSelectionBackground({ bounds, rotation }: TLSelectionBackgroundProps): JSX.Element; /** @public @react */ export declare const DefaultShapeWrapper: ForwardRefExoticComponent<TLShapeWrapperProps & RefAttributes<HTMLDivElement>>; /** @public @react */ export declare function DefaultSpinner(props: React.SVGProps<SVGSVGElement>): JSX.Element; /** @public @react */ export declare const DefaultSvgDefs: () => null; /** @public */ export declare const defaultTldrawOptions: { readonly actionShortcutsLocation: "swap"; readonly adjacentShapeMargin: 10; readonly animationMediumMs: 320; readonly camera: TLCameraOptions; readonly cameraMovingTimeoutMs: 64; readonly cameraSlideFriction: 0.09; readonly coarseDragDistanceSquared: 36; readonly coarseHandleRadius: 20; readonly coarsePointerWidth: 12; readonly collaboratorCheckIntervalMs: 1200; readonly collaboratorIdleTimeoutMs: 3000; readonly collaboratorInactiveTimeoutMs: 60000; readonly createTextOnCanvasDoubleClick: true; readonly debouncedZoom: true; readonly debouncedZoomThreshold: 500; readonly deepLinks: undefined; readonly defaultSvgPadding: 32; readonly doubleClickDurationMs: 450; readonly dragDistanceSquared: 16; readonly edgeScrollDelay: 200; readonly edgeScrollDistance: 8; readonly edgeScrollEaseDuration: 200; readonly edgeScrollSpeed: 25; readonly enableToolbarKeyboardShortcuts: true; readonly experimental__onDropOnCanvas: undefined; readonly exportProvider: ExoticComponent<FragmentProps>; readonly flattenImageBoundsExpand: 64; readonly flattenImageBoundsPadding: 16; readonly followChaseViewportSnap: 2; readonly gridSteps: readonly [{ readonly mid: 0.15; readonly min: -1; readonly step: 64; }, { readonly mid: 0.375; readonly min: 0.05; readonly step: 16; }, { readonly mid: 1; readonly min: 0.15; readonly step: 4; }, { readonly mid: 2.5; readonly min: 0.7; readonly step: 1; }]; readonly handleRadius: 12; readonly hitTestMargin: 8; readonly laserDelayMs: 1200; readonly laserFadeoutMs: 500; readonly longPressDurationMs: 500; readonly maxExportDelayMs: 5000; readonly maxFilesAtOnce: 100; readonly maxFontsToLoadBeforeRender: number; readonly maxPages: 40; readonly maxShapesPerPage: 4000; readonly multiClickDurationMs: 200; readonly nonce: undefined; readonly onBeforeCopyToClipboard: undefined; readonly onBeforePasteFromClipboard: undefined; readonly onClipboardPasteRaw: undefined; readonly quickZoomPreservesScreenBounds: true; readonly rightClickPanning: true; readonly snapThreshold: 8; readonly spacebarPanning: true; readonly temporaryAssetPreviewLifetimeMs: 180000; readonly text: {}; readonly textShadowLod: 0.35; readonly tooltipDelayMs: 700; readonly uiCoarseDragDistanceSquared: 625; readonly uiDragDistanceSquared: 16; readonly zoomToFitPadding: 128; }; /** @public */ export declare const defaultUserPreferences: Readonly<{ animationSpeed: 0 | 1; areKeyboardShortcutsEnabled: true; color: "#02B1CC" | "#11B3A3" | "#39B178" | "#55B467" | "#7B66DC" | "#9D5BD2" | "#BD54C6" | "#E34BA9" | "#EC5E41" | "#F04F88" | "#F2555A" | "#FF802B"; colorScheme: "light"; edgeScrollSpeed: 1; enhancedA11yMode: false; inputMode: null; isDynamicSizeMode: false; isPasteAtCursorMode: false; isSnapMode: false; isWrapMode: false; isZoomDirectionInverted: false; locale: "ar" | "bn" | "ca" | "cs" | "da" | "de" | "el" | "en" | "es" | "fa" | "fi" | "fr" | "gl" | "gu-in" | "he" | "hi-in" | "hr" | "hu" | "id" | "it" | "ja" | "km-kh" | "kn" | "ko-kr" | "ml" | "mr" | "ms" | "ne" | "nl" | "no" | "pa" | "pl" | "pt-br" | "pt-pt" | "ro" | "ru" | "sl" | "so" | "sv" | "ta" | "te" | "th" | "tl" | "tr" | "uk" | "ur" | "vi" | "zh-cn" | "zh-tw"; name: ""; }>; /** @public */ export declare const defaultUserStore: TLUserStore; /** * Convert degrees to radians. * * @param d - The degree in degrees. * @public */ export declare function degreesToRadians(d: number): number; /** @public */ export declare const EASINGS: { readonly easeInCubic: (t: number) => number; readonly easeInExpo: (t: number) => number; readonly easeInOutCubic: (t: number) => number; readonly easeInOutExpo: (t: number) => number; readonly easeInOutQuad: (t: number) => number; readonly easeInOutQuart: (t: number) => number; readonly easeInOutQuint: (t: number) => number; readonly easeInOutSine: (t: number) => number; readonly easeInQuad: (t: number) => number; readonly easeInQuart: (t: number) => number; readonly easeInQuint: (t: number) => number; readonly easeInSine: (t: number) => number; readonly easeOutCubic: (t: number) => number; readonly easeOutExpo: (t: number) => number; readonly easeOutQuad: (t: number) => number; readonly easeOutQuart: (t: number) => number; readonly easeOutQuint: (t: number) => number; readonly easeOutSine: (t: number) => number; readonly linear: (t: number) => number; }; /** @public */ export declare class Edge2d extends Geometry2d { private _start; private _end; private _dx; private _dy; private _len2; constructor(config: { end: Vec; start: Vec; }); getLength(): number; getVertices(): Vec[]; nearestPoint(point: VecLike): Vec; distanceToPoint(point: VecLike, _hitInside?: boolean): number; getSvgPathData(first?: boolean): string; } /** @public */ export declare class EdgeScrollManager { editor: Editor; constructor(editor: Editor); private _isEdgeScrolling; private _edgeScrollDuration; getIsEdgeScrolling(): boolean; /** * Update the camera position when the mouse is close to the edge of the screen. * Run this on every tick when in a state where edge scrolling is enabled. * * @public */ updateEdgeScrolling(elapsed: number): void; /* Excluded from this release type: getEdgeProximityFactors */ private getEdgeScroll; /** * Moves the camera when the mouse is close to the edge of the screen. * @public */ private moveCameraWhenCloseToEdge; } /** @public */ export declare class Editor extends EventEmitter<TLEventMap> { readonly id: string; constructor({ store, user, shapeUtils, bindingUtils, assetUtils: assetUtilConstructors, overlayUtils: overlayUtilConstructors, tools, getContainer, cameraOptions, initialState, autoFocus, options: _options, textOptions: _textOptions, getShapeVisibility, colorScheme, fontAssetUrls, themes, initialTheme }: TLEditorOptions); private readonly _getShapeVisibility?; private getIsShapeHiddenCache; isShapeHidden(shapeOrId: TLShape | TLShapeId): boolean; readonly options: TldrawOptions; readonly contextId: string; /** * The editor's store * * @public */ readonly store: TLStore; /** * The root state of the statechart. * * @public */ readonly root: StateNode; /** * Set a tool. Useful if you need to add a tool to the state chart on demand, * after the editor has already been initialized. * * @param Tool - The tool to set. * @param parent - The parent state node to set the tool on. * * @public */ setTool(Tool: TLStateNodeConstructor, parent?: StateNode): void; /** * Remove a tool. Useful if you need to remove a tool from the state chart on demand, * after the editor has already been initialized. * * @param Tool - The tool to delete. * @param parent - The parent state node to remove the tool from. * * @public */ removeTool(Tool: TLStateNodeConstructor, parent?: StateNode): void; /** * A set of functions to call when the editor is disposed. * * @public */ readonly disposables: Set<() => void>; /** * Whether the editor is disposed. * * @public */ isDisposed: boolean; /* Excluded from this release type: _tickManager */ /** * A manager for the editor's input state. * * @public */ readonly inputs: InputsManager; /** * A manager for the editor's snapping feature. * * @public */ readonly snaps: SnapManager; /** * A manager for performance measurement hooks. * * @public */ readonly performance: PerformanceManager; /* Excluded from this release type: _spatialIndex */ /** * A manager for the any asynchronous events and making sure they're * cleaned up upon disposal. * * @public */ readonly timers: { dispose: () => void; requestAnimationFrame: (callback: FrameRequestCallback) => number; setInterval: (handler: TimerHandler, timeout?: number | undefined, ...args: any[]) => number; setTimeout: (handler: TimerHandler, timeout?: number | undefined, ...args: any[]) => number; }; /** * A manager for remote peer collaborators connected to this editor. * * @public */ readonly collaborators: CollaboratorsManager; /** * A manager for the user and their preferences. * * @public */ readonly user: UserPreferencesManager; /* Excluded from this release type: _themeManager */ /** * A helper for measuring text. * * @public */ readonly textMeasure: TextManager; /** * A utility for managing the set of fonts that should be rendered in the document. * * @public */ readonly fonts: FontManager; /** * A manager for the editor's scribbles. * * @public */ readonly scribbles: ScribbleManager; /** * A manager for canvas overlay UI elements (selection handles, shape handles, etc.). * * @public */ readonly overlays: OverlayManager; /** * A manager for side effects and correct state enforcement. See {@link @tldraw/store#StoreSideEffects} for details. * * @public */ readonly sideEffects: StoreSideEffects<TLRecord>; /** * A manager for moving the camera when the mouse is at the edge of the screen. * * @public */ edgeScrollManager: EdgeScrollManager; /* Excluded from this release type: focusManager */ /** * The current HTML element containing the editor. * * @example * ```ts * const container = editor.getContainer() * ``` * * @public */ getContainer: () => HTMLElement; /* Excluded from this release type: getContainerDocument */ /* Excluded from this release type: getContainerWindow */ /** * Dispose the editor. * * @public */ dispose(): void; /** * Get the current color mode (`'light'` or `'dark'`), based on the user's dark mode preference. * * @public */ getColorMode(): 'dark' | 'light'; /** * Set the color mode. Note that this is a convenience method that passes the mode to * `user.updateUserPreferences`, which is the source of truth for the user's color mode preference. * * @public */ setColorMode(mode: 'dark' | 'light'): this; /** * Get the id of the current theme. * * @public */ getCurrentThemeId(): TLThemeId; /** * Get the current theme definition. * * @public */ getCurrentTheme(): TLTheme; /** * Set the current theme by id. * * @public */ setCurrentTheme(id: TLThemeId): this; /** * Get all registered theme definitions. * * @public */ getThemes(): TLThemes; /** * Get a single theme definition by id. * * @public */ getTheme(id: TLThemeId): TLTheme | undefined; /** * Replace all theme definitions, or update them via a callback that receives a deep copy. * The `'default'` theme must always be present in the result. * * @example * ```ts * // Replace all themes * editor.updateThemes({ default: myDefaultTheme, ocean: myOceanTheme }) * * // Update via callback * editor.updateThemes((themes) => { * delete themes.ocean * return themes * }) * ``` * * @public */ updateThemes(themes: ((themes: TLThemes) => TLThemes) | TLThemes): this; /** * Register or update a single theme definition. The theme is keyed by its `id` property. * * @example * ```ts * // Override a property on the default theme * editor.updateTheme({ ...editor.getTheme('default')!, fontSize: 24 }) * * // Register a new theme * editor.updateTheme({ id: 'ocean', ...myOceanTheme }) * ``` * * @public */ updateTheme(theme: TLTheme): this; /** * A map of shape utility classes (TLShapeUtils) by shape type. * * @public */ shapeUtils: { readonly [K in string]?: ShapeUtil<TLShape>; }; /* Excluded from this release type: _shapeUtilsByAssetType */ styleProps: { [key: string]: Map<StyleProp<any>, string>; }; /** * Get a shape util from a shape itself. * * @example * ```ts * const util = editor.getShapeUtil(myArrowShape) * const util = editor.getShapeUtil('arrow') * const util = editor.getShapeUtil<TLArrowShape>(myArrowShape) * const util = editor.getShapeUtil(TLArrowShape)('arrow') * ``` * * @param shape - A shape, shape partial, or shape type. * * @public */ getShapeUtil<K extends TLShape['type']>(type: K): ShapeUtil<Extract<TLShape, { type: K; }>>; getShapeUtil<S extends TLShape>(shape: S | S['type'] | TLShapePartial<S>): ShapeUtil<S>; getShapeUtil<T extends ShapeUtil>(type: T extends ShapeUtil<infer R> ? R['type'] : string): T; /** * Returns true if the editor has a shape util for the given shape / shape type. * * @param shape - A shape, shape partial, or shape type. */ hasShapeUtil(shape: TLShape | TLShapePartial<TLShape>): boolean; hasShapeUtil(type: TLShape['type']): boolean; hasShapeUtil<T extends ShapeUtil>(type: T extends ShapeUtil<infer R> ? R['type'] : string): boolean; /** * Get the shape util that handles the given asset type. * Returns the shape util whose {@link ShapeUtil.handledAssetTypes} includes * the given asset type, or undefined if none matches. * * @param assetType - The asset type string. * @public */ getShapeUtilForAssetType(assetType: string): ShapeUtil | undefined; /** * A map of shape utility classes (TLShapeUtils) by shape type. * * @public */ bindingUtils: { readonly [K in string]?: BindingUtil<TLBinding>; }; /** * Get a binding util from a binding itself. * * @example * ```ts * const util = editor.getBindingUtil(myArrowBinding) * const util = editor.getBindingUtil('arrow') * const util = editor.getBindingUtil<TLArrowBinding>(myArrowBinding)