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 { }