@cloudsignal/collaborate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROMPT.md +128 -0
- package/README.md +192 -0
- package/dist/index.cjs +1182 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +409 -0
- package/dist/index.d.ts +409 -0
- package/dist/index.js +1153 -0
- package/dist/index.js.map +1 -0
- package/llms.txt +45 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context/SpaceContext.ts","../src/utils/colors.ts","../src/constants.ts","../src/context/SpaceProvider.tsx","../src/hooks/useSpace.ts","../src/hooks/usePresence.ts","../src/hooks/useCursors.ts","../src/hooks/useLock.ts","../src/hooks/useTypingIndicator.ts","../src/utils/uid.ts","../src/hooks/useReactions.ts","../src/hooks/useBroadcast.ts","../src/utils/lww.ts","../src/hooks/useSharedState.ts","../src/components/AvatarStack.tsx","../src/components/CursorOverlay.tsx","../src/components/TypingIndicator.tsx","../src/components/LockIndicator.tsx","../src/components/ReactionBar.tsx","../src/components/PresenceBorder.tsx","../src/index.ts"],"names":["createContext","useState","useRef","useEffect","useMemo","useCallback","CloudSignalClient","useContext","user","jsx","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["'use client'\n\nimport { createContext } from 'react'\nimport type { SpaceContextValue } from '../types'\n\nexport const SpaceContext = createContext<SpaceContextValue | null>(null)\n","const CURSOR_COLORS = [\n '#3B82F6', // Blue\n '#EF4444', // Red\n '#22C55E', // Green\n '#A855F7', // Purple\n '#F97316', // Orange\n '#EC4899', // Pink\n '#14B8A6', // Teal\n '#EAB308', // Yellow\n '#6366F1', // Indigo\n '#F43F5E', // Rose\n] as const\n\n/**\n * Deterministic color assignment based on userId.\n * Same user always gets the same color across sessions.\n */\nexport function getColorForUser(userId: string): string {\n let hash = 0\n for (let i = 0; i < userId.length; i++) {\n hash = ((hash << 5) - hash + userId.charCodeAt(i)) | 0\n }\n return CURSOR_COLORS[Math.abs(hash) % CURSOR_COLORS.length]\n}\n\n/**\n * Get user initials from display name (for avatars).\n * \"Alice Smith\" → \"AS\", \"bob\" → \"B\"\n */\nexport function getInitials(name: string): string {\n return name\n .split(/\\s+/)\n .map(w => w[0])\n .filter(Boolean)\n .slice(0, 2)\n .join('')\n .toUpperCase()\n}\n","/** MQTT topic prefix for all space collaboration messages */\nexport const TOPIC_PREFIX = '$spaces'\n\n/** Topic segments for each collaboration primitive */\nexport const TOPICS = {\n PRESENCE: 'presence',\n CURSORS: 'cursors',\n LOCKS: 'locks',\n TYPING: 'typing',\n REACTIONS: 'reactions',\n BROADCAST: 'broadcast',\n STATE: 'state',\n} as const\n\n/** Default timing constants */\nexport const DEFAULTS = {\n /** Presence heartbeat interval (ms) */\n PRESENCE_HEARTBEAT_MS: 10_000,\n /** Time before a user is considered offline (ms) */\n PRESENCE_TIMEOUT_MS: 30_000,\n /** Cursor publish throttle (ms) — ~33Hz */\n CURSOR_THROTTLE_MS: 30,\n /** Time before a cursor fades out (ms) */\n CURSOR_STALE_MS: 3_000,\n /** Typing indicator auto-reset (ms) */\n TYPING_TIMEOUT_MS: 3_000,\n /** Minimum interval between typing publishes (ms) */\n TYPING_THROTTLE_MS: 2_000,\n /** Time before a typing user is cleaned from the list (ms) */\n TYPING_STALE_MS: 4_000,\n /** How long a reaction stays visible (ms) */\n REACTION_DURATION_MS: 3_000,\n /** Max visible reactions at once */\n REACTION_MAX_VISIBLE: 20,\n /** Stats sync interval (ms) — for cursor count etc. */\n STATS_TICK_MS: 1_000,\n /** Opacity fade-out sync interval (ms) */\n FADE_TICK_MS: 200,\n} as const\n\n/** Build a full MQTT topic for a space */\nexport function spaceTopic(spaceId: string, segment: string): string {\n return `${TOPIC_PREFIX}/${spaceId}/${segment}`\n}\n\n/** Build the wildcard subscription for a space */\nexport function spaceWildcard(spaceId: string): string {\n return `${TOPIC_PREFIX}/${spaceId}/#`\n}\n","'use client'\n\nimport { useEffect, useRef, useState, useCallback, useMemo } from 'react'\nimport CloudSignalClient from '@cloudsignal/mqtt-client'\nimport { SpaceContext } from './SpaceContext'\nimport { getColorForUser } from '../utils/colors'\nimport { TOPIC_PREFIX, TOPICS, DEFAULTS, spaceWildcard, spaceTopic } from '../constants'\nimport type { SpaceProps, SpaceUser, SpaceContextValue, TopicHandler, PublishOptions } from '../types'\n\n// Type for the CloudSignal client instance\ninterface CSClient {\n connect(config: Record<string, unknown>): Promise<void>\n connectWithToken(config: Record<string, unknown>): Promise<void>\n subscribe(topic: string, qos?: 0 | 1 | 2): Promise<void>\n unsubscribe(topic: string): Promise<void>\n transmit(topic: string, message: string | object, options?: { qos?: 0 | 1 | 2; retain?: boolean }): void\n destroy(): void\n onMessage(handler: (topic: string, message: string) => void): void\n offMessage(handler: (topic: string, message: string) => void): void\n onConnectionStatusChange: ((connected: boolean) => void) | null\n onReconnecting: ((attempt: number) => void) | null\n onAuthError: ((error: Error) => void) | null\n}\n\nexport function Space({\n id,\n connection,\n userName,\n userColor,\n userAvatar,\n userData,\n debug = false,\n presenceHeartbeatMs = DEFAULTS.PRESENCE_HEARTBEAT_MS,\n presenceTimeoutMs = DEFAULTS.PRESENCE_TIMEOUT_MS,\n onConnectionChange,\n children,\n}: SpaceProps) {\n const [isConnected, setIsConnected] = useState(false)\n const [isConnecting, setIsConnecting] = useState(false)\n const [error, setError] = useState<Error | null>(null)\n\n // Refs for StrictMode safety\n const clientRef = useRef<CSClient | null>(null)\n const connectingRef = useRef(false)\n const mountedRef = useRef(true)\n const handlersRef = useRef(new Map<string, Set<TopicHandler>>())\n const messageHandlerRef = useRef<((topic: string, message: string) => void) | null>(null)\n const onConnectionChangeRef = useRef(onConnectionChange)\n\n useEffect(() => {\n onConnectionChangeRef.current = onConnectionChange\n }, [onConnectionChange])\n\n // Build self user\n const resolvedColor = userColor || getColorForUser(userName)\n const self = useMemo<SpaceUser>(() => ({\n userId: connection.username || userName,\n name: userName,\n color: resolvedColor,\n avatar: userAvatar,\n data: userData,\n joinedAt: Date.now(),\n lastSeen: Date.now(),\n }), [connection.username, userName, resolvedColor, userAvatar, userData])\n\n const selfRef = useRef(self)\n useEffect(() => { selfRef.current = self }, [self])\n\n // Logging\n const log = useCallback((...args: unknown[]) => {\n if (debug) console.log('[CloudSignal:Space]', ...args)\n }, [debug])\n\n // Topic handler registry\n const addTopicHandler = useCallback((segment: string, handler: TopicHandler): (() => void) => {\n if (!handlersRef.current.has(segment)) {\n handlersRef.current.set(segment, new Set())\n }\n handlersRef.current.get(segment)!.add(handler)\n return () => {\n handlersRef.current.get(segment)?.delete(handler)\n }\n }, [])\n\n // Message router — parses topic, dispatches to registered handlers\n const routeMessage = useCallback((topic: string, messageStr: string) => {\n const prefix = `${TOPIC_PREFIX}/${id}/`\n if (!topic.startsWith(prefix)) return\n\n const subtopic = topic.slice(prefix.length) // e.g. \"cursors\", \"state/myKey\", \"broadcast/chat\"\n const segment = subtopic.split('/')[0] // first segment is the feature\n\n let payload: unknown\n try {\n payload = JSON.parse(messageStr)\n } catch {\n payload = messageStr\n }\n\n log('←', segment, subtopic)\n\n const handlers = handlersRef.current.get(segment)\n if (handlers) {\n for (const handler of handlers) {\n handler(subtopic, payload)\n }\n }\n }, [id, log])\n\n // Publish helper — auto-prefixes with space topic\n const publish = useCallback((subtopic: string, payload: unknown, options?: PublishOptions) => {\n if (!clientRef.current) {\n log('Cannot publish: not connected')\n return\n }\n const topic = spaceTopic(id, subtopic)\n const message = typeof payload === 'string' ? payload : JSON.stringify(payload)\n clientRef.current.transmit(topic, message, options)\n log('→', subtopic)\n }, [id, log])\n\n // Presence heartbeat\n useEffect(() => {\n if (!isConnected) return\n\n const sendHeartbeat = () => {\n publish(TOPICS.PRESENCE, {\n userId: selfRef.current.userId,\n name: selfRef.current.name,\n color: selfRef.current.color,\n avatar: selfRef.current.avatar,\n data: selfRef.current.data,\n ts: Date.now(),\n })\n }\n\n // Send immediately on connect\n sendHeartbeat()\n\n const interval = setInterval(sendHeartbeat, presenceHeartbeatMs)\n return () => clearInterval(interval)\n }, [isConnected, publish, presenceHeartbeatMs])\n\n // Explicit leave on beforeunload\n useEffect(() => {\n if (!isConnected) return\n\n const handleUnload = () => {\n publish(TOPICS.PRESENCE, {\n userId: selfRef.current.userId,\n type: 'leave',\n ts: Date.now(),\n })\n }\n\n window.addEventListener('beforeunload', handleUnload)\n return () => window.removeEventListener('beforeunload', handleUnload)\n }, [isConnected, publish])\n\n // Connect on mount, disconnect on unmount\n useEffect(() => {\n mountedRef.current = true\n\n const doConnect = async () => {\n if (connectingRef.current || clientRef.current) return\n\n connectingRef.current = true\n setIsConnecting(true)\n setError(null)\n\n try {\n const clientOptions: Record<string, unknown> = {\n debug,\n preset: 'desktop',\n }\n if (connection.tokenServiceUrl) {\n clientOptions.tokenServiceUrl = connection.tokenServiceUrl\n }\n\n const client = new CloudSignalClient(clientOptions) as unknown as CSClient\n\n // Connection status\n client.onConnectionStatusChange = (connected: boolean) => {\n log('Connection:', connected)\n if (mountedRef.current) {\n setIsConnected(connected)\n onConnectionChangeRef.current?.(connected)\n }\n }\n\n client.onReconnecting = (attempt: number) => {\n log('Reconnecting, attempt:', attempt)\n }\n\n client.onAuthError = (err: Error) => {\n log('Auth error:', err.message)\n if (mountedRef.current) {\n setError(err)\n setIsConnected(false)\n }\n clientRef.current = null\n }\n\n // Message routing\n const handler = (topic: string, message: string) => routeMessage(topic, message)\n messageHandlerRef.current = handler\n client.onMessage(handler)\n\n // Build LWT (Last Will and Testament) for auto-leave on disconnect\n const willPayload = JSON.stringify({\n userId: selfRef.current.userId,\n type: 'leave',\n ts: Date.now(),\n })\n\n // Connect with appropriate auth method\n if (connection.secretKey || connection.externalToken) {\n await client.connectWithToken({\n host: connection.host,\n organizationId: connection.organizationId,\n secretKey: connection.secretKey,\n externalToken: connection.externalToken,\n willTopic: spaceTopic(id, TOPICS.PRESENCE),\n willMessage: willPayload,\n willQos: 0,\n })\n } else {\n await client.connect({\n host: connection.host,\n username: connection.username,\n password: connection.password,\n willTopic: spaceTopic(id, TOPICS.PRESENCE),\n willMessage: willPayload,\n willQos: 0,\n })\n }\n\n if (!mountedRef.current) {\n client.destroy()\n return\n }\n\n clientRef.current = client\n\n // Subscribe to all space topics\n await client.subscribe(spaceWildcard(id), 0)\n log('Subscribed to', spaceWildcard(id))\n\n } catch (err) {\n log('Connection failed:', err)\n if (mountedRef.current) {\n setError(err instanceof Error ? err : new Error(String(err)))\n }\n } finally {\n connectingRef.current = false\n if (mountedRef.current) setIsConnecting(false)\n }\n }\n\n doConnect()\n\n return () => {\n mountedRef.current = false\n\n // Publish explicit leave before destroying\n if (clientRef.current) {\n try {\n const leaveTopic = spaceTopic(id, TOPICS.PRESENCE)\n const leavePayload = JSON.stringify({\n userId: selfRef.current.userId,\n type: 'leave',\n ts: Date.now(),\n })\n clientRef.current.transmit(leaveTopic, leavePayload, { qos: 0 })\n } catch {\n // Best-effort leave\n }\n clientRef.current.destroy()\n clientRef.current = null\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [id, connection.host, connection.username, connection.password, connection.secretKey, connection.externalToken, connection.organizationId])\n\n // Context value\n const contextValue = useMemo<SpaceContextValue>(() => ({\n spaceId: id,\n self,\n isConnected,\n isConnecting,\n error,\n presenceTimeoutMs,\n publish,\n addTopicHandler,\n }), [id, self, isConnected, isConnecting, error, presenceTimeoutMs, publish, addTopicHandler])\n\n return (\n <SpaceContext.Provider value={contextValue}>\n {children}\n </SpaceContext.Provider>\n )\n}\n\n// Export alias for explicit naming in AI-generated code\nexport { Space as CloudSignalSpace }\n","'use client'\n\nimport { useContext } from 'react'\nimport { SpaceContext } from '../context/SpaceContext'\nimport type { SpaceContextValue } from '../types'\n\n/**\n * Access the current Space context.\n * Must be used within a `<Space>` provider.\n */\nexport function useSpace(): SpaceContextValue {\n const ctx = useContext(SpaceContext)\n if (!ctx) {\n throw new Error(\n 'useSpace() must be used within a <Space> provider. ' +\n 'Wrap your component tree with <Space id=\"...\" connection={...}>'\n )\n }\n return ctx\n}\n","'use client'\n\nimport { useEffect, useRef, useState, useCallback } from 'react'\nimport { useSpace } from './useSpace'\nimport { TOPICS } from '../constants'\nimport type { SpaceUser, UsePresenceReturn } from '../types'\n\n/**\n * Track who's in the space.\n * Presence heartbeats are managed by the Space provider —\n * this hook only reads and exposes the member list.\n */\nexport function usePresence(): UsePresenceReturn {\n const { self, addTopicHandler, presenceTimeoutMs } = useSpace()\n\n const [members, setMembers] = useState<SpaceUser[]>([self])\n const membersMapRef = useRef(new Map<string, SpaceUser>())\n const joinCallbacksRef = useRef(new Set<(user: SpaceUser) => void>())\n const leaveCallbacksRef = useRef(new Set<(user: SpaceUser) => void>())\n\n // Initialize with self\n useEffect(() => {\n membersMapRef.current.set(self.userId, self)\n setMembers([self])\n }, [self])\n\n // Handle presence messages\n useEffect(() => {\n const unsubscribe = addTopicHandler(TOPICS.PRESENCE, (_subtopic, payload) => {\n if (typeof payload !== 'object' || payload === null) return\n const data = payload as Record<string, unknown>\n const userId = data.userId as string\n if (!userId) return\n\n // Leave message\n if (data.type === 'leave') {\n const user = membersMapRef.current.get(userId)\n if (user) {\n membersMapRef.current.delete(userId)\n setMembers(Array.from(membersMapRef.current.values()))\n for (const cb of leaveCallbacksRef.current) cb(user)\n }\n return\n }\n\n // Heartbeat / join\n const isNew = !membersMapRef.current.has(userId)\n const user: SpaceUser = {\n userId,\n name: (data.name as string) || userId,\n color: (data.color as string) || '#3B82F6',\n avatar: data.avatar as string | undefined,\n data: data.data as Record<string, unknown> | undefined,\n joinedAt: isNew ? Date.now() : (membersMapRef.current.get(userId)?.joinedAt ?? Date.now()),\n lastSeen: Date.now(),\n }\n\n membersMapRef.current.set(userId, user)\n setMembers(Array.from(membersMapRef.current.values()))\n\n if (isNew) {\n for (const cb of joinCallbacksRef.current) cb(user)\n }\n })\n\n return unsubscribe\n }, [addTopicHandler])\n\n // Clean stale members\n useEffect(() => {\n const interval = setInterval(() => {\n const now = Date.now()\n let changed = false\n for (const [userId, user] of membersMapRef.current) {\n if (userId === self.userId) continue // Never remove self\n if (now - user.lastSeen > presenceTimeoutMs) {\n membersMapRef.current.delete(userId)\n changed = true\n for (const cb of leaveCallbacksRef.current) cb(user)\n }\n }\n if (changed) {\n setMembers(Array.from(membersMapRef.current.values()))\n }\n }, 5_000)\n\n return () => clearInterval(interval)\n }, [self.userId, presenceTimeoutMs])\n\n const onJoin = useCallback((callback: (user: SpaceUser) => void) => {\n joinCallbacksRef.current.add(callback)\n return () => { joinCallbacksRef.current.delete(callback) }\n }, [])\n\n const onLeave = useCallback((callback: (user: SpaceUser) => void) => {\n leaveCallbacksRef.current.add(callback)\n return () => { leaveCallbacksRef.current.delete(callback) }\n }, [])\n\n return {\n members,\n count: members.length,\n self,\n onJoin,\n onLeave,\n }\n}\n","'use client'\n\nimport { useEffect, useRef, useState, useCallback } from 'react'\nimport { useSpace } from './useSpace'\nimport { TOPICS, DEFAULTS } from '../constants'\nimport type { CursorData, UseCursorsOptions, UseCursorsReturn } from '../types'\n\n/**\n * Live cursor tracking.\n *\n * Performance: Cursor data is stored in refs (not state) to avoid\n * re-renders on every MQTT message (~33 messages/sec per user).\n * The `onUpdateRef` callback lets CursorOverlay sync DOM imperatively.\n * A 1Hz state snapshot is provided for non-critical display (e.g. online count).\n */\nexport function useCursors(options: UseCursorsOptions = {}): UseCursorsReturn {\n const {\n throttleMs = DEFAULTS.CURSOR_THROTTLE_MS,\n staleMs = DEFAULTS.CURSOR_STALE_MS,\n } = options\n\n const { self, publish, addTopicHandler } = useSpace()\n\n // Ref-based cursor storage — zero re-renders\n const cursorsRef = useRef<Map<string, CursorData>>(new Map())\n const onUpdateRef = useRef<(() => void) | null>(null)\n const lastPublishRef = useRef(0)\n const throttleMsRef = useRef(throttleMs)\n\n // 1Hz state snapshot for display\n const [cursors, setCursors] = useState<CursorData[]>([])\n\n useEffect(() => { throttleMsRef.current = throttleMs }, [throttleMs])\n\n // Handle incoming cursor messages\n useEffect(() => {\n const unsubscribe = addTopicHandler(TOPICS.CURSORS, (_subtopic, payload) => {\n if (typeof payload !== 'object' || payload === null) return\n const data = payload as Record<string, unknown>\n const userId = data.userId as string\n if (!userId || userId === self.userId) return\n\n const now = Date.now()\n\n cursorsRef.current.set(userId, {\n userId,\n name: (data.name as string) || userId,\n x: (data.x as number) || 0,\n y: (data.y as number) || 0,\n color: (data.color as string) || '#3B82F6',\n ts: (data.ts as number) || now,\n lastSeen: now,\n })\n\n // Notify CursorOverlay to update DOM directly\n onUpdateRef.current?.()\n })\n\n return unsubscribe\n }, [addTopicHandler, self.userId])\n\n // Publish own cursor position (throttled)\n const publishCursor = useCallback((x: number, y: number) => {\n const now = Date.now()\n if (now - lastPublishRef.current < throttleMsRef.current) return\n lastPublishRef.current = now\n\n publish(TOPICS.CURSORS, {\n userId: self.userId,\n name: self.name,\n x,\n y,\n color: self.color,\n ts: now,\n }, { qos: 0 })\n }, [publish, self.userId, self.name, self.color])\n\n // 1Hz tick: clean stale cursors + sync state snapshot\n useEffect(() => {\n const interval = setInterval(() => {\n const now = Date.now()\n let changed = false\n for (const [id, cursor] of cursorsRef.current) {\n if (now - cursor.lastSeen > staleMs) {\n cursorsRef.current.delete(id)\n changed = true\n }\n }\n if (changed) {\n onUpdateRef.current?.()\n }\n\n setCursors(Array.from(cursorsRef.current.values()))\n }, DEFAULTS.STATS_TICK_MS)\n\n return () => clearInterval(interval)\n }, [staleMs])\n\n return {\n cursorsRef,\n onUpdateRef,\n cursors,\n publishCursor,\n }\n}\n","'use client'\n\nimport { useEffect, useRef, useState, useCallback } from 'react'\nimport { useSpace } from './useSpace'\nimport { TOPICS } from '../constants'\nimport type { SpaceUser, UseLockReturn } from '../types'\n\n/**\n * Component locking — \"I'm editing this.\"\n *\n * Only one user can hold a lock at a time.\n * Automatically unlocks when the component unmounts.\n */\nexport function useLock(componentId: string): UseLockReturn {\n const { self, publish, addTopicHandler } = useSpace()\n\n const [lockedBy, setLockedBy] = useState<SpaceUser | null>(null)\n const lockedByRef = useRef<SpaceUser | null>(null)\n const isLockedByMeRef = useRef(false)\n const publishRef = useRef(publish)\n const selfUserIdRef = useRef(self.userId)\n\n useEffect(() => { publishRef.current = publish }, [publish])\n useEffect(() => { selfUserIdRef.current = self.userId }, [self.userId])\n\n const isLocked = lockedBy !== null\n const isLockedByMe = lockedBy?.userId === self.userId\n\n // Listen for lock/unlock messages\n useEffect(() => {\n const unsubscribe = addTopicHandler(TOPICS.LOCKS, (_subtopic, payload) => {\n if (typeof payload !== 'object' || payload === null) return\n const data = payload as Record<string, unknown>\n if (data.componentId !== componentId) return\n\n const action = data.action as string\n\n if (action === 'lock') {\n const user: SpaceUser = {\n userId: data.userId as string,\n name: (data.name as string) || (data.userId as string),\n color: (data.color as string) || '#3B82F6',\n joinedAt: Date.now(),\n lastSeen: Date.now(),\n }\n lockedByRef.current = user\n isLockedByMeRef.current = user.userId === self.userId\n setLockedBy(user)\n } else if (action === 'unlock') {\n lockedByRef.current = null\n isLockedByMeRef.current = false\n setLockedBy(null)\n }\n })\n\n return unsubscribe\n }, [addTopicHandler, componentId, self.userId])\n\n const lock = useCallback(() => {\n // Can't lock if already locked by someone else\n if (lockedByRef.current && lockedByRef.current.userId !== self.userId) return\n\n publish(TOPICS.LOCKS, {\n userId: self.userId,\n name: self.name,\n color: self.color,\n componentId,\n action: 'lock',\n ts: Date.now(),\n }, { qos: 1 })\n }, [publish, self, componentId])\n\n const unlock = useCallback(() => {\n if (!isLockedByMeRef.current) return\n\n publish(TOPICS.LOCKS, {\n userId: self.userId,\n componentId,\n action: 'unlock',\n ts: Date.now(),\n }, { qos: 1 })\n }, [publish, self.userId, componentId])\n\n // Auto-unlock on unmount — uses refs for fresh values\n useEffect(() => {\n return () => {\n if (isLockedByMeRef.current) {\n publishRef.current(TOPICS.LOCKS, {\n userId: selfUserIdRef.current,\n componentId,\n action: 'unlock',\n ts: Date.now(),\n }, { qos: 1 })\n }\n }\n }, [componentId])\n\n return { isLocked, lockedBy, isLockedByMe, lock, unlock }\n}\n","'use client'\n\nimport { useEffect, useRef, useState, useCallback } from 'react'\nimport { useSpace } from './useSpace'\nimport { TOPICS, DEFAULTS } from '../constants'\nimport type { SpaceUser, UseTypingIndicatorReturn } from '../types'\n\n/**\n * Typing indicator — \"Alice is typing...\"\n *\n * Auto-resets after 3s of inactivity. Throttled to max 1 publish per 2s.\n * Users not seen typing in 4s are cleaned from the list.\n */\nexport function useTypingIndicator(inputId?: string): UseTypingIndicatorReturn {\n const { self, publish, addTopicHandler } = useSpace()\n\n const [typingUsers, setTypingUsers] = useState<SpaceUser[]>([])\n const [isTyping, setIsTyping] = useState(false)\n const typingMapRef = useRef(new Map<string, SpaceUser & { lastSeen: number }>())\n const lastPublishRef = useRef(0)\n const stopTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n // Listen for typing messages\n useEffect(() => {\n const unsubscribe = addTopicHandler(TOPICS.TYPING, (_subtopic, payload) => {\n if (typeof payload !== 'object' || payload === null) return\n const data = payload as Record<string, unknown>\n const userId = data.userId as string\n if (!userId || userId === self.userId) return\n\n // Filter by inputId if specified\n if (inputId && data.inputId !== inputId) return\n\n const isCurrentlyTyping = data.isTyping as boolean\n\n if (isCurrentlyTyping) {\n typingMapRef.current.set(userId, {\n userId,\n name: (data.name as string) || userId,\n color: (data.color as string) || '#3B82F6',\n joinedAt: Date.now(),\n lastSeen: Date.now(),\n })\n } else {\n typingMapRef.current.delete(userId)\n }\n\n setTypingUsers(Array.from(typingMapRef.current.values()))\n })\n\n return unsubscribe\n }, [addTopicHandler, self.userId, inputId])\n\n // Clean stale typing users\n useEffect(() => {\n const interval = setInterval(() => {\n const now = Date.now()\n let changed = false\n for (const [userId, user] of typingMapRef.current) {\n if (now - user.lastSeen > DEFAULTS.TYPING_STALE_MS) {\n typingMapRef.current.delete(userId)\n changed = true\n }\n }\n if (changed) {\n setTypingUsers(Array.from(typingMapRef.current.values()))\n }\n }, 2_000)\n\n return () => clearInterval(interval)\n }, [])\n\n const publishTyping = useCallback((typing: boolean) => {\n publish(TOPICS.TYPING, {\n userId: self.userId,\n name: self.name,\n color: self.color,\n inputId,\n isTyping: typing,\n ts: Date.now(),\n }, { qos: 0 })\n }, [publish, self, inputId])\n\n const startTyping = useCallback(() => {\n const now = Date.now()\n\n // Throttle publishes\n if (now - lastPublishRef.current >= DEFAULTS.TYPING_THROTTLE_MS) {\n lastPublishRef.current = now\n publishTyping(true)\n }\n\n setIsTyping(true)\n\n // Reset auto-stop timer\n if (stopTimerRef.current) clearTimeout(stopTimerRef.current)\n stopTimerRef.current = setTimeout(() => {\n publishTyping(false)\n setIsTyping(false)\n }, DEFAULTS.TYPING_TIMEOUT_MS)\n }, [publishTyping])\n\n const stopTyping = useCallback(() => {\n if (stopTimerRef.current) {\n clearTimeout(stopTimerRef.current)\n stopTimerRef.current = null\n }\n publishTyping(false)\n setIsTyping(false)\n }, [publishTyping])\n\n // Stop typing on unmount — notify other users\n const publishTypingRef = useRef(publishTyping)\n useEffect(() => { publishTypingRef.current = publishTyping }, [publishTyping])\n\n useEffect(() => {\n return () => {\n if (stopTimerRef.current) {\n clearTimeout(stopTimerRef.current)\n publishTypingRef.current(false)\n }\n }\n }, [])\n\n return { typingUsers, startTyping, stopTyping, isTyping }\n}\n","/** Generate a short random ID for reactions etc. */\nexport function shortId(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID().slice(0, 8)\n }\n return Math.random().toString(36).slice(2, 10)\n}\n","'use client'\n\nimport { useEffect, useRef, useState, useCallback } from 'react'\nimport { useSpace } from './useSpace'\nimport { TOPICS, DEFAULTS } from '../constants'\nimport { shortId } from '../utils/uid'\nimport type { Reaction, UseReactionsOptions, UseReactionsReturn } from '../types'\n\n/**\n * Emoji reactions — floating emojis that auto-expire.\n */\nexport function useReactions(options: UseReactionsOptions = {}): UseReactionsReturn {\n const {\n maxVisible = DEFAULTS.REACTION_MAX_VISIBLE,\n durationMs = DEFAULTS.REACTION_DURATION_MS,\n } = options\n\n const { self, publish, addTopicHandler } = useSpace()\n const [reactions, setReactions] = useState<Reaction[]>([])\n const timersRef = useRef(new Map<string, ReturnType<typeof setTimeout>>())\n\n const addReaction = useCallback((reaction: Reaction) => {\n // Assign a stable random x position for the float animation\n const positioned = reaction.x != null ? reaction : { ...reaction, x: Math.random() * 80 + 10 }\n setReactions(prev => {\n const next = [...prev, positioned]\n // Cap at max visible\n return next.length > maxVisible ? next.slice(-maxVisible) : next\n })\n\n // Auto-remove after duration\n const timer = setTimeout(() => {\n setReactions(prev => prev.filter(r => r.id !== reaction.id))\n timersRef.current.delete(reaction.id)\n }, durationMs)\n\n timersRef.current.set(reaction.id, timer)\n }, [maxVisible, durationMs])\n\n // Listen for reaction messages (filter out self — we add locally in sendReaction)\n useEffect(() => {\n const unsubscribe = addTopicHandler(TOPICS.REACTIONS, (_subtopic, payload) => {\n if (typeof payload !== 'object' || payload === null) return\n const data = payload as Record<string, unknown>\n if (!data.emoji) return\n if ((data.userId as string) === self.userId) return\n\n addReaction({\n id: (data.id as string) || shortId(),\n userId: (data.userId as string) || '',\n name: (data.name as string) || '',\n emoji: data.emoji as string,\n color: (data.color as string) || '#3B82F6',\n ts: (data.ts as number) || Date.now(),\n })\n })\n\n return unsubscribe\n }, [addTopicHandler, addReaction, self.userId])\n\n const sendReaction = useCallback((emoji: string) => {\n const reaction: Reaction = {\n id: shortId(),\n userId: self.userId,\n name: self.name,\n emoji,\n color: self.color,\n ts: Date.now(),\n }\n\n publish(TOPICS.REACTIONS, reaction, { qos: 0 })\n\n // Also show locally\n addReaction(reaction)\n }, [publish, self, addReaction])\n\n // Cleanup timers on unmount\n useEffect(() => {\n return () => {\n for (const timer of timersRef.current.values()) {\n clearTimeout(timer)\n }\n }\n }, [])\n\n return { reactions, sendReaction }\n}\n","'use client'\n\nimport { useEffect, useRef, useState, useCallback } from 'react'\nimport { useSpace } from './useSpace'\nimport { TOPICS } from '../constants'\nimport type { UseBroadcastReturn } from '../types'\n\n/**\n * Generic pub/sub for custom events.\n *\n * If `event` is provided, scopes to that event name.\n * Otherwise, receives all broadcast messages.\n *\n * @example\n * // Scoped to \"chat\" events\n * const { broadcast, onMessage } = useBroadcast<ChatMessage>('chat')\n * broadcast({ text: 'Hello!' })\n *\n * @example\n * // All broadcast events\n * const { onMessage } = useBroadcast()\n */\nexport function useBroadcast<T = unknown>(event?: string): UseBroadcastReturn<T> {\n const { publish, addTopicHandler } = useSpace()\n\n const [lastMessage, setLastMessage] = useState<T | null>(null)\n const callbacksRef = useRef(new Set<(data: T) => void>())\n\n // Listen for broadcast messages\n useEffect(() => {\n const unsubscribe = addTopicHandler(TOPICS.BROADCAST, (subtopic, payload) => {\n // If event is specified, only match that event\n // subtopic will be \"broadcast/chat\" or just \"broadcast\"\n const eventName = subtopic.includes('/') ? subtopic.split('/').slice(1).join('/') : undefined\n\n if (event && eventName !== event) return\n\n const data = payload as T\n setLastMessage(data)\n for (const cb of callbacksRef.current) {\n cb(data)\n }\n })\n\n return unsubscribe\n }, [addTopicHandler, event])\n\n const broadcast = useCallback((data: T) => {\n const subtopic = event\n ? `${TOPICS.BROADCAST}/${event}`\n : TOPICS.BROADCAST\n publish(subtopic, data, { qos: 0 })\n }, [publish, event])\n\n const onMessage = useCallback((callback: (data: T) => void) => {\n callbacksRef.current.add(callback)\n return () => { callbacksRef.current.delete(callback) }\n }, [])\n\n return { broadcast, lastMessage, onMessage }\n}\n","/**\n * Last-Write-Wins merge for useSharedState.\n * Compares timestamps — the message with the newer timestamp wins.\n */\nexport interface LWWEntry<T> {\n value: T\n ts: number\n userId: string\n}\n\nexport function lwwMerge<T>(\n current: LWWEntry<T>,\n incoming: LWWEntry<T>,\n): LWWEntry<T> {\n return incoming.ts > current.ts ? incoming : current\n}\n","'use client'\n\nimport { useEffect, useRef, useState, useCallback } from 'react'\nimport { useSpace } from './useSpace'\nimport { TOPICS } from '../constants'\nimport { lwwMerge, type LWWEntry } from '../utils/lww'\nimport type { UseSharedStateReturn } from '../types'\n\n/**\n * Synced key-value state across all users in the space.\n *\n * Uses Last-Write-Wins (LWW) merge — the update with the newest\n * timestamp wins. Retained messages ensure new joiners get current state.\n *\n * @example\n * const [count, setCount] = useSharedState('counter', 0)\n * <button onClick={() => setCount(count + 1)}>Count: {count}</button>\n */\nexport function useSharedState<T>(key: string, initialValue: T): UseSharedStateReturn<T> {\n const { self, publish, addTopicHandler } = useSpace()\n\n const [value, setValue] = useState<T>(initialValue)\n const entryRef = useRef<LWWEntry<T>>({\n value: initialValue,\n ts: 0,\n userId: self.userId,\n })\n\n // Listen for state updates on this key\n useEffect(() => {\n const subtopic = `${TOPICS.STATE}/${key}`\n\n const unsubscribe = addTopicHandler(TOPICS.STATE, (incomingSubtopic, payload) => {\n // subtopic arrives as \"state/myKey\" — match against our key\n const incomingKey = incomingSubtopic.includes('/')\n ? incomingSubtopic.split('/').slice(1).join('/')\n : undefined\n\n if (incomingKey !== key) return\n if (typeof payload !== 'object' || payload === null) return\n\n const data = payload as Record<string, unknown>\n const incoming: LWWEntry<T> = {\n value: data.value as T,\n ts: (data.ts as number) || 0,\n userId: (data.userId as string) || '',\n }\n\n const merged = lwwMerge(entryRef.current, incoming)\n if (merged !== entryRef.current) {\n entryRef.current = merged\n setValue(merged.value)\n }\n })\n\n return unsubscribe\n }, [addTopicHandler, key])\n\n const update = useCallback((newValue: T) => {\n const ts = Date.now()\n const entry: LWWEntry<T> = { value: newValue, ts, userId: self.userId }\n\n // Update locally immediately\n entryRef.current = entry\n setValue(newValue)\n\n // Publish with retain so new joiners get the latest value\n publish(`${TOPICS.STATE}/${key}`, {\n value: newValue,\n ts,\n userId: self.userId,\n }, { qos: 1, retain: true })\n }, [publish, key, self.userId])\n\n return [value, update]\n}\n","'use client'\n\nimport { usePresence } from '../hooks/usePresence'\nimport { getInitials } from '../utils/colors'\nimport type { AvatarStackProps } from '../types'\n\n/**\n * Shows who's online — overlapping avatars with a \"+N\" overflow badge.\n *\n * @example\n * <AvatarStack max={4} size={36} className=\"absolute top-4 right-4\" />\n */\nexport function AvatarStack({ max = 5, size = 32, className = '' }: AvatarStackProps) {\n const { members } = usePresence()\n\n const visible = members.slice(0, max)\n const overflow = members.length - max\n\n return (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center' }}\n >\n <div style={{ display: 'flex', flexDirection: 'row-reverse' }}>\n {overflow > 0 && (\n <div\n style={{\n width: size,\n height: size,\n borderRadius: '50%',\n backgroundColor: '#6B7280',\n color: 'white',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: size * 0.35,\n fontWeight: 600,\n border: '2px solid white',\n marginLeft: -size * 0.25,\n position: 'relative',\n zIndex: 0,\n }}\n >\n +{overflow}\n </div>\n )}\n {visible.map((user, i) => (\n <div\n key={user.userId}\n title={user.name}\n style={{\n width: size,\n height: size,\n borderRadius: '50%',\n backgroundColor: user.color,\n color: 'white',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: size * 0.35,\n fontWeight: 600,\n border: '2px solid white',\n marginLeft: i === visible.length - 1 ? 0 : -size * 0.25,\n position: 'relative',\n zIndex: visible.length - i,\n overflow: 'hidden',\n }}\n >\n {user.avatar ? (\n <img\n src={user.avatar}\n alt={user.name}\n style={{ width: '100%', height: '100%', objectFit: 'cover' }}\n />\n ) : (\n getInitials(user.name)\n )}\n </div>\n ))}\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { useEffect, useRef, useCallback } from 'react'\nimport { useCursors } from '../hooks/useCursors'\nimport { DEFAULTS } from '../constants'\nimport type { CursorOverlayProps, CursorData } from '../types'\n\n/**\n * Drop-in cursor overlay — wraps children and renders live cursors.\n *\n * Mouse tracking and cursor rendering are handled automatically.\n * Uses imperative DOM manipulation for sub-frame performance.\n *\n * @example\n * <CursorOverlay>\n * <div style={{ width: '100%', height: '400px' }}>\n * Your collaborative content here\n * </div>\n * </CursorOverlay>\n */\nexport function CursorOverlay({\n throttleMs,\n staleMs = DEFAULTS.CURSOR_STALE_MS,\n className = '',\n children,\n}: CursorOverlayProps) {\n const { cursorsRef, onUpdateRef, publishCursor } = useCursors({ throttleMs, staleMs })\n\n const containerRef = useRef<HTMLDivElement>(null)\n const wrapperRef = useRef<HTMLDivElement>(null)\n const nodesRef = useRef<Map<string, HTMLDivElement>>(new Map())\n\n // Imperatively sync cursor DOM nodes — no React re-renders\n const syncDOM = useCallback(() => {\n const container = containerRef.current\n const wrapper = wrapperRef.current\n if (!container || !wrapper) return\n\n const w = container.clientWidth\n const h = container.clientHeight\n const now = Date.now()\n const cursors = cursorsRef.current\n\n // Remove DOM nodes for gone cursors\n for (const [id, node] of nodesRef.current) {\n if (!cursors.has(id)) {\n node.remove()\n nodesRef.current.delete(id)\n }\n }\n\n // Update or create DOM nodes\n for (const [id, cursor] of cursors) {\n const age = now - cursor.lastSeen\n const opacity = age > 1000\n ? Math.max(0, 1 - (age - 1000) / (staleMs - 1000))\n : 1\n\n let node = nodesRef.current.get(id)\n if (!node) {\n node = createCursorNode(cursor)\n wrapper.appendChild(node)\n nodesRef.current.set(id, node)\n }\n\n node.style.transform = `translate(${cursor.x * w}px, ${cursor.y * h}px)`\n node.style.opacity = String(opacity)\n }\n }, [cursorsRef, staleMs])\n\n // Register sync callback for the hook\n useEffect(() => {\n onUpdateRef.current = syncDOM\n return () => { onUpdateRef.current = null }\n }, [onUpdateRef, syncDOM])\n\n // Sync on container resize\n useEffect(() => {\n const container = containerRef.current\n if (!container) return\n const observer = new ResizeObserver(() => syncDOM())\n observer.observe(container)\n return () => observer.disconnect()\n }, [syncDOM])\n\n // Periodic sync for opacity fade-out\n useEffect(() => {\n const interval = setInterval(syncDOM, DEFAULTS.FADE_TICK_MS)\n return () => clearInterval(interval)\n }, [syncDOM])\n\n // Mouse tracking\n const handleMouseMove = useCallback((e: React.MouseEvent) => {\n const container = containerRef.current\n if (!container) return\n const rect = container.getBoundingClientRect()\n const x = (e.clientX - rect.left) / rect.width\n const y = (e.clientY - rect.top) / rect.height\n publishCursor(x, y)\n }, [publishCursor])\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ position: 'relative', overflow: 'hidden' }}\n onMouseMove={handleMouseMove}\n >\n {children}\n <div\n ref={wrapperRef}\n style={{\n position: 'absolute',\n inset: 0,\n pointerEvents: 'none',\n zIndex: 50,\n }}\n />\n </div>\n )\n}\n\n/** Create an SVG cursor DOM node with a name label */\nfunction createCursorNode(cursor: CursorData): HTMLDivElement {\n const node = document.createElement('div')\n node.style.position = 'absolute'\n node.style.left = '0'\n node.style.top = '0'\n node.style.pointerEvents = 'none'\n node.style.transition = 'transform 30ms linear'\n node.innerHTML = `\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 20 20\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" style=\"transform: translate(-2px, -2px)\">\n <path d=\"M3 3L10.07 19.97L12.58 12.58L19.97 10.07L3 3Z\" fill=\"${cursor.color}\" stroke=\"white\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <div style=\"margin-left: 16px; margin-top: 4px; white-space: nowrap; border-radius: 4px; padding: 2px 6px; font-size: 12px; font-weight: 500; color: white; background-color: ${cursor.color}; box-shadow: 0 1px 2px rgba(0,0,0,0.1);\">${escapeHtml(cursor.name)}</div>\n `\n return node\n}\n\nfunction escapeHtml(str: string): string {\n const div = document.createElement('div')\n div.textContent = str\n return div.innerHTML\n}\n","'use client'\n\nimport { useTypingIndicator } from '../hooks/useTypingIndicator'\nimport type { TypingIndicatorProps } from '../types'\n\n/**\n * Displays who's currently typing.\n *\n * Renders nothing if nobody is typing.\n * Automatically formats: \"Alice is typing...\", \"Alice and Bob are typing...\",\n * \"3 people are typing...\"\n *\n * @example\n * <TypingIndicator className=\"text-sm text-gray-500 h-5\" />\n */\nexport function TypingIndicator({ inputId, className = '' }: TypingIndicatorProps) {\n const { typingUsers } = useTypingIndicator(inputId)\n\n if (typingUsers.length === 0) {\n return <div className={className} style={{ minHeight: '1.25rem' }} />\n }\n\n let text: string\n if (typingUsers.length === 1) {\n text = `${typingUsers[0].name} is typing...`\n } else if (typingUsers.length === 2) {\n text = `${typingUsers[0].name} and ${typingUsers[1].name} are typing...`\n } else {\n text = `${typingUsers.length} people are typing...`\n }\n\n return (\n <div className={className} style={{ minHeight: '1.25rem' }}>\n <span style={{ display: 'inline-flex', alignItems: 'center', gap: '4px' }}>\n <TypingDots />\n {text}\n </span>\n </div>\n )\n}\n\nfunction TypingDots() {\n return (\n <span style={{ display: 'inline-flex', gap: '2px', alignItems: 'center' }}>\n {[0, 1, 2].map(i => (\n <span\n key={i}\n style={{\n width: 4,\n height: 4,\n borderRadius: '50%',\n backgroundColor: 'currentColor',\n opacity: 0.6,\n animation: `cs-typing-dot 1.4s infinite`,\n animationDelay: `${i * 0.2}s`,\n }}\n />\n ))}\n <style>{`\n @keyframes cs-typing-dot {\n 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }\n 40% { opacity: 1; transform: scale(1); }\n }\n `}</style>\n </span>\n )\n}\n","'use client'\n\nimport { useLock } from '../hooks/useLock'\nimport type { LockIndicatorProps } from '../types'\n\n/**\n * Shows lock status for a component and renders a colored border\n * when locked by another user.\n *\n * @example\n * <LockIndicator componentId=\"title-field\">\n * <input\n * onFocus={() => lock()}\n * onBlur={() => unlock()}\n * disabled={isLocked && !isLockedByMe}\n * />\n * </LockIndicator>\n *\n * Or use the hook directly for more control:\n * const { isLocked, lockedBy, lock, unlock } = useLock('title-field')\n */\nexport function LockIndicator({ componentId, className = '', children }: LockIndicatorProps) {\n const { isLocked, lockedBy, isLockedByMe } = useLock(componentId)\n\n const borderColor = isLocked\n ? isLockedByMe\n ? lockedBy?.color || '#3B82F6'\n : lockedBy?.color || '#EF4444'\n : 'transparent'\n\n return (\n <div\n className={className}\n style={{\n position: 'relative',\n borderRadius: '6px',\n border: `2px solid ${borderColor}`,\n transition: 'border-color 200ms ease',\n }}\n >\n {children}\n {isLocked && !isLockedByMe && lockedBy && (\n <div\n style={{\n position: 'absolute',\n top: -10,\n right: 8,\n padding: '1px 6px',\n borderRadius: '4px',\n fontSize: '11px',\n fontWeight: 500,\n color: 'white',\n backgroundColor: lockedBy.color,\n whiteSpace: 'nowrap',\n zIndex: 10,\n }}\n >\n {lockedBy.name}\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport { useReactions } from '../hooks/useReactions'\nimport type { ReactionBarProps } from '../types'\n\nconst DEFAULT_EMOJIS = ['👍', '❤️', '😂', '🎉', '🔥', '👀', '🚀', '💯']\n\n/**\n * Emoji reaction bar with floating animations.\n *\n * Renders a row of emoji buttons + floating reactions from all users.\n *\n * @example\n * <ReactionBar emojis={['👍', '❤️', '🎉']} className=\"fixed bottom-4 right-4\" />\n */\nexport function ReactionBar({ emojis = DEFAULT_EMOJIS, className = '' }: ReactionBarProps) {\n const { reactions, sendReaction } = useReactions()\n\n return (\n <div className={className} style={{ position: 'relative' }}>\n {/* Floating reactions */}\n <div\n style={{\n position: 'absolute',\n bottom: '100%',\n left: 0,\n right: 0,\n height: 120,\n pointerEvents: 'none',\n overflow: 'hidden',\n }}\n >\n {reactions.map(reaction => (\n <span\n key={reaction.id}\n style={{\n position: 'absolute',\n bottom: 0,\n left: `${reaction.x ?? 50}%`,\n fontSize: '24px',\n animation: 'cs-reaction-float 3s ease-out forwards',\n pointerEvents: 'none',\n }}\n >\n {reaction.emoji}\n </span>\n ))}\n </div>\n\n {/* Emoji buttons */}\n <div style={{ display: 'flex', gap: '4px', flexWrap: 'wrap' }}>\n {emojis.map(emoji => (\n <button\n key={emoji}\n onClick={() => sendReaction(emoji)}\n style={{\n padding: '4px 8px',\n fontSize: '18px',\n border: '1px solid #E5E7EB',\n borderRadius: '8px',\n backgroundColor: 'white',\n cursor: 'pointer',\n transition: 'transform 100ms ease, background-color 100ms ease',\n }}\n onMouseEnter={e => {\n e.currentTarget.style.transform = 'scale(1.15)'\n e.currentTarget.style.backgroundColor = '#F3F4F6'\n }}\n onMouseLeave={e => {\n e.currentTarget.style.transform = 'scale(1)'\n e.currentTarget.style.backgroundColor = 'white'\n }}\n >\n {emoji}\n </button>\n ))}\n </div>\n\n <style>{`\n @keyframes cs-reaction-float {\n 0% { opacity: 1; transform: translateY(0) scale(1); }\n 100% { opacity: 0; transform: translateY(-100px) scale(1.5); }\n }\n `}</style>\n </div>\n )\n}\n","'use client'\n\nimport { useLock } from '../hooks/useLock'\nimport type { PresenceBorderProps } from '../types'\n\n/**\n * Wraps a component with a colored border showing who's focused on it.\n * Automatically locks/unlocks on focus/blur of child interactive elements.\n *\n * @example\n * <PresenceBorder componentId=\"description-field\">\n * <textarea placeholder=\"Description...\" />\n * </PresenceBorder>\n */\nexport function PresenceBorder({\n componentId,\n borderWidth = 2,\n className = '',\n children,\n}: PresenceBorderProps) {\n const { isLocked, lockedBy, isLockedByMe, lock, unlock } = useLock(componentId)\n\n const borderColor = isLocked\n ? (lockedBy?.color || '#3B82F6')\n : 'transparent'\n\n return (\n <div\n className={className}\n style={{\n position: 'relative',\n borderRadius: '6px',\n border: `${borderWidth}px solid ${borderColor}`,\n transition: 'border-color 200ms ease',\n }}\n onFocusCapture={() => lock()}\n onBlurCapture={() => unlock()}\n >\n {children}\n {isLocked && lockedBy && (\n <div\n style={{\n position: 'absolute',\n top: -(borderWidth + 8),\n left: 8,\n padding: '0px 6px',\n borderRadius: '4px 4px 0 0',\n fontSize: '11px',\n fontWeight: 500,\n color: 'white',\n backgroundColor: lockedBy.color,\n whiteSpace: 'nowrap',\n lineHeight: '16px',\n }}\n >\n {isLockedByMe ? 'You' : lockedBy.name}\n </div>\n )}\n </div>\n )\n}\n","// Provider\nexport { Space, CloudSignalSpace } from './context/SpaceProvider'\n\n// Hooks\nexport { useSpace } from './hooks/useSpace'\nexport { usePresence } from './hooks/usePresence'\nexport { useCursors } from './hooks/useCursors'\nexport { useLock } from './hooks/useLock'\nexport { useTypingIndicator } from './hooks/useTypingIndicator'\nexport { useReactions } from './hooks/useReactions'\nexport { useBroadcast } from './hooks/useBroadcast'\nexport { useSharedState } from './hooks/useSharedState'\n\n// Components\nexport { AvatarStack } from './components/AvatarStack'\nexport { CursorOverlay } from './components/CursorOverlay'\nexport { TypingIndicator } from './components/TypingIndicator'\nexport { LockIndicator } from './components/LockIndicator'\nexport { ReactionBar } from './components/ReactionBar'\nexport { PresenceBorder } from './components/PresenceBorder'\n\n// Utilities\nexport { getColorForUser, getInitials } from './utils/colors'\n\n// Constants\nexport { TOPIC_PREFIX, TOPICS, DEFAULTS, spaceTopic, spaceWildcard } from './constants'\n\n// Types\nexport type {\n SpaceUser,\n SpaceConnectionConfig,\n SpaceProps,\n SpaceContextValue,\n PublishOptions,\n CursorData,\n UseCursorsOptions,\n UseCursorsReturn,\n UsePresenceReturn,\n UseLockReturn,\n UseTypingIndicatorReturn,\n Reaction,\n UseReactionsOptions,\n UseReactionsReturn,\n UseBroadcastReturn,\n UseSharedStateReturn,\n AvatarStackProps,\n CursorOverlayProps,\n TypingIndicatorProps,\n LockIndicatorProps,\n ReactionBarProps,\n PresenceBorderProps,\n} from './types'\n\nexport const VERSION = '0.1.0'\n"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode, RefObject, MutableRefObject } from 'react';
|
|
3
|
+
|
|
4
|
+
/** A user present in a collaborative space */
|
|
5
|
+
interface SpaceUser {
|
|
6
|
+
userId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
color: string;
|
|
9
|
+
avatar?: string;
|
|
10
|
+
data?: Record<string, unknown>;
|
|
11
|
+
joinedAt: number;
|
|
12
|
+
lastSeen: number;
|
|
13
|
+
}
|
|
14
|
+
/** How to connect to CloudSignal — supports credentials, token, or external IdP */
|
|
15
|
+
interface SpaceConnectionConfig {
|
|
16
|
+
/** WebSocket URL (e.g. wss://connect.cloudsignal.app:18885/) */
|
|
17
|
+
host: string;
|
|
18
|
+
/** Direct credential auth */
|
|
19
|
+
username?: string;
|
|
20
|
+
password?: string;
|
|
21
|
+
/** Token-based auth */
|
|
22
|
+
organizationId?: string;
|
|
23
|
+
secretKey?: string;
|
|
24
|
+
/** External IdP token (Supabase, Clerk, etc.) */
|
|
25
|
+
externalToken?: string;
|
|
26
|
+
/** Token service URL for V2 auth */
|
|
27
|
+
tokenServiceUrl?: string;
|
|
28
|
+
}
|
|
29
|
+
interface SpaceProps {
|
|
30
|
+
/** Unique space identifier — used as MQTT topic namespace */
|
|
31
|
+
id: string;
|
|
32
|
+
/** Connection configuration */
|
|
33
|
+
connection: SpaceConnectionConfig;
|
|
34
|
+
/** Display name for the local user */
|
|
35
|
+
userName: string;
|
|
36
|
+
/** Hex color for the local user (auto-assigned if omitted) */
|
|
37
|
+
userColor?: string;
|
|
38
|
+
/** Optional avatar URL */
|
|
39
|
+
userAvatar?: string;
|
|
40
|
+
/** Arbitrary metadata attached to the user's presence */
|
|
41
|
+
userData?: Record<string, unknown>;
|
|
42
|
+
/** Enable debug logging */
|
|
43
|
+
debug?: boolean;
|
|
44
|
+
/** Presence heartbeat interval in ms (default: 10000) */
|
|
45
|
+
presenceHeartbeatMs?: number;
|
|
46
|
+
/** Time before a user is considered offline in ms (default: 30000) */
|
|
47
|
+
presenceTimeoutMs?: number;
|
|
48
|
+
/** Connection status change callback */
|
|
49
|
+
onConnectionChange?: (connected: boolean) => void;
|
|
50
|
+
children: ReactNode;
|
|
51
|
+
}
|
|
52
|
+
type TopicHandler = (subtopic: string, payload: unknown) => void;
|
|
53
|
+
interface SpaceContextValue {
|
|
54
|
+
spaceId: string;
|
|
55
|
+
self: SpaceUser;
|
|
56
|
+
isConnected: boolean;
|
|
57
|
+
isConnecting: boolean;
|
|
58
|
+
error: Error | null;
|
|
59
|
+
/** Time before a user is considered offline in ms */
|
|
60
|
+
presenceTimeoutMs: number;
|
|
61
|
+
/** Publish a message to a space topic (automatically prefixed) */
|
|
62
|
+
publish: (subtopic: string, payload: unknown, options?: PublishOptions) => void;
|
|
63
|
+
/** Register a handler for a topic segment — returns unsubscribe function */
|
|
64
|
+
addTopicHandler: (segment: string, handler: TopicHandler) => () => void;
|
|
65
|
+
}
|
|
66
|
+
interface PublishOptions {
|
|
67
|
+
qos?: 0 | 1 | 2;
|
|
68
|
+
retain?: boolean;
|
|
69
|
+
}
|
|
70
|
+
interface CursorData {
|
|
71
|
+
userId: string;
|
|
72
|
+
name: string;
|
|
73
|
+
x: number;
|
|
74
|
+
y: number;
|
|
75
|
+
color: string;
|
|
76
|
+
ts: number;
|
|
77
|
+
lastSeen: number;
|
|
78
|
+
}
|
|
79
|
+
interface UseCursorsOptions {
|
|
80
|
+
/** Throttle publish rate in ms (default: 30 → ~33Hz) */
|
|
81
|
+
throttleMs?: number;
|
|
82
|
+
/** Time before a cursor fades out in ms (default: 3000) */
|
|
83
|
+
staleMs?: number;
|
|
84
|
+
}
|
|
85
|
+
interface UseCursorsReturn {
|
|
86
|
+
/** Ref-based cursor map for imperative rendering (no re-renders) */
|
|
87
|
+
cursorsRef: RefObject<Map<string, CursorData>>;
|
|
88
|
+
/** Callback ref — set by CursorOverlay to sync DOM on each message */
|
|
89
|
+
onUpdateRef: MutableRefObject<(() => void) | null>;
|
|
90
|
+
/** State snapshot of cursors, updated at 1Hz for display purposes */
|
|
91
|
+
cursors: CursorData[];
|
|
92
|
+
/** Publish the local user's cursor position (normalized 0–1) */
|
|
93
|
+
publishCursor: (x: number, y: number) => void;
|
|
94
|
+
}
|
|
95
|
+
interface UsePresenceReturn {
|
|
96
|
+
/** All users currently in the space (including self) */
|
|
97
|
+
members: SpaceUser[];
|
|
98
|
+
/** Number of members */
|
|
99
|
+
count: number;
|
|
100
|
+
/** The local user */
|
|
101
|
+
self: SpaceUser;
|
|
102
|
+
/** Register a callback for when a user joins */
|
|
103
|
+
onJoin: (callback: (user: SpaceUser) => void) => () => void;
|
|
104
|
+
/** Register a callback for when a user leaves */
|
|
105
|
+
onLeave: (callback: (user: SpaceUser) => void) => () => void;
|
|
106
|
+
}
|
|
107
|
+
interface UseLockReturn {
|
|
108
|
+
/** Whether this component is currently locked */
|
|
109
|
+
isLocked: boolean;
|
|
110
|
+
/** The user who holds the lock (null if unlocked) */
|
|
111
|
+
lockedBy: SpaceUser | null;
|
|
112
|
+
/** Whether the local user holds the lock */
|
|
113
|
+
isLockedByMe: boolean;
|
|
114
|
+
/** Acquire the lock */
|
|
115
|
+
lock: () => void;
|
|
116
|
+
/** Release the lock */
|
|
117
|
+
unlock: () => void;
|
|
118
|
+
}
|
|
119
|
+
interface UseTypingIndicatorReturn {
|
|
120
|
+
/** Users currently typing */
|
|
121
|
+
typingUsers: SpaceUser[];
|
|
122
|
+
/** Signal that the local user started typing */
|
|
123
|
+
startTyping: () => void;
|
|
124
|
+
/** Signal that the local user stopped typing */
|
|
125
|
+
stopTyping: () => void;
|
|
126
|
+
/** Whether the local user is currently marked as typing */
|
|
127
|
+
isTyping: boolean;
|
|
128
|
+
}
|
|
129
|
+
interface Reaction {
|
|
130
|
+
id: string;
|
|
131
|
+
userId: string;
|
|
132
|
+
name: string;
|
|
133
|
+
emoji: string;
|
|
134
|
+
color: string;
|
|
135
|
+
ts: number;
|
|
136
|
+
/** Horizontal position for float animation (0–100), assigned on creation */
|
|
137
|
+
x?: number;
|
|
138
|
+
}
|
|
139
|
+
interface UseReactionsOptions {
|
|
140
|
+
/** Max visible reactions at once (default: 20) */
|
|
141
|
+
maxVisible?: number;
|
|
142
|
+
/** How long a reaction stays visible in ms (default: 3000) */
|
|
143
|
+
durationMs?: number;
|
|
144
|
+
}
|
|
145
|
+
interface UseReactionsReturn {
|
|
146
|
+
/** Currently visible reactions */
|
|
147
|
+
reactions: Reaction[];
|
|
148
|
+
/** Send an emoji reaction */
|
|
149
|
+
sendReaction: (emoji: string) => void;
|
|
150
|
+
}
|
|
151
|
+
interface UseBroadcastReturn<T = unknown> {
|
|
152
|
+
/** Send a broadcast message */
|
|
153
|
+
broadcast: (data: T) => void;
|
|
154
|
+
/** Last received message */
|
|
155
|
+
lastMessage: T | null;
|
|
156
|
+
/** Register a callback for incoming broadcasts */
|
|
157
|
+
onMessage: (callback: (data: T) => void) => () => void;
|
|
158
|
+
}
|
|
159
|
+
type UseSharedStateReturn<T> = [
|
|
160
|
+
/** Current value */
|
|
161
|
+
T,
|
|
162
|
+
/** Update the value (last-write-wins across clients) */
|
|
163
|
+
(value: T) => void
|
|
164
|
+
];
|
|
165
|
+
interface AvatarStackProps {
|
|
166
|
+
/** Max avatars before showing "+N" (default: 5) */
|
|
167
|
+
max?: number;
|
|
168
|
+
/** Avatar size in pixels (default: 32) */
|
|
169
|
+
size?: number;
|
|
170
|
+
className?: string;
|
|
171
|
+
}
|
|
172
|
+
interface CursorOverlayProps {
|
|
173
|
+
/** Throttle publish rate in ms (default: 30) */
|
|
174
|
+
throttleMs?: number;
|
|
175
|
+
/** Cursor stale timeout in ms (default: 3000) */
|
|
176
|
+
staleMs?: number;
|
|
177
|
+
className?: string;
|
|
178
|
+
children?: ReactNode;
|
|
179
|
+
}
|
|
180
|
+
interface TypingIndicatorProps {
|
|
181
|
+
/** Optional input ID to scope typing to */
|
|
182
|
+
inputId?: string;
|
|
183
|
+
className?: string;
|
|
184
|
+
}
|
|
185
|
+
interface LockIndicatorProps {
|
|
186
|
+
/** ID of the component being locked */
|
|
187
|
+
componentId: string;
|
|
188
|
+
className?: string;
|
|
189
|
+
children?: ReactNode;
|
|
190
|
+
}
|
|
191
|
+
interface ReactionBarProps {
|
|
192
|
+
/** Emoji options to show (default: common set) */
|
|
193
|
+
emojis?: string[];
|
|
194
|
+
className?: string;
|
|
195
|
+
}
|
|
196
|
+
interface PresenceBorderProps {
|
|
197
|
+
/** Component ID for lock tracking */
|
|
198
|
+
componentId: string;
|
|
199
|
+
/** Border width in pixels (default: 2) */
|
|
200
|
+
borderWidth?: number;
|
|
201
|
+
className?: string;
|
|
202
|
+
children?: ReactNode;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
declare function Space({ id, connection, userName, userColor, userAvatar, userData, debug, presenceHeartbeatMs, presenceTimeoutMs, onConnectionChange, children, }: SpaceProps): react_jsx_runtime.JSX.Element;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Access the current Space context.
|
|
209
|
+
* Must be used within a `<Space>` provider.
|
|
210
|
+
*/
|
|
211
|
+
declare function useSpace(): SpaceContextValue;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Track who's in the space.
|
|
215
|
+
* Presence heartbeats are managed by the Space provider —
|
|
216
|
+
* this hook only reads and exposes the member list.
|
|
217
|
+
*/
|
|
218
|
+
declare function usePresence(): UsePresenceReturn;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Live cursor tracking.
|
|
222
|
+
*
|
|
223
|
+
* Performance: Cursor data is stored in refs (not state) to avoid
|
|
224
|
+
* re-renders on every MQTT message (~33 messages/sec per user).
|
|
225
|
+
* The `onUpdateRef` callback lets CursorOverlay sync DOM imperatively.
|
|
226
|
+
* A 1Hz state snapshot is provided for non-critical display (e.g. online count).
|
|
227
|
+
*/
|
|
228
|
+
declare function useCursors(options?: UseCursorsOptions): UseCursorsReturn;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Component locking — "I'm editing this."
|
|
232
|
+
*
|
|
233
|
+
* Only one user can hold a lock at a time.
|
|
234
|
+
* Automatically unlocks when the component unmounts.
|
|
235
|
+
*/
|
|
236
|
+
declare function useLock(componentId: string): UseLockReturn;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Typing indicator — "Alice is typing..."
|
|
240
|
+
*
|
|
241
|
+
* Auto-resets after 3s of inactivity. Throttled to max 1 publish per 2s.
|
|
242
|
+
* Users not seen typing in 4s are cleaned from the list.
|
|
243
|
+
*/
|
|
244
|
+
declare function useTypingIndicator(inputId?: string): UseTypingIndicatorReturn;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Emoji reactions — floating emojis that auto-expire.
|
|
248
|
+
*/
|
|
249
|
+
declare function useReactions(options?: UseReactionsOptions): UseReactionsReturn;
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Generic pub/sub for custom events.
|
|
253
|
+
*
|
|
254
|
+
* If `event` is provided, scopes to that event name.
|
|
255
|
+
* Otherwise, receives all broadcast messages.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* // Scoped to "chat" events
|
|
259
|
+
* const { broadcast, onMessage } = useBroadcast<ChatMessage>('chat')
|
|
260
|
+
* broadcast({ text: 'Hello!' })
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* // All broadcast events
|
|
264
|
+
* const { onMessage } = useBroadcast()
|
|
265
|
+
*/
|
|
266
|
+
declare function useBroadcast<T = unknown>(event?: string): UseBroadcastReturn<T>;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Synced key-value state across all users in the space.
|
|
270
|
+
*
|
|
271
|
+
* Uses Last-Write-Wins (LWW) merge — the update with the newest
|
|
272
|
+
* timestamp wins. Retained messages ensure new joiners get current state.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* const [count, setCount] = useSharedState('counter', 0)
|
|
276
|
+
* <button onClick={() => setCount(count + 1)}>Count: {count}</button>
|
|
277
|
+
*/
|
|
278
|
+
declare function useSharedState<T>(key: string, initialValue: T): UseSharedStateReturn<T>;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Shows who's online — overlapping avatars with a "+N" overflow badge.
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* <AvatarStack max={4} size={36} className="absolute top-4 right-4" />
|
|
285
|
+
*/
|
|
286
|
+
declare function AvatarStack({ max, size, className }: AvatarStackProps): react_jsx_runtime.JSX.Element;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Drop-in cursor overlay — wraps children and renders live cursors.
|
|
290
|
+
*
|
|
291
|
+
* Mouse tracking and cursor rendering are handled automatically.
|
|
292
|
+
* Uses imperative DOM manipulation for sub-frame performance.
|
|
293
|
+
*
|
|
294
|
+
* @example
|
|
295
|
+
* <CursorOverlay>
|
|
296
|
+
* <div style={{ width: '100%', height: '400px' }}>
|
|
297
|
+
* Your collaborative content here
|
|
298
|
+
* </div>
|
|
299
|
+
* </CursorOverlay>
|
|
300
|
+
*/
|
|
301
|
+
declare function CursorOverlay({ throttleMs, staleMs, className, children, }: CursorOverlayProps): react_jsx_runtime.JSX.Element;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Displays who's currently typing.
|
|
305
|
+
*
|
|
306
|
+
* Renders nothing if nobody is typing.
|
|
307
|
+
* Automatically formats: "Alice is typing...", "Alice and Bob are typing...",
|
|
308
|
+
* "3 people are typing..."
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* <TypingIndicator className="text-sm text-gray-500 h-5" />
|
|
312
|
+
*/
|
|
313
|
+
declare function TypingIndicator({ inputId, className }: TypingIndicatorProps): react_jsx_runtime.JSX.Element;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Shows lock status for a component and renders a colored border
|
|
317
|
+
* when locked by another user.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* <LockIndicator componentId="title-field">
|
|
321
|
+
* <input
|
|
322
|
+
* onFocus={() => lock()}
|
|
323
|
+
* onBlur={() => unlock()}
|
|
324
|
+
* disabled={isLocked && !isLockedByMe}
|
|
325
|
+
* />
|
|
326
|
+
* </LockIndicator>
|
|
327
|
+
*
|
|
328
|
+
* Or use the hook directly for more control:
|
|
329
|
+
* const { isLocked, lockedBy, lock, unlock } = useLock('title-field')
|
|
330
|
+
*/
|
|
331
|
+
declare function LockIndicator({ componentId, className, children }: LockIndicatorProps): react_jsx_runtime.JSX.Element;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Emoji reaction bar with floating animations.
|
|
335
|
+
*
|
|
336
|
+
* Renders a row of emoji buttons + floating reactions from all users.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* <ReactionBar emojis={['👍', '❤️', '🎉']} className="fixed bottom-4 right-4" />
|
|
340
|
+
*/
|
|
341
|
+
declare function ReactionBar({ emojis, className }: ReactionBarProps): react_jsx_runtime.JSX.Element;
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Wraps a component with a colored border showing who's focused on it.
|
|
345
|
+
* Automatically locks/unlocks on focus/blur of child interactive elements.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* <PresenceBorder componentId="description-field">
|
|
349
|
+
* <textarea placeholder="Description..." />
|
|
350
|
+
* </PresenceBorder>
|
|
351
|
+
*/
|
|
352
|
+
declare function PresenceBorder({ componentId, borderWidth, className, children, }: PresenceBorderProps): react_jsx_runtime.JSX.Element;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Deterministic color assignment based on userId.
|
|
356
|
+
* Same user always gets the same color across sessions.
|
|
357
|
+
*/
|
|
358
|
+
declare function getColorForUser(userId: string): string;
|
|
359
|
+
/**
|
|
360
|
+
* Get user initials from display name (for avatars).
|
|
361
|
+
* "Alice Smith" → "AS", "bob" → "B"
|
|
362
|
+
*/
|
|
363
|
+
declare function getInitials(name: string): string;
|
|
364
|
+
|
|
365
|
+
/** MQTT topic prefix for all space collaboration messages */
|
|
366
|
+
declare const TOPIC_PREFIX = "$spaces";
|
|
367
|
+
/** Topic segments for each collaboration primitive */
|
|
368
|
+
declare const TOPICS: {
|
|
369
|
+
readonly PRESENCE: "presence";
|
|
370
|
+
readonly CURSORS: "cursors";
|
|
371
|
+
readonly LOCKS: "locks";
|
|
372
|
+
readonly TYPING: "typing";
|
|
373
|
+
readonly REACTIONS: "reactions";
|
|
374
|
+
readonly BROADCAST: "broadcast";
|
|
375
|
+
readonly STATE: "state";
|
|
376
|
+
};
|
|
377
|
+
/** Default timing constants */
|
|
378
|
+
declare const DEFAULTS: {
|
|
379
|
+
/** Presence heartbeat interval (ms) */
|
|
380
|
+
readonly PRESENCE_HEARTBEAT_MS: 10000;
|
|
381
|
+
/** Time before a user is considered offline (ms) */
|
|
382
|
+
readonly PRESENCE_TIMEOUT_MS: 30000;
|
|
383
|
+
/** Cursor publish throttle (ms) — ~33Hz */
|
|
384
|
+
readonly CURSOR_THROTTLE_MS: 30;
|
|
385
|
+
/** Time before a cursor fades out (ms) */
|
|
386
|
+
readonly CURSOR_STALE_MS: 3000;
|
|
387
|
+
/** Typing indicator auto-reset (ms) */
|
|
388
|
+
readonly TYPING_TIMEOUT_MS: 3000;
|
|
389
|
+
/** Minimum interval between typing publishes (ms) */
|
|
390
|
+
readonly TYPING_THROTTLE_MS: 2000;
|
|
391
|
+
/** Time before a typing user is cleaned from the list (ms) */
|
|
392
|
+
readonly TYPING_STALE_MS: 4000;
|
|
393
|
+
/** How long a reaction stays visible (ms) */
|
|
394
|
+
readonly REACTION_DURATION_MS: 3000;
|
|
395
|
+
/** Max visible reactions at once */
|
|
396
|
+
readonly REACTION_MAX_VISIBLE: 20;
|
|
397
|
+
/** Stats sync interval (ms) — for cursor count etc. */
|
|
398
|
+
readonly STATS_TICK_MS: 1000;
|
|
399
|
+
/** Opacity fade-out sync interval (ms) */
|
|
400
|
+
readonly FADE_TICK_MS: 200;
|
|
401
|
+
};
|
|
402
|
+
/** Build a full MQTT topic for a space */
|
|
403
|
+
declare function spaceTopic(spaceId: string, segment: string): string;
|
|
404
|
+
/** Build the wildcard subscription for a space */
|
|
405
|
+
declare function spaceWildcard(spaceId: string): string;
|
|
406
|
+
|
|
407
|
+
declare const VERSION = "0.1.0";
|
|
408
|
+
|
|
409
|
+
export { AvatarStack, type AvatarStackProps, Space as CloudSignalSpace, type CursorData, CursorOverlay, type CursorOverlayProps, DEFAULTS, LockIndicator, type LockIndicatorProps, PresenceBorder, type PresenceBorderProps, type PublishOptions, type Reaction, ReactionBar, type ReactionBarProps, Space, type SpaceConnectionConfig, type SpaceContextValue, type SpaceProps, type SpaceUser, TOPICS, TOPIC_PREFIX, TypingIndicator, type TypingIndicatorProps, type UseBroadcastReturn, type UseCursorsOptions, type UseCursorsReturn, type UseLockReturn, type UsePresenceReturn, type UseReactionsOptions, type UseReactionsReturn, type UseSharedStateReturn, type UseTypingIndicatorReturn, VERSION, getColorForUser, getInitials, spaceTopic, spaceWildcard, useBroadcast, useCursors, useLock, usePresence, useReactions, useSharedState, useSpace, useTypingIndicator };
|