import { Editor } from 'tldraw'; import { TLAssetStore } from 'tldraw'; import { TLObjectStoreAccess } from '@tldraw/sync-core'; import { TLPersistentClientSocket } from '@tldraw/sync-core'; import { TLPresenceStateInfo } from 'tldraw'; import { TLStore } from 'tldraw'; import { TLStoreSchemaOptions } from 'tldraw'; import { TLStoreWithStatus } from 'tldraw'; import { TLThemes } from 'tldraw'; import { TLUser } from 'tldraw'; import { TLUserStore } from 'tldraw'; /** * A store wrapper specifically for remote collaboration that excludes local-only states. * This type represents a tldraw store that is synchronized with a remote multiplayer server. * * Unlike the base TLStoreWithStatus, this excludes 'synced-local' and 'not-synced' states * since remote stores are always either loading, connected to a server, or in an error state. * * @example * ```tsx * function MyCollaborativeApp() { * const store: RemoteTLStoreWithStatus = useSync({ * uri: 'wss://myserver.com/sync/room-123', * assets: myAssetStore * }) * * if (store.status === 'loading') { * return
Connecting to multiplayer session...
* } * * if (store.status === 'error') { * return
Connection failed: {store.error.message}
* } * * // store.status === 'synced-remote' * return * } * ``` * * @public */ export declare type RemoteTLStoreWithStatus = (Extract & { /** * Write access for object-store lane record types (e.g. comments), as granted by the * server for this session. Independent of the canvas read-only state, so a session can * be allowed to comment without being allowed to edit. Defaults to `'write'`. */ readonly objectAccess: TLObjectStoreAccess; }) | Exclude; /** * Creates a reactive store synchronized with a multiplayer server for real-time collaboration. * * This hook manages the complete lifecycle of a collaborative tldraw session, including * WebSocket connection establishment, state synchronization, user presence, and error handling. * The returned store can be passed directly to the Tldraw component to enable multiplayer features. * * The store progresses through multiple states: * - `loading`: Establishing connection and synchronizing initial state * - `synced-remote`: Successfully connected and actively synchronizing changes * - `error`: Connection failed or synchronization error occurred * * For optimal performance with media assets, provide an `assets` store that implements * external blob storage. Without this, large images and videos will be stored inline * as base64, causing performance issues during serialization. * * @param opts - Configuration options for multiplayer synchronization * - `uri` - WebSocket server URI (string or async function returning URI) * - `assets` - Asset store for blob storage (required for production use) * - `users` - User store for identity, presence and attribution * - `getUserPresence` - Optional function to customize presence data * - `onCustomMessageReceived` - Handler for custom socket messages * - `roomId` - Room identifier for analytics (internal use) * - `onMount` - Callback when editor mounts (internal use) * - `trackAnalyticsEvent` - Analytics event tracker (internal use) * * @returns A reactive store wrapper with connection status and synchronized store * * @example * ```tsx * // Basic multiplayer setup * function CollaborativeApp() { * const store = useSync({ * uri: 'wss://myserver.com/sync/room-123', * assets: myAssetStore, * users: { * currentUser: computed('current-user', () => ({ * id: createUserId('user-1'), * name: 'Alice', * color: '#ff0000', * meta: {}, * })), * } * }) * * if (store.status === 'loading') { * return
Connecting to collaboration session...
* } * * if (store.status === 'error') { * return
Failed to connect: {store.error.message}
* } * * return * } * ``` * * @example * ```tsx * // Dynamic authentication with user store * function AuthenticatedApp() { * const store = useSync({ * uri: async () => { * const token = await getAuthToken() * return `wss://myserver.com/sync/room-123?token=${token}` * }, * assets: authenticatedAssetStore, * users: myUserStore, * getUserPresence: (store, user) => { * return { * userId: user.id, * userName: user.name, * cursor: getCurrentCursor(store) * } * } * }) * * return * } * ``` * * @public */ export declare function useSync(opts: UseSyncOptions & TLStoreSchemaOptions): RemoteTLStoreWithStatus; /** @public */ export declare type UseSyncConnectFn = (query: { sessionId: string; storeId: string; }) => TLPersistentClientSocket; /** * Creates a tldraw store synced with a multiplayer room hosted on tldraw's demo server `https://demo.tldraw.xyz`. * * The store can be passed directly into the `` component to enable multiplayer features. * It will handle loading states, and enable multiplayer UX like user cursors and following. * * All data on the demo server is * * - Deleted after a day or so. * - Publicly accessible to anyone who knows the room ID. Use your company name as a prefix to help avoid collisions, or generate UUIDs for maximum privacy. * * @example * ```tsx * function MyApp() { * const store = useSyncDemo({roomId: 'my-app-test-room'}) * return * } * ``` * * @param options - Options for the multiplayer demo sync store. See {@link UseSyncDemoOptions} and {@link @tldraw/editor#TLStoreSchemaOptions}. * * @public */ export declare function useSyncDemo(options: UseSyncDemoOptions & TLStoreSchemaOptions): RemoteTLStoreWithStatus; /** @public */ export declare interface UseSyncDemoOptions { /** * The room ID to sync with. Make sure the room ID is unique. The namespace is shared by * everyone using the demo server. Consider prefixing it with your company or project name. */ roomId: string; /** * User store for identity, presence and attribution. * If not provided, a default implementation based on localStorage will be used. */ users?: TLUserStore; /* Excluded from this release type: host */ /** * {@inheritdoc UseSyncOptions.getUserPresence} * @public */ getUserPresence?(store: TLStore, user: TLUser): null | TLPresenceStateInfo; } /** * Options for the {@link useSync} hook. * @public */ export declare type UseSyncOptions = UseSyncOptionsWithConnectFn | UseSyncOptionsWithUri; /** * Configuration options for the {@link useSync} hook to establish multiplayer collaboration. * * This interface defines the required and optional settings for connecting to a multiplayer * server, managing user presence, handling assets, and customizing the collaboration experience. * * @example * ```tsx * const syncOptions: UseSyncOptions = { * uri: 'wss://myserver.com/sync/room-123', * assets: myAssetStore, * users: { * currentUser: myCurrentUserSignal, * }, * getUserPresence: (store, user) => ({ * userId: user.id, * userName: user.name, * cursor: getCursorPosition() * }) * } * ``` * * @public */ export declare interface UseSyncOptionsBase { /** * Named theme definitions. When provided, custom color names are automatically * registered before the store is constructed so persisted data with those * colors passes validation on load. */ themes?: Partial; /** * Asset store implementation for handling file uploads and storage. * * Required for production applications to handle images, videos, and other * media efficiently. Without an asset store, files are stored inline as * base64, which causes performance issues with large files. * * The asset store must implement upload (for new files) and resolve * (for displaying existing files) methods. For prototyping, you can use * {@link @tldraw/editor#inlineBase64AssetStore} but this is not recommended for production. * * @example * ```ts * const myAssetStore: TLAssetStore = { * upload: async (asset, file) => { * const url = await uploadToCloudStorage(file) * return { src: url } * }, * resolve: (asset, context) => { * return getOptimizedUrl(asset.src, context) * } * } * ``` */ assets: TLAssetStore; /** * User store for identity, presence and attribution. * * Both methods return reactive {@link @tldraw/state#Signal | Signals}. * `currentUser` provides the current user's identity (used for * both presence broadcasting and shape attribution) and optionally * `resolve(userId)` looks up other users by ID. If not provided, * a default implementation backed by localStorage user preferences is * used, with `resolve` falling back to presence records in the store. */ users?: TLUserStore; /** * Handler for receiving custom messages sent through the multiplayer connection. * * Use this to implement custom communication channels between clients beyond * the standard shape and presence synchronization. Messages are sent using * the TLSyncClient's sendMessage method. * * @param data - The custom message data received from another client * * @example * ```ts * onCustomMessageReceived: (data) => { * if (data.type === 'chat') { * displayChatMessage(data.message, data.userId) * } * } * ``` */ onCustomMessageReceived?(data: any): void; /* Excluded from this release type: onMount */ /* Excluded from this release type: roomId */ /* Excluded from this release type: trackAnalyticsEvent */ /** * A reactive function that returns a {@link @tldraw/tlschema#TLInstancePresence} object. * * This function is called reactively whenever the store state changes and * determines what presence information to broadcast to other clients. The * result is synchronized across all connected clients for displaying cursors, * selections, and other collaborative indicators. * * If not provided, uses the default implementation which includes standard * cursor position and selection state. Custom implementations allow you to * add additional presence data like current tool, view state, or custom status. * * See {@link @tldraw/tlschema#getDefaultUserPresence} for * the default implementation of this function. * * @param store - The current TLStore * @param user - The current user information * @returns Presence state to broadcast to other clients, or null to hide presence * * @example * ```ts * getUserPresence: (store, user) => { * return { * userId: user.id, * userName: user.name, * cursor: { x: 100, y: 200 }, * currentTool: 'select', * isActive: true * } * } * ``` */ getUserPresence?(store: TLStore, user: TLUser): null | TLPresenceStateInfo; } /** @public */ export declare interface UseSyncOptionsWithConnectFn extends UseSyncOptionsBase { /** * Create a connection to the server. Mostly you should use {@link UseSyncOptionsWithUri.uri} * instead, but this is useful if you want to use a custom transport to connect to the server, * instead of our default websocket-based transport. */ connect: UseSyncConnectFn; uri?: never; } /** @public */ export declare interface UseSyncOptionsWithUri extends UseSyncOptionsBase { /** * The WebSocket URI of the multiplayer server for real-time synchronization. * * Must include the protocol (wss:// for secure, ws:// for local development). * HTTP/HTTPS URLs will be automatically upgraded to WebSocket connections. * * Can be a static string or a function that returns a URI (useful for dynamic * authentication tokens or room routing). The function is called on each * connection attempt, allowing for token refresh and dynamic routing. * * Reserved query parameters `sessionId` and `storeId` are automatically added * by the sync system and should not be included in your URI. * * @example * ```ts * // Static URI * uri: 'wss://myserver.com/sync/room-123' * * // Dynamic URI with authentication * uri: async () => { * const token = await getAuthToken() * return `wss://myserver.com/sync/room-123?token=${token}` * } * ``` */ uri: (() => Promise | string) | string; connect?: never; } export * from "@tldraw/sync-core"; export { }