@thrillee/aegischat 0.1.1 → 0.1.3

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/dist/index.js CHANGED
@@ -307,16 +307,19 @@ function useChat(options) {
307
307
  const oldestMessageId = (0, import_react.useRef)(null);
308
308
  const activeChannelIdRef = (0, import_react.useRef)(null);
309
309
  const configRef = (0, import_react.useRef)(config);
310
- const sessionRef = (0, import_react.useRef)(null);
310
+ const sessionRef = (0, import_react.useRef)(initialSession ?? null);
311
+ if (initialSession && !config) {
312
+ configureApiClient({
313
+ baseUrl: initialSession.api_url,
314
+ getAccessToken: async () => sessionRef.current?.access_token || ""
315
+ });
316
+ }
311
317
  (0, import_react.useEffect)(() => {
312
318
  configRef.current = config;
313
319
  }, [config]);
314
320
  (0, import_react.useEffect)(() => {
315
321
  activeChannelIdRef.current = activeChannelId;
316
322
  }, [activeChannelId]);
317
- (0, import_react.useEffect)(() => {
318
- sessionRef.current = session;
319
- }, [session]);
320
323
  const getActiveChannelId = (0, import_react.useCallback)(() => {
321
324
  if (typeof window === "undefined") return null;
322
325
  return sessionStorage.getItem(SESSION_STORAGE_KEY);
@@ -480,6 +483,7 @@ function useChat(options) {
480
483
  ws.onopen = () => {
481
484
  console.log("[AegisChat] WebSocket connected");
482
485
  setIsConnected(true);
486
+ setIsConnecting(false);
483
487
  reconnectAttempts.current = 0;
484
488
  onConnectionChange?.(true);
485
489
  pingInterval.current = setInterval(() => {
@@ -502,6 +506,7 @@ function useChat(options) {
502
506
  ws.onclose = () => {
503
507
  console.log("[AegisChat] WebSocket disconnected");
504
508
  setIsConnected(false);
509
+ setIsConnecting(false);
505
510
  clearTimers();
506
511
  onConnectionChange?.(false);
507
512
  if (!isManualDisconnect.current && reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {
@@ -520,7 +525,8 @@ function useChat(options) {
520
525
  }, [clearTimers, handleWebSocketMessage, onConnectionChange]);
521
526
  const connect = (0, import_react.useCallback)(async () => {
522
527
  console.log("[AegisChat] connect() called");
523
- if (!sessionRef.current && !config) {
528
+ const targetSession = sessionRef.current ?? initialSession;
529
+ if (!targetSession) {
524
530
  throw new Error("Either config or initialSession must be provided");
525
531
  }
526
532
  if (sessionRef.current) {
@@ -528,19 +534,9 @@ function useChat(options) {
528
534
  connectWebSocket();
529
535
  return;
530
536
  }
531
- try {
532
- setIsConnecting(true);
533
- console.log("[AegisChat] Fetching chat session...");
534
- const result = await chatApi.connect({ role, client_id: clientId });
535
- console.log("[AegisChat] Chat session received:", result);
536
- setSession(result.data);
537
- setIsConnecting(false);
538
- } catch (error) {
539
- console.error("[AegisChat] Failed to get chat session:", error);
540
- setIsConnecting(false);
541
- throw error;
542
- }
543
- }, [role, clientId, connectWebSocket]);
537
+ console.log("[AegisChat] Using initialSession, calling connectWebSocket directly");
538
+ connectWebSocket();
539
+ }, [connectWebSocket]);
544
540
  const disconnect = (0, import_react.useCallback)(() => {
545
541
  isManualDisconnect.current = true;
546
542
  clearTimers();
@@ -794,9 +790,6 @@ function useChat(options) {
794
790
  const deleteFailedMessage = (0, import_react.useCallback)((tempId) => {
795
791
  setMessages((prev) => prev.filter((m) => m.tempId !== tempId));
796
792
  }, []);
797
- (0, import_react.useEffect)(() => {
798
- connect();
799
- }, []);
800
793
  (0, import_react.useEffect)(() => {
801
794
  if (session && !isConnected && !isConnecting && autoConnect) {
802
795
  connectWebSocket();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/hooks/useChat.ts","../src/services/api.ts","../src/hooks/useAutoRead.ts","../src/hooks/useChannels.ts","../src/hooks/useMessages.ts","../src/hooks/useTypingIndicator.ts","../src/hooks/useReactions.ts","../src/hooks/useFileUpload.ts","../src/hooks/useMentions.ts"],"sourcesContent":["// ============================================================================\n// AegisChat React SDK - Main Entry Point\n// ============================================================================\n\nexport { useChat } from './hooks/useChat';\nexport type { UseChatOptions, UseChatReturn } from './hooks/useChat';\n\nexport { useAutoRead } from './hooks/useAutoRead';\nexport type { UseAutoReadOptions, UseAutoReadReturn } from './hooks/useAutoRead';\n\nexport { useChannels } from './hooks/useChannels';\nexport type { UseChannelsOptions, UseChannelsReturn } from './hooks/useChannels';\n\nexport { useMessages } from './hooks/useMessages';\nexport type { UseMessagesOptions, UseMessagesReturn } from './hooks/useMessages';\n\nexport { useTypingIndicator } from './hooks/useTypingIndicator';\nexport type { UseTypingIndicatorOptions } from './hooks/useTypingIndicator';\n\nexport { useReactions } from './hooks/useReactions';\nexport type { UseReactionsOptions } from './hooks/useReactions';\n\nexport { useFileUpload } from './hooks/useFileUpload';\nexport type { UseFileUploadOptions } from './hooks/useFileUpload';\n\nexport { useMentions } from './hooks/useMentions';\nexport type { UseMentionsOptions } from './hooks/useMentions';\n\nexport {\n chatApi,\n channelsApi,\n messagesApi,\n reactionsApi,\n filesApi,\n usersApi,\n configureApiClient,\n} from './services/api';\n\nexport type {\n AegisConfig,\n ChatSession,\n ChatConnectParams,\n UserSummary,\n UserStatus,\n Channel,\n ChannelListItem,\n ChannelType,\n Message,\n MessageSummary,\n MessageType,\n MessageStatus,\n MessageMetadata,\n FileAttachment,\n TypingUser,\n TypingEvent,\n ReactionSummary,\n ReactionEvent,\n UploadProgress,\n WebSocketMessage,\n WebSocketStatus,\n ApiResponse,\n ApiError,\n PaginationParams,\n PaginationMeta,\n PaginatedResponse,\n MessagesResponse,\n ChannelsResponse,\n} from './types';\n","// ============================================================================\n// AegisChat React SDK - useChat Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { chatApi, channelsApi, messagesApi, filesApi } from '../services/api';\nimport type {\n AegisConfig,\n ChatSession,\n ChannelListItem,\n Message,\n MessagesResponse,\n TypingUser,\n UserSummary,\n FileAttachment,\n UploadProgress,\n MessageSummary,\n} from '../types';\n\nconst TYPING_TIMEOUT = 3000;\nconst RECONNECT_INTERVAL = 3000;\nconst MAX_RECONNECT_ATTEMPTS = 5;\nconst MAX_RECONNECT_DELAY = 30000;\nconst PING_INTERVAL = 30000;\nconst SESSION_STORAGE_KEY = '@aegischat/activeChannel';\n\nexport interface UseChatOptions {\n config?: AegisConfig;\n role: 'lawyer' | 'client';\n clientId?: string;\n \n initialSession?: ChatSession | null;\n autoConnect?: boolean;\n onMessage?: (message: Message) => void;\n onTyping?: (channelId: string, user: TypingUser) => void;\n onConnectionChange?: (connected: boolean) => void;\n}\n\nexport interface UseChatReturn {\n session: ChatSession | null;\n isConnected: boolean;\n isConnecting: boolean;\n channels: ChannelListItem[];\n messages: Message[];\n activeChannelId: string | null;\n typingUsers: TypingUser[];\n isLoadingChannels: boolean;\n isLoadingMessages: boolean;\n hasMoreMessages: boolean;\n uploadProgress: UploadProgress[];\n connect: () => Promise<void>;\n disconnect: () => void;\n selectChannel: (channelId: string) => void;\n sendMessage: (content: string, options?: { type?: string; parent_id?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n sendMessageWithFiles: (content: string, files: File[], options?: { type?: string; parent_id?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n uploadFile: (file: File) => Promise<FileAttachment | null>;\n loadMoreMessages: () => Promise<void>;\n startTyping: () => void;\n stopTyping: () => void;\n refreshChannels: () => Promise<void>;\n createDMWithUser: (userId: string) => Promise<string | null>;\n retryMessage: (tempId: string) => Promise<void>;\n deleteFailedMessage: (tempId: string) => void;\n markAsRead: (channelId: string) => Promise<void>;\n}\n\nexport function useChat(options: UseChatOptions): UseChatReturn {\n const { config, role, clientId, initialSession, autoConnect = true, onMessage, onTyping, onConnectionChange } = options;\n\n const [session, setSession] = useState<ChatSession | null>(initialSession ?? null);\n const [isConnected, setIsConnected] = useState(false);\n const [isConnecting, setIsConnecting] = useState(false);\n const [activeChannelId, setActiveChannelIdState] = useState<string | null>(null);\n const [channels, setChannels] = useState<ChannelListItem[]>([]);\n const [messages, setMessages] = useState<Message[]>([]);\n const [typingUsers, setTypingUsers] = useState<Record<string, TypingUser[]>>({});\n const [isLoadingChannels, setIsLoadingChannels] = useState(false);\n const [isLoadingMessages, setIsLoadingMessages] = useState(false);\n const [hasMoreMessages, setHasMoreMessages] = useState(true);\n const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);\n\n const wsRef = useRef<WebSocket | null>(null);\n const reconnectAttempts = useRef(0);\n const reconnectTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pingInterval = useRef<ReturnType<typeof setInterval> | null>(null);\n const typingTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isManualDisconnect = useRef(false);\n const oldestMessageId = useRef<string | null>(null);\n const activeChannelIdRef = useRef<string | null>(null);\n const configRef = useRef(config);\n const sessionRef = useRef<ChatSession | null>(null);\n\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n useEffect(() => {\n activeChannelIdRef.current = activeChannelId;\n }, [activeChannelId]);\n\n useEffect(() => {\n sessionRef.current = session;\n }, [session]);\n\n const getActiveChannelId = useCallback((): string | null => {\n if (typeof window === 'undefined') return null;\n return sessionStorage.getItem(SESSION_STORAGE_KEY);\n }, []);\n\n const setActiveChannelId = useCallback((id: string | null) => {\n setActiveChannelIdState(id);\n if (typeof window !== 'undefined') {\n if (id) {\n sessionStorage.setItem(SESSION_STORAGE_KEY, id);\n } else {\n sessionStorage.removeItem(SESSION_STORAGE_KEY);\n }\n }\n }, []);\n\n const fetchFromComms = useCallback(async <T>(path: string, fetchOptions: RequestInit = {}): Promise<T> => {\n const currentSession = sessionRef.current;\n if (!currentSession) {\n throw new Error('Chat session not initialized');\n }\n\n const response = await fetch(`${currentSession.api_url}${path}`, {\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${currentSession.access_token}`,\n ...fetchOptions.headers,\n },\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n const data = await response.json();\n return data.data || data;\n }, []);\n\n const clearTimers = useCallback(() => {\n if (reconnectTimeout.current) {\n clearTimeout(reconnectTimeout.current);\n reconnectTimeout.current = null;\n }\n if (pingInterval.current) {\n clearInterval(pingInterval.current);\n pingInterval.current = null;\n }\n }, []);\n\n const handleWebSocketMessage = useCallback((data: { type: string; payload: unknown }) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n console.log('[AegisChat] WebSocket message received:', data.type, data);\n\n switch (data.type) {\n case 'message.new': {\n const newMessage = data.payload as Message;\n if (newMessage.channel_id === currentActiveChannelId) {\n setMessages((prev) => {\n const existingIndex = prev.findIndex(\n (m) => m.tempId && m.content === newMessage.content && m.status === 'sending'\n );\n if (existingIndex !== -1) {\n const updated = [...prev];\n updated[existingIndex] = { ...newMessage, status: 'sent' };\n return updated;\n }\n if (prev.some((m) => m.id === newMessage.id)) return prev;\n return [...prev, { ...newMessage, status: 'delivered' }];\n });\n onMessage?.(newMessage);\n }\n setChannels((prev) => {\n const updated = prev.map((ch) =>\n ch.id === newMessage.channel_id\n ? {\n ...ch,\n last_message: {\n id: newMessage.id,\n content: newMessage.content,\n created_at: newMessage.created_at,\n sender: { id: newMessage.sender_id, display_name: 'Unknown', status: 'online' as const },\n } as MessageSummary,\n unread_count: ch.id === currentActiveChannelId ? 0 : ch.unread_count + 1,\n }\n : ch\n );\n return updated.sort((a, b) => {\n const timeA = a.last_message?.created_at || '';\n const timeB = b.last_message?.created_at || '';\n return timeB.localeCompare(timeA);\n });\n });\n break;\n }\n case 'message.updated': {\n const updatedMessage = data.payload as Message;\n setMessages((prev) => prev.map((m) => (m.id === updatedMessage.id ? updatedMessage : m)));\n break;\n }\n case 'message.deleted': {\n const { message_id } = data.payload as { message_id: string };\n setMessages((prev) => prev.map((m) => (m.id === message_id ? { ...m, deleted: true } : m)));\n break;\n }\n case 'message.delivered':\n case 'message.read': {\n const { message_id, channel_id, status } = data.payload as { message_id: string; channel_id: string; status: string };\n if (channel_id === currentActiveChannelId) {\n setMessages((prev) =>\n prev.map((m) => (m.id === message_id ? { ...m, status: (status as Message['status']) || 'delivered' } : m))\n );\n }\n break;\n }\n case 'message.delivered.batch':\n case 'message.read.batch': {\n const { channel_id } = data.payload as { channel_id: string };\n if (channel_id === currentActiveChannelId) {\n setMessages((prev) =>\n prev.map((m) => (m.status === 'sent' || m.status === 'delivered' ? { ...m, status: data.type === 'message.delivered.batch' ? 'delivered' : 'read' } : m))\n );\n }\n break;\n }\n case 'typing.start': {\n const { channel_id, user } = data.payload as { channel_id: string; user: UserSummary };\n const typingUser: TypingUser = {\n id: user.id,\n displayName: user.display_name,\n avatarUrl: user.avatar_url,\n startedAt: Date.now(),\n };\n setTypingUsers((prev) => ({\n ...prev,\n [channel_id]: [...(prev[channel_id] || []).filter((u) => u.id !== user.id), typingUser],\n }));\n onTyping?.(channel_id, typingUser);\n break;\n }\n case 'typing.stop': {\n const { channel_id, user_id } = data.payload as { channel_id: string; user_id: string };\n setTypingUsers((prev) => ({\n ...prev,\n [channel_id]: (prev[channel_id] || []).filter((u) => u.id !== user_id),\n }));\n break;\n }\n case 'pong':\n break;\n default:\n console.log('[AegisChat] Unhandled message type:', data.type);\n }\n }, [onMessage, onTyping]);\n\n const connectWebSocket = useCallback(() => {\n const currentSession = sessionRef.current;\n if (!currentSession?.websocket_url || !currentSession?.access_token) {\n console.warn('[AegisChat] Cannot connect WebSocket - missing session or token');\n return;\n }\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n console.log('[AegisChat] WebSocket already open, skipping connection');\n return;\n }\n\n setIsConnecting(true);\n isManualDisconnect.current = false;\n\n const wsUrl = `${currentSession.websocket_url}?token=${currentSession.access_token}`;\n console.log('[AegisChat] Creating WebSocket connection to:', wsUrl);\n const ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n console.log('[AegisChat] WebSocket connected');\n setIsConnected(true);\n reconnectAttempts.current = 0;\n onConnectionChange?.(true);\n\n pingInterval.current = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ type: 'ping' }));\n }\n }, PING_INTERVAL);\n\n if (activeChannelIdRef.current) {\n ws.send(JSON.stringify({ type: 'channel.join', payload: { channel_id: activeChannelIdRef.current } }));\n }\n };\n\n ws.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n handleWebSocketMessage(data);\n } catch (error) {\n console.error('[AegisChat] Failed to parse WebSocket message:', error);\n }\n };\n\n ws.onclose = () => {\n console.log('[AegisChat] WebSocket disconnected');\n setIsConnected(false);\n clearTimers();\n onConnectionChange?.(false);\n\n if (!isManualDisconnect.current && reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {\n const delay = Math.min(RECONNECT_INTERVAL * Math.pow(2, reconnectAttempts.current), MAX_RECONNECT_DELAY);\n console.log(`[AegisChat] Reconnecting in ${delay}ms...`);\n reconnectTimeout.current = setTimeout(() => {\n reconnectAttempts.current++;\n connectWebSocket();\n }, delay);\n }\n };\n\n ws.onerror = (error) => {\n console.error('[AegisChat] WebSocket error:', error);\n };\n\n wsRef.current = ws;\n }, [clearTimers, handleWebSocketMessage, onConnectionChange]);\n\n const connect = useCallback(async () => {\n console.log('[AegisChat] connect() called');\n if (!sessionRef.current && !config) {\n throw new Error('Either config or initialSession must be provided');\n }\n if (sessionRef.current) {\n console.log('[AegisChat] Session exists, calling connectWebSocket directly');\n connectWebSocket();\n return;\n }\n\n try {\n setIsConnecting(true);\n console.log('[AegisChat] Fetching chat session...');\n const result = await chatApi.connect({ role, client_id: clientId });\n console.log('[AegisChat] Chat session received:', result);\n setSession(result.data);\n setIsConnecting(false);\n } catch (error) {\n console.error('[AegisChat] Failed to get chat session:', error);\n setIsConnecting(false);\n throw error;\n }\n }, [role, clientId, connectWebSocket]);\n\n const disconnect = useCallback(() => {\n isManualDisconnect.current = true;\n clearTimers();\n if (wsRef.current) {\n wsRef.current.close();\n wsRef.current = null;\n }\n setIsConnected(false);\n setSession(null);\n setChannels([]);\n setMessages([]);\n }, [clearTimers]);\n\n const refreshChannels = useCallback(async () => {\n const currentSession = sessionRef.current;\n if (!currentSession) return;\n\n setIsLoadingChannels(true);\n try {\n const response = await channelsApi.list({});\n setChannels(response.data.channels || []);\n } catch (error) {\n console.error('[AegisChat] Failed to fetch channels:', error);\n } finally {\n setIsLoadingChannels(false);\n }\n }, []);\n\n const selectChannel = useCallback(async (channelId: string) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n setActiveChannelId(channelId);\n setMessages([]);\n setHasMoreMessages(true);\n oldestMessageId.current = null;\n\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n if (currentActiveChannelId) {\n wsRef.current.send(JSON.stringify({ type: 'channel.leave', payload: { channel_id: currentActiveChannelId } }));\n }\n wsRef.current.send(JSON.stringify({ type: 'channel.join', payload: { channel_id: channelId } }));\n }\n\n setIsLoadingMessages(true);\n try {\n const response = await fetchFromComms<MessagesResponse>(`/channels/${channelId}/messages?limit=50`);\n setMessages(response.messages || []);\n setHasMoreMessages(response.has_more);\n if (response.oldest_id) {\n oldestMessageId.current = response.oldest_id;\n }\n\n await markAsRead(channelId);\n\n setChannels((prev) => prev.map((ch) => (ch.id === channelId ? { ...ch, unread_count: 0 } : ch)));\n } catch (error) {\n console.error('[AegisChat] Failed to load messages:', error);\n setMessages([]);\n } finally {\n setIsLoadingMessages(false);\n }\n }, [setActiveChannelId, fetchFromComms]);\n\n const markAsRead = useCallback(async (channelId: string) => {\n try {\n await fetchFromComms(`/channels/${channelId}/read`, { method: 'POST' });\n } catch (error) {\n console.error('[AegisChat] Failed to mark as read:', error);\n }\n }, [fetchFromComms]);\n\n const loadMoreMessages = useCallback(async () => {\n if (!activeChannelId || !hasMoreMessages || isLoadingMessages) return;\n\n setIsLoadingMessages(true);\n try {\n const params = oldestMessageId.current ? `?before=${oldestMessageId.current}&limit=50` : '?limit=50';\n const response = await fetchFromComms<MessagesResponse>(`/channels/${activeChannelId}/messages${params}`);\n setMessages((prev) => [...(response.messages || []), ...prev]);\n setHasMoreMessages(response.has_more);\n if (response.oldest_id) {\n oldestMessageId.current = response.oldest_id;\n }\n } catch (error) {\n console.error('[AegisChat] Failed to load more messages:', error);\n } finally {\n setIsLoadingMessages(false);\n }\n }, [activeChannelId, hasMoreMessages, isLoadingMessages, fetchFromComms]);\n\n const sendMessage = useCallback(async (\n content: string,\n msgOptions: { type?: string; parent_id?: string; metadata?: Record<string, unknown> } = {}\n ) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n const currentSession = sessionRef.current;\n if (!currentActiveChannelId || !content.trim() || !currentSession) return;\n\n const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const trimmedContent = content.trim();\n\n const optimisticMessage: Message = {\n id: tempId,\n tempId,\n channel_id: currentActiveChannelId,\n sender_id: currentSession.comms_user_id,\n content: trimmedContent,\n type: (msgOptions.type as Message['type']) || 'text',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n status: 'sending',\n metadata: msgOptions.metadata || {},\n };\n\n setMessages((prev) => [...prev, optimisticMessage]);\n\n const now = new Date().toISOString();\n setChannels((prev) => {\n const updated = prev.map((ch) =>\n ch.id === currentActiveChannelId\n ? {\n ...ch,\n last_message: {\n id: tempId,\n content: trimmedContent,\n created_at: now,\n sender: { id: currentSession.comms_user_id, display_name: 'You', status: 'online' as const },\n },\n }\n : ch\n );\n return updated.sort((a, b) => {\n const timeA = a.last_message?.created_at || '';\n const timeB = b.last_message?.created_at || '';\n return timeB.localeCompare(timeA);\n });\n });\n\n try {\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({ content: trimmedContent, type: msgOptions.type || 'text', parent_id: msgOptions.parent_id, metadata: msgOptions.metadata }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to send message:', error);\n setMessages((prev) =>\n prev.map((m) =>\n m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m\n )\n );\n throw error;\n }\n }, [fetchFromComms]);\n\n const uploadFile = useCallback(async (file: File): Promise<FileAttachment | null> => {\n const currentSession = sessionRef.current;\n if (!currentSession) return null;\n\n const fileId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n\n setUploadProgress((prev) => [...prev, { fileId, fileName: file.name, progress: 0, status: 'pending' }]);\n\n try {\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: 'uploading', progress: 10 } : p));\n\n const uploadUrlResponse = await fetchFromComms<{ upload_url: string; file_id: string; expires_at: string }>('/files/upload-url', {\n method: 'POST',\n body: JSON.stringify({ file_name: file.name, file_type: file.type || 'application/octet-stream', file_size: file.size }),\n });\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, fileId: uploadUrlResponse.file_id, progress: 30 } : p));\n\n const uploadResponse = await fetch(uploadUrlResponse.upload_url, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n\n if (!uploadResponse.ok) throw new Error(`Upload failed: ${uploadResponse.statusText}`);\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: 'confirming', progress: 70 } : p));\n\n const confirmResponse = await fetchFromComms<{ file: FileAttachment }>('/files', {\n method: 'POST',\n body: JSON.stringify({ file_id: uploadUrlResponse.file_id }),\n });\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: 'complete', progress: 100 } : p));\n setTimeout(() => setUploadProgress((prev) => prev.filter((p) => p.fileId !== uploadUrlResponse.file_id)), 2000);\n\n return confirmResponse.file;\n } catch (error) {\n console.error('[AegisChat] Failed to upload file:', error);\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: 'error', error: error instanceof Error ? error.message : 'Upload failed' } : p));\n setTimeout(() => setUploadProgress((prev) => prev.filter((p) => p.fileId !== fileId)), 5000);\n return null;\n }\n }, [fetchFromComms]);\n\n const sendMessageWithFiles = useCallback(async (\n content: string,\n files: File[],\n msgOptions: { type?: string; parent_id?: string; metadata?: Record<string, unknown> } = {}\n ) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n const currentSession = sessionRef.current;\n if (!currentActiveChannelId || (!content.trim() && files.length === 0) || !currentSession) return;\n\n const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const trimmedContent = content.trim();\n\n const optimisticMessage: Message = {\n id: tempId,\n tempId,\n channel_id: currentActiveChannelId,\n sender_id: currentSession.comms_user_id,\n content: trimmedContent || `Uploading ${files.length} file(s)...`,\n type: 'file',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n status: 'sending',\n metadata: { ...msgOptions.metadata, files: files.map((f) => ({ id: `temp-${f.name}`, filename: f.name, mime_type: f.type, size: f.size, url: '' })) },\n };\n\n setMessages((prev) => [...prev, optimisticMessage]);\n\n try {\n const uploadedFiles: FileAttachment[] = [];\n for (const file of files) {\n const attachment = await uploadFile(file);\n if (attachment) uploadedFiles.push(attachment);\n }\n\n const messageType = uploadedFiles.length > 0 && !trimmedContent ? 'file' : 'text';\n\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({\n content: trimmedContent || (uploadedFiles.length > 0 ? `Shared ${uploadedFiles.length} file(s)` : ''),\n type: msgOptions.type || messageType,\n parent_id: msgOptions.parent_id,\n metadata: { ...msgOptions.metadata, files: uploadedFiles },\n file_ids: uploadedFiles.map((f) => f.id),\n }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to send message with files:', error);\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m));\n throw error;\n }\n }, [fetchFromComms, uploadFile]);\n\n const stopTyping = useCallback(() => {\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!currentActiveChannelId || !wsRef.current) return;\n wsRef.current.send(JSON.stringify({ type: 'typing.stop', payload: { channel_id: currentActiveChannelId } }));\n if (typingTimeout.current) {\n clearTimeout(typingTimeout.current);\n typingTimeout.current = null;\n }\n }, []);\n\n const startTyping = useCallback(() => {\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!currentActiveChannelId || !wsRef.current) return;\n wsRef.current.send(JSON.stringify({ type: 'typing.start', payload: { channel_id: currentActiveChannelId } }));\n if (typingTimeout.current) clearTimeout(typingTimeout.current);\n typingTimeout.current = setTimeout(stopTyping, TYPING_TIMEOUT);\n }, [stopTyping]);\n\n const createDMWithUser = useCallback(async (userId: string): Promise<string | null> => {\n try {\n const channel = await fetchFromComms<{ id: string }>('/channels/dm', {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n });\n await refreshChannels();\n return channel.id;\n } catch (error) {\n console.error('[AegisChat] Failed to create DM:', error);\n return null;\n }\n }, [fetchFromComms, refreshChannels]);\n\n const retryMessage = useCallback(async (tempId: string) => {\n const failedMessage = messages.find((m) => m.tempId === tempId && m.status === 'failed');\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!failedMessage || !currentActiveChannelId) return;\n\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'sending', errorMessage: undefined } : m));\n\n try {\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({ content: failedMessage.content, type: failedMessage.type, metadata: failedMessage.metadata }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to retry message:', error);\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m));\n }\n }, [messages, fetchFromComms]);\n\n const deleteFailedMessage = useCallback((tempId: string) => {\n setMessages((prev) => prev.filter((m) => m.tempId !== tempId));\n }, []);\n\n // Effects\n useEffect(() => {\n connect();\n }, []);\n\n useEffect(() => {\n if (session && !isConnected && !isConnecting && autoConnect) {\n connectWebSocket();\n }\n }, [session, isConnected, isConnecting, autoConnect, connectWebSocket]);\n\n useEffect(() => {\n if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {\n wsRef.current.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n handleWebSocketMessage(data);\n } catch (error) {\n console.error('[AegisChat] Failed to parse WebSocket message:', error);\n }\n };\n }\n }, [handleWebSocketMessage]);\n\n useEffect(() => {\n if (isConnected && channels.length === 0) {\n refreshChannels();\n }\n }, [isConnected, channels.length, refreshChannels]);\n\n useEffect(() => {\n const storedActiveChannel = getActiveChannelId();\n if (storedActiveChannel && !activeChannelId) {\n selectChannel(storedActiveChannel);\n }\n }, [getActiveChannelId, activeChannelId, selectChannel]);\n\n useEffect(() => {\n return () => {\n clearTimers();\n if (typingTimeout.current) clearTimeout(typingTimeout.current);\n if (wsRef.current) {\n isManualDisconnect.current = true;\n wsRef.current.close();\n wsRef.current = null;\n }\n };\n }, [clearTimers]);\n\n return {\n session,\n isConnected,\n isConnecting,\n channels,\n messages,\n activeChannelId,\n typingUsers: activeChannelId ? typingUsers[activeChannelId] || [] : [],\n isLoadingChannels,\n isLoadingMessages,\n hasMoreMessages,\n uploadProgress,\n connect,\n disconnect,\n selectChannel,\n sendMessage,\n sendMessageWithFiles,\n uploadFile,\n loadMoreMessages,\n startTyping,\n stopTyping,\n refreshChannels,\n createDMWithUser,\n retryMessage,\n deleteFailedMessage,\n markAsRead,\n };\n}\n\nexport default useChat;\n","// ============================================================================\n// AegisChat React SDK - API Service\n// ============================================================================\n\nimport type {\n ApiResponse,\n ChatSession,\n ChatConnectParams,\n Channel,\n ChannelListItem,\n Message,\n MessagesResponse,\n UserSummary,\n ReactionSummary,\n FileAttachment,\n UploadUrlResponse,\n} from '../types';\n\nlet baseUrl = '';\nlet getAccessToken: () => Promise<string> | string = () => '';\nlet onUnauthorized: (() => void) | undefined;\n\nexport function configureApiClient(config: {\n baseUrl: string;\n getAccessToken: () => Promise<string> | string;\n onUnauthorized?: () => void;\n}): void {\n baseUrl = config.baseUrl;\n getAccessToken = config.getAccessToken;\n onUnauthorized = config.onUnauthorized;\n}\n\nasync function fetchWithAuth<T>(\n path: string,\n options: RequestInit = {}\n): Promise<T> {\n const token = await (typeof getAccessToken === 'function' \n ? getAccessToken() \n : getAccessToken);\n\n const response = await fetch(`${baseUrl}${path}`, {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n ...options.headers,\n },\n });\n\n if (response.status === 401) {\n onUnauthorized?.();\n throw new Error('Unauthorized');\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n return response.json();\n}\n\nexport const chatApi = {\n /**\n * Connect to chat session\n */\n async connect(\n params: ChatConnectParams,\n signal?: AbortSignal\n ): Promise<ApiResponse<ChatSession>> {\n return fetchWithAuth('/api/v1/chat/connect', {\n method: 'POST',\n body: JSON.stringify(params),\n signal,\n });\n },\n\n /**\n * Refresh access token\n */\n async refreshToken(\n refreshToken: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ access_token: string; expires_in: number }>> {\n return fetchWithAuth('/api/v1/chat/refresh', {\n method: 'POST',\n body: JSON.stringify({ refresh_token: refreshToken }),\n signal,\n });\n },\n};\n\nexport const channelsApi = {\n /**\n * List channels\n */\n async list(\n options: { type?: string; limit?: number } = {},\n signal?: AbortSignal\n ): Promise<ApiResponse<{ channels: ChannelListItem[] }>> {\n const params = new URLSearchParams();\n if (options.type) params.append('type', options.type);\n if (options.limit) params.append('limit', String(options.limit));\n const query = params.toString() ? `?${params.toString()}` : '';\n return fetchWithAuth(`/api/v1/channels${query}`, { signal });\n },\n\n /**\n * Get channel by ID\n */\n async get(channelId: string, signal?: AbortSignal): Promise<ApiResponse<Channel>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}`, { signal });\n },\n\n /**\n * Get or create DM channel\n */\n async getOrCreateDM(\n userId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth('/api/v1/channels/dm', {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n signal,\n });\n },\n\n /**\n * Create channel\n */\n async create(\n data: { name: string; type?: string; description?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth('/api/v1/channels', {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Mark channel as read\n */\n async markAsRead(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ unread_count: number }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/read`, {\n method: 'POST',\n signal,\n });\n },\n\n /**\n * Get channel members\n */\n async getMembers(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ members: UserSummary[] }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/members`, { signal });\n },\n\n /**\n * Update channel\n */\n async update(\n channelId: string,\n data: { name?: string; description?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n signal,\n });\n },\n};\n\nexport const messagesApi = {\n /**\n * List messages in a channel\n */\n async list(\n channelId: string,\n options: { limit?: number; before?: string } = {},\n signal?: AbortSignal\n ): Promise<ApiResponse<MessagesResponse>> {\n const params = new URLSearchParams();\n if (options.limit) params.append('limit', String(options.limit));\n if (options.before) params.append('before', options.before);\n const query = params.toString() ? `?${params.toString()}` : '';\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages${query}`, { signal });\n },\n\n /**\n * Send a message\n */\n async send(\n channelId: string,\n data: { content: string; type?: string; parent_id?: string; metadata?: Record<string, unknown>; file_ids?: string[] },\n signal?: AbortSignal\n ): Promise<ApiResponse<Message>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages`, {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Update a message\n */\n async update(\n channelId: string,\n messageId: string,\n data: { content?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Message>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Delete a message\n */\n async delete(\n channelId: string,\n messageId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {\n method: 'DELETE',\n signal,\n });\n },\n\n /**\n * Mark messages as delivered\n */\n async markDelivered(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/delivered`, {\n method: 'POST',\n signal,\n });\n },\n\n /**\n * Mark messages as read\n */\n async markRead(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/read`, {\n method: 'POST',\n signal,\n });\n },\n};\n\nexport const reactionsApi = {\n /**\n * Add reaction to a message\n */\n async add(\n channelId: string,\n messageId: string,\n emoji: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}/reactions`, {\n method: 'POST',\n body: JSON.stringify({ emoji }),\n signal,\n });\n },\n\n /**\n * Remove reaction from a message\n */\n async remove(\n channelId: string,\n messageId: string,\n emoji: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {\n return fetchWithAuth(\n `/api/v1/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,\n { method: 'DELETE', signal }\n );\n },\n};\n\nexport const filesApi = {\n /**\n * Get upload URL\n */\n async getUploadUrl(\n data: { file_name: string; file_type: string; file_size: number },\n signal?: AbortSignal\n ): Promise<ApiResponse<UploadUrlResponse>> {\n return fetchWithAuth('/api/v1/files/upload-url', {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Confirm file upload\n */\n async confirm(\n fileId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ file: FileAttachment }>> {\n return fetchWithAuth('/api/v1/files', {\n method: 'POST',\n body: JSON.stringify({ file_id: fileId }),\n signal,\n });\n },\n\n /**\n * Get download URL\n */\n async getDownloadUrl(\n fileId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ url: string; expires_at: string }>> {\n return fetchWithAuth(`/api/v1/files/${fileId}/download`, { signal });\n },\n};\n\nexport const usersApi = {\n /**\n * Search users\n */\n async search(\n query: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ users: UserSummary[] }>> {\n return fetchWithAuth(`/api/v1/users/search?q=${encodeURIComponent(query)}`, { signal });\n },\n\n /**\n * Get user by ID\n */\n async get(userId: string, signal?: AbortSignal): Promise<ApiResponse<UserSummary>> {\n return fetchWithAuth(`/api/v1/users/${userId}`, { signal });\n },\n};\n\nexport default {\n chatApi,\n channelsApi,\n messagesApi,\n reactionsApi,\n filesApi,\n usersApi,\n configureApiClient,\n};\n","// ============================================================================\n// AegisChat React SDK - useAutoRead Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { channelsApi } from '../services/api';\n\nconst SESSION_STORAGE_KEY = '@aegischat/activeChannel';\n\nexport interface UseAutoReadOptions {\n onMarkAsRead?: (channelId: string) => void;\n}\n\nexport interface UseAutoReadReturn {\n markAsRead: (channelId: string) => Promise<void>;\n markAllAsRead: () => Promise<void>;\n isFocused: boolean;\n}\n\nexport function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn {\n const [isFocused, setIsFocused] = useState(false);\n\n useEffect(() => {\n setIsFocused(typeof document !== 'undefined' && document.hasFocus());\n\n const handleFocus = () => setIsFocused(true);\n const handleBlur = () => setIsFocused(false);\n\n window.addEventListener('focus', handleFocus);\n window.addEventListener('blur', handleBlur);\n\n return () => {\n window.removeEventListener('focus', handleFocus);\n window.removeEventListener('blur', handleBlur);\n };\n }, []);\n\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n const activeChannelId = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (activeChannelId) {\n markAsRead(activeChannelId);\n }\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => document.removeEventListener('visibilitychange', handleVisibilityChange);\n }, []);\n\n const markAsRead = useCallback(async (channelId: string) => {\n if (!isFocused) return;\n try {\n await channelsApi.markAsRead(channelId);\n options.onMarkAsRead?.(channelId);\n } catch (error) {\n console.error('[AegisChat] useAutoRead: Failed to mark as read:', error);\n }\n }, [isFocused, options.onMarkAsRead]);\n\n const markAllAsRead = useCallback(async () => {\n if (!isFocused) return;\n try {\n const response = await channelsApi.list({});\n const channels = response.data.channels || [];\n await Promise.all(\n channels.filter((ch) => ch.unread_count > 0).map((ch) => channelsApi.markAsRead(ch.id))\n );\n } catch (error) {\n console.error('[AegisChat] useAutoRead: Failed to mark all as read:', error);\n }\n }, [isFocused]);\n\n return { markAsRead, markAllAsRead, isFocused };\n}\n\nexport default useAutoRead;\n","// ============================================================================\n// AegisChat React SDK - useChannels Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { channelsApi } from '../services/api';\nimport type { ChannelListItem, Channel } from '../types';\n\nexport interface UseChannelsOptions {\n type?: 'direct' | 'public' | 'private';\n limit?: number;\n autoFetch?: boolean;\n onChannelCreated?: (channel: Channel) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseChannelsReturn {\n channels: ChannelListItem[];\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n getOrCreateDM: (userId: string) => Promise<Channel>;\n markAsRead: (channelId: string) => Promise<void>;\n}\n\nexport function useChannels(options: UseChannelsOptions = {}): UseChannelsReturn {\n const { type, limit = 20, autoFetch = true, onChannelCreated, onError } = options;\n\n const [channels, setChannels] = useState<ChannelListItem[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const fetchChannels = useCallback(async (signal?: AbortSignal) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const response = await channelsApi.list({ type, limit }, signal);\n if (signal?.aborted) return;\n setChannels(response.data.channels);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') return;\n const error = err instanceof Error ? err : new Error('Failed to fetch channels');\n setError(error);\n onError?.(error);\n } finally {\n if (!signal?.aborted) setIsLoading(false);\n }\n }, [type, limit, onError]);\n\n const refetch = useCallback(async () => {\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n await fetchChannels(controller.signal);\n }, [fetchChannels]);\n\n const getOrCreateDM = useCallback(async (userId: string): Promise<Channel> => {\n try {\n const response = await channelsApi.getOrCreateDM(userId);\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n fetchChannels(controller.signal);\n onChannelCreated?.(response.data);\n return response.data;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create DM');\n onError?.(error);\n throw error;\n }\n }, [fetchChannels, onChannelCreated, onError]);\n\n const markAsRead = useCallback(async (channelId: string): Promise<void> => {\n try {\n await channelsApi.markAsRead(channelId);\n setChannels((prev) => prev.map((ch) => ch.id === channelId ? { ...ch, unread_count: 0 } : ch));\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to mark as read');\n onError?.(error);\n throw error;\n }\n }, [onError]);\n\n useEffect(() => {\n if (autoFetch) {\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n fetchChannels(controller.signal);\n return () => controller.abort();\n }\n }, [autoFetch, fetchChannels]);\n\n return { channels, isLoading, error, refetch, getOrCreateDM, markAsRead };\n}\n\nexport default useChannels;\n","// ============================================================================\n// AegisChat React SDK - useMessages Hook\n// ============================================================================\n\nimport { useCallback, useState } from 'react';\nimport { messagesApi } from '../services/api';\nimport type { Message, MessagesResponse } from '../types';\n\nexport interface UseMessagesOptions {\n channelId: string;\n}\n\nexport interface UseMessagesReturn {\n messages: Message[];\n isLoading: boolean;\n hasMore: boolean;\n sendMessage: (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\nexport function useMessages(_options: UseMessagesOptions): UseMessagesReturn {\n const [messages, setMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasMore, setHasMore] = useState(true);\n\n const sendMessage = useCallback(async (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => {\n // Implementation would go here\n }, []);\n\n const loadMore = useCallback(async () => {\n // Implementation would go here\n }, []);\n\n return { messages, isLoading, hasMore, sendMessage, loadMore };\n}\n\nexport default useMessages;\n","// ============================================================================\n// AegisChat React SDK - useTypingIndicator Hook\n// ============================================================================\n\nimport { useCallback, useState } from 'react';\nimport type { TypingUser } from '../types';\n\nexport interface UseTypingIndicatorOptions {\n channelId: string;\n ws?: WebSocket | null;\n}\n\nexport interface UseTypingIndicatorReturn {\n typingUsers: TypingUser[];\n startTyping: () => void;\n stopTyping: () => void;\n}\n\nexport function useTypingIndicator(_options: UseTypingIndicatorOptions): UseTypingIndicatorReturn {\n const [typingUsers, setTypingUsers] = useState<TypingUser[]>([]);\n\n const startTyping = useCallback(() => {}, []);\n const stopTyping = useCallback(() => {}, []);\n\n return { typingUsers, startTyping, stopTyping };\n}\n\nexport default useTypingIndicator;\n","// ============================================================================\n// AegisChat React SDK - useReactions Hook\n// ============================================================================\n\nimport { useState } from 'react';\nimport type { ReactionSummary } from '../types';\n\nexport interface UseReactionsOptions {\n channelId: string;\n messageId: string;\n}\n\nexport interface UseReactionsReturn {\n reactions: ReactionSummary[];\n addReaction: (emoji: string) => Promise<void>;\n removeReaction: (emoji: string) => Promise<void>;\n}\n\nexport function useReactions(_options: UseReactionsOptions): UseReactionsReturn {\n const [reactions, setReactions] = useState<ReactionSummary[]>([]);\n\n const addReaction = async (_emoji: string) => {};\n const removeReaction = async (_emoji: string) => {};\n\n return { reactions, addReaction, removeReaction };\n}\n\nexport default useReactions;\n","// ============================================================================\n// AegisChat React SDK - useFileUpload Hook\n// ============================================================================\n\nimport { useState } from 'react';\nimport type { FileAttachment, UploadProgress } from '../types';\n\nexport interface UseFileUploadOptions {\n channelId: string;\n}\n\nexport interface UseFileUploadReturn {\n uploadProgress: UploadProgress[];\n upload: (file: File) => Promise<FileAttachment | null>;\n}\n\nexport function useFileUpload(_options: UseFileUploadOptions): UseFileUploadReturn {\n const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);\n\n const upload = async (_file: File): Promise<FileAttachment | null> => null;\n\n return { uploadProgress, upload };\n}\n\nexport default useFileUpload;\n","// ============================================================================\n// AegisChat React SDK - useMentions Hook\n// ============================================================================\n\nexport interface UseMentionsOptions {}\n\nexport interface UseMentionsReturn {\n parseMentions: (content: string) => string[];\n highlightMentions: (content: string) => string;\n isUserMentioned: (content: string, userId: string) => boolean;\n}\n\nexport function useMentions(_options: UseMentionsOptions = {}): UseMentionsReturn {\n const parseMentions = (content: string): string[] => {\n const mentionRegex = /@\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n const mentions: string[] = [];\n let match;\n while ((match = mentionRegex.exec(content)) !== null) {\n mentions.push(match[2]);\n }\n return mentions;\n };\n\n const highlightMentions = (content: string): string => {\n return content.replace(/@\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<span class=\"mention\">@$1</span>');\n };\n\n const isUserMentioned = (content: string, userId: string): boolean => {\n const mentions = parseMentions(content);\n return mentions.includes(userId);\n };\n\n return { parseMentions, highlightMentions, isUserMentioned };\n}\n\nexport default useMentions;\n"],"mappings":"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","names":["import_react","SESSION_STORAGE_KEY","import_react","error","import_react","import_react","import_react","import_react"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/hooks/useChat.ts","../src/services/api.ts","../src/hooks/useAutoRead.ts","../src/hooks/useChannels.ts","../src/hooks/useMessages.ts","../src/hooks/useTypingIndicator.ts","../src/hooks/useReactions.ts","../src/hooks/useFileUpload.ts","../src/hooks/useMentions.ts"],"sourcesContent":["// ============================================================================\n// AegisChat React SDK - Main Entry Point\n// ============================================================================\n\nexport { useChat } from './hooks/useChat';\nexport type { UseChatOptions, UseChatReturn } from './hooks/useChat';\n\nexport { useAutoRead } from './hooks/useAutoRead';\nexport type { UseAutoReadOptions, UseAutoReadReturn } from './hooks/useAutoRead';\n\nexport { useChannels } from './hooks/useChannels';\nexport type { UseChannelsOptions, UseChannelsReturn } from './hooks/useChannels';\n\nexport { useMessages } from './hooks/useMessages';\nexport type { UseMessagesOptions, UseMessagesReturn } from './hooks/useMessages';\n\nexport { useTypingIndicator } from './hooks/useTypingIndicator';\nexport type { UseTypingIndicatorOptions } from './hooks/useTypingIndicator';\n\nexport { useReactions } from './hooks/useReactions';\nexport type { UseReactionsOptions } from './hooks/useReactions';\n\nexport { useFileUpload } from './hooks/useFileUpload';\nexport type { UseFileUploadOptions } from './hooks/useFileUpload';\n\nexport { useMentions } from './hooks/useMentions';\nexport type { UseMentionsOptions } from './hooks/useMentions';\n\nexport {\n chatApi,\n channelsApi,\n messagesApi,\n reactionsApi,\n filesApi,\n usersApi,\n configureApiClient,\n} from './services/api';\n\nexport type {\n AegisConfig,\n ChatSession,\n ChatConnectParams,\n UserSummary,\n UserStatus,\n Channel,\n ChannelListItem,\n ChannelType,\n Message,\n MessageSummary,\n MessageType,\n MessageStatus,\n MessageMetadata,\n FileAttachment,\n TypingUser,\n TypingEvent,\n ReactionSummary,\n ReactionEvent,\n UploadProgress,\n WebSocketMessage,\n WebSocketStatus,\n ApiResponse,\n ApiError,\n PaginationParams,\n PaginationMeta,\n PaginatedResponse,\n MessagesResponse,\n ChannelsResponse,\n} from './types';\n","// ============================================================================\n// AegisChat React SDK - useChat Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { chatApi, channelsApi, messagesApi, filesApi, configureApiClient } from '../services/api';\nimport type {\n AegisConfig,\n ChatSession,\n ChannelListItem,\n Message,\n MessagesResponse,\n TypingUser,\n UserSummary,\n FileAttachment,\n UploadProgress,\n MessageSummary,\n} from '../types';\n\nconst TYPING_TIMEOUT = 3000;\nconst RECONNECT_INTERVAL = 3000;\nconst MAX_RECONNECT_ATTEMPTS = 5;\nconst MAX_RECONNECT_DELAY = 30000;\nconst PING_INTERVAL = 30000;\nconst SESSION_STORAGE_KEY = '@aegischat/activeChannel';\n\nexport interface UseChatOptions {\n config?: AegisConfig;\n role: 'lawyer' | 'client';\n clientId?: string;\n \n initialSession?: ChatSession | null;\n autoConnect?: boolean;\n onMessage?: (message: Message) => void;\n onTyping?: (channelId: string, user: TypingUser) => void;\n onConnectionChange?: (connected: boolean) => void;\n}\n\nexport interface UseChatReturn {\n session: ChatSession | null;\n isConnected: boolean;\n isConnecting: boolean;\n channels: ChannelListItem[];\n messages: Message[];\n activeChannelId: string | null;\n typingUsers: TypingUser[];\n isLoadingChannels: boolean;\n isLoadingMessages: boolean;\n hasMoreMessages: boolean;\n uploadProgress: UploadProgress[];\n connect: () => Promise<void>;\n disconnect: () => void;\n selectChannel: (channelId: string) => void;\n sendMessage: (content: string, options?: { type?: string; parent_id?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n sendMessageWithFiles: (content: string, files: File[], options?: { type?: string; parent_id?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n uploadFile: (file: File) => Promise<FileAttachment | null>;\n loadMoreMessages: () => Promise<void>;\n startTyping: () => void;\n stopTyping: () => void;\n refreshChannels: () => Promise<void>;\n createDMWithUser: (userId: string) => Promise<string | null>;\n retryMessage: (tempId: string) => Promise<void>;\n deleteFailedMessage: (tempId: string) => void;\n markAsRead: (channelId: string) => Promise<void>;\n}\n\nexport function useChat(options: UseChatOptions): UseChatReturn {\n const { config, role, clientId, initialSession, autoConnect = true, onMessage, onTyping, onConnectionChange } = options;\n\n const [session, setSession] = useState<ChatSession | null>(initialSession ?? null);\n const [isConnected, setIsConnected] = useState(false);\n const [isConnecting, setIsConnecting] = useState(false);\n const [activeChannelId, setActiveChannelIdState] = useState<string | null>(null);\n const [channels, setChannels] = useState<ChannelListItem[]>([]);\n const [messages, setMessages] = useState<Message[]>([]);\n const [typingUsers, setTypingUsers] = useState<Record<string, TypingUser[]>>({});\n const [isLoadingChannels, setIsLoadingChannels] = useState(false);\n const [isLoadingMessages, setIsLoadingMessages] = useState(false);\n const [hasMoreMessages, setHasMoreMessages] = useState(true);\n const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);\n\n const wsRef = useRef<WebSocket | null>(null);\n const reconnectAttempts = useRef(0);\n const reconnectTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pingInterval = useRef<ReturnType<typeof setInterval> | null>(null);\n const typingTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isManualDisconnect = useRef(false);\n const oldestMessageId = useRef<string | null>(null);\n const activeChannelIdRef = useRef<string | null>(null);\n const configRef = useRef(config);\n const sessionRef = useRef<ChatSession | null>(initialSession ?? null);\n\n // Configure API client when initialSession is provided (so channelsApi/messagesApi/filesApi use correct baseUrl)\n if (initialSession && !config) {\n configureApiClient({\n baseUrl: initialSession.api_url,\n getAccessToken: async () => sessionRef.current?.access_token || \"\",\n });\n }\n\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n useEffect(() => {\n activeChannelIdRef.current = activeChannelId;\n }, [activeChannelId]);\n\n\n const getActiveChannelId = useCallback((): string | null => {\n if (typeof window === 'undefined') return null;\n return sessionStorage.getItem(SESSION_STORAGE_KEY);\n }, []);\n\n const setActiveChannelId = useCallback((id: string | null) => {\n setActiveChannelIdState(id);\n if (typeof window !== 'undefined') {\n if (id) {\n sessionStorage.setItem(SESSION_STORAGE_KEY, id);\n } else {\n sessionStorage.removeItem(SESSION_STORAGE_KEY);\n }\n }\n }, []);\n\n const fetchFromComms = useCallback(async <T>(path: string, fetchOptions: RequestInit = {}): Promise<T> => {\n const currentSession = sessionRef.current;\n if (!currentSession) {\n throw new Error('Chat session not initialized');\n }\n\n const response = await fetch(`${currentSession.api_url}${path}`, {\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${currentSession.access_token}`,\n ...fetchOptions.headers,\n },\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n const data = await response.json();\n return data.data || data;\n }, []);\n\n const clearTimers = useCallback(() => {\n if (reconnectTimeout.current) {\n clearTimeout(reconnectTimeout.current);\n reconnectTimeout.current = null;\n }\n if (pingInterval.current) {\n clearInterval(pingInterval.current);\n pingInterval.current = null;\n }\n }, []);\n\n const handleWebSocketMessage = useCallback((data: { type: string; payload: unknown }) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n console.log('[AegisChat] WebSocket message received:', data.type, data);\n\n switch (data.type) {\n case 'message.new': {\n const newMessage = data.payload as Message;\n if (newMessage.channel_id === currentActiveChannelId) {\n setMessages((prev) => {\n const existingIndex = prev.findIndex(\n (m) => m.tempId && m.content === newMessage.content && m.status === 'sending'\n );\n if (existingIndex !== -1) {\n const updated = [...prev];\n updated[existingIndex] = { ...newMessage, status: 'sent' };\n return updated;\n }\n if (prev.some((m) => m.id === newMessage.id)) return prev;\n return [...prev, { ...newMessage, status: 'delivered' }];\n });\n onMessage?.(newMessage);\n }\n setChannels((prev) => {\n const updated = prev.map((ch) =>\n ch.id === newMessage.channel_id\n ? {\n ...ch,\n last_message: {\n id: newMessage.id,\n content: newMessage.content,\n created_at: newMessage.created_at,\n sender: { id: newMessage.sender_id, display_name: 'Unknown', status: 'online' as const },\n } as MessageSummary,\n unread_count: ch.id === currentActiveChannelId ? 0 : ch.unread_count + 1,\n }\n : ch\n );\n return updated.sort((a, b) => {\n const timeA = a.last_message?.created_at || '';\n const timeB = b.last_message?.created_at || '';\n return timeB.localeCompare(timeA);\n });\n });\n break;\n }\n case 'message.updated': {\n const updatedMessage = data.payload as Message;\n setMessages((prev) => prev.map((m) => (m.id === updatedMessage.id ? updatedMessage : m)));\n break;\n }\n case 'message.deleted': {\n const { message_id } = data.payload as { message_id: string };\n setMessages((prev) => prev.map((m) => (m.id === message_id ? { ...m, deleted: true } : m)));\n break;\n }\n case 'message.delivered':\n case 'message.read': {\n const { message_id, channel_id, status } = data.payload as { message_id: string; channel_id: string; status: string };\n if (channel_id === currentActiveChannelId) {\n setMessages((prev) =>\n prev.map((m) => (m.id === message_id ? { ...m, status: (status as Message['status']) || 'delivered' } : m))\n );\n }\n break;\n }\n case 'message.delivered.batch':\n case 'message.read.batch': {\n const { channel_id } = data.payload as { channel_id: string };\n if (channel_id === currentActiveChannelId) {\n setMessages((prev) =>\n prev.map((m) => (m.status === 'sent' || m.status === 'delivered' ? { ...m, status: data.type === 'message.delivered.batch' ? 'delivered' : 'read' } : m))\n );\n }\n break;\n }\n case 'typing.start': {\n const { channel_id, user } = data.payload as { channel_id: string; user: UserSummary };\n const typingUser: TypingUser = {\n id: user.id,\n displayName: user.display_name,\n avatarUrl: user.avatar_url,\n startedAt: Date.now(),\n };\n setTypingUsers((prev) => ({\n ...prev,\n [channel_id]: [...(prev[channel_id] || []).filter((u) => u.id !== user.id), typingUser],\n }));\n onTyping?.(channel_id, typingUser);\n break;\n }\n case 'typing.stop': {\n const { channel_id, user_id } = data.payload as { channel_id: string; user_id: string };\n setTypingUsers((prev) => ({\n ...prev,\n [channel_id]: (prev[channel_id] || []).filter((u) => u.id !== user_id),\n }));\n break;\n }\n case 'pong':\n break;\n default:\n console.log('[AegisChat] Unhandled message type:', data.type);\n }\n }, [onMessage, onTyping]);\n\n const connectWebSocket = useCallback(() => {\n const currentSession = sessionRef.current;\n if (!currentSession?.websocket_url || !currentSession?.access_token) {\n console.warn('[AegisChat] Cannot connect WebSocket - missing session or token');\n return;\n }\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n console.log('[AegisChat] WebSocket already open, skipping connection');\n return;\n }\n\n setIsConnecting(true);\n isManualDisconnect.current = false;\n\n const wsUrl = `${currentSession.websocket_url}?token=${currentSession.access_token}`;\n console.log('[AegisChat] Creating WebSocket connection to:', wsUrl);\n const ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n console.log('[AegisChat] WebSocket connected');\n setIsConnected(true);\n setIsConnecting(false);\n reconnectAttempts.current = 0;\n onConnectionChange?.(true);\n\n pingInterval.current = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ type: 'ping' }));\n }\n }, PING_INTERVAL);\n\n if (activeChannelIdRef.current) {\n ws.send(JSON.stringify({ type: 'channel.join', payload: { channel_id: activeChannelIdRef.current } }));\n }\n };\n\n ws.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n handleWebSocketMessage(data);\n } catch (error) {\n console.error('[AegisChat] Failed to parse WebSocket message:', error);\n }\n };\n\n ws.onclose = () => {\n console.log('[AegisChat] WebSocket disconnected');\n setIsConnected(false);\n setIsConnecting(false);\n clearTimers();\n onConnectionChange?.(false);\n\n if (!isManualDisconnect.current && reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {\n const delay = Math.min(RECONNECT_INTERVAL * Math.pow(2, reconnectAttempts.current), MAX_RECONNECT_DELAY);\n console.log(`[AegisChat] Reconnecting in ${delay}ms...`);\n reconnectTimeout.current = setTimeout(() => {\n reconnectAttempts.current++;\n connectWebSocket();\n }, delay);\n }\n };\n\n ws.onerror = (error) => {\n console.error('[AegisChat] WebSocket error:', error);\n };\n\n wsRef.current = ws;\n }, [clearTimers, handleWebSocketMessage, onConnectionChange]);\n\n const connect = useCallback(async () => {\n console.log('[AegisChat] connect() called');\n const targetSession = sessionRef.current ?? initialSession;\n if (!targetSession) {\n throw new Error('Either config or initialSession must be provided');\n }\n if (sessionRef.current) {\n console.log('[AegisChat] Session exists, calling connectWebSocket directly');\n connectWebSocket();\n return;\n }\n\n // If initialSession was provided but sessionRef wasn't set yet, use it directly\n console.log('[AegisChat] Using initialSession, calling connectWebSocket directly');\n connectWebSocket();\n }, [connectWebSocket]);\n\n const disconnect = useCallback(() => {\n isManualDisconnect.current = true;\n clearTimers();\n if (wsRef.current) {\n wsRef.current.close();\n wsRef.current = null;\n }\n setIsConnected(false);\n setSession(null);\n setChannels([]);\n setMessages([]);\n }, [clearTimers]);\n\n const refreshChannels = useCallback(async () => {\n const currentSession = sessionRef.current;\n if (!currentSession) return;\n\n setIsLoadingChannels(true);\n try {\n const response = await channelsApi.list({});\n setChannels(response.data.channels || []);\n } catch (error) {\n console.error('[AegisChat] Failed to fetch channels:', error);\n } finally {\n setIsLoadingChannels(false);\n }\n }, []);\n\n const selectChannel = useCallback(async (channelId: string) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n setActiveChannelId(channelId);\n setMessages([]);\n setHasMoreMessages(true);\n oldestMessageId.current = null;\n\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n if (currentActiveChannelId) {\n wsRef.current.send(JSON.stringify({ type: 'channel.leave', payload: { channel_id: currentActiveChannelId } }));\n }\n wsRef.current.send(JSON.stringify({ type: 'channel.join', payload: { channel_id: channelId } }));\n }\n\n setIsLoadingMessages(true);\n try {\n const response = await fetchFromComms<MessagesResponse>(`/channels/${channelId}/messages?limit=50`);\n setMessages(response.messages || []);\n setHasMoreMessages(response.has_more);\n if (response.oldest_id) {\n oldestMessageId.current = response.oldest_id;\n }\n\n await markAsRead(channelId);\n\n setChannels((prev) => prev.map((ch) => (ch.id === channelId ? { ...ch, unread_count: 0 } : ch)));\n } catch (error) {\n console.error('[AegisChat] Failed to load messages:', error);\n setMessages([]);\n } finally {\n setIsLoadingMessages(false);\n }\n }, [setActiveChannelId, fetchFromComms]);\n\n const markAsRead = useCallback(async (channelId: string) => {\n try {\n await fetchFromComms(`/channels/${channelId}/read`, { method: 'POST' });\n } catch (error) {\n console.error('[AegisChat] Failed to mark as read:', error);\n }\n }, [fetchFromComms]);\n\n const loadMoreMessages = useCallback(async () => {\n if (!activeChannelId || !hasMoreMessages || isLoadingMessages) return;\n\n setIsLoadingMessages(true);\n try {\n const params = oldestMessageId.current ? `?before=${oldestMessageId.current}&limit=50` : '?limit=50';\n const response = await fetchFromComms<MessagesResponse>(`/channels/${activeChannelId}/messages${params}`);\n setMessages((prev) => [...(response.messages || []), ...prev]);\n setHasMoreMessages(response.has_more);\n if (response.oldest_id) {\n oldestMessageId.current = response.oldest_id;\n }\n } catch (error) {\n console.error('[AegisChat] Failed to load more messages:', error);\n } finally {\n setIsLoadingMessages(false);\n }\n }, [activeChannelId, hasMoreMessages, isLoadingMessages, fetchFromComms]);\n\n const sendMessage = useCallback(async (\n content: string,\n msgOptions: { type?: string; parent_id?: string; metadata?: Record<string, unknown> } = {}\n ) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n const currentSession = sessionRef.current;\n if (!currentActiveChannelId || !content.trim() || !currentSession) return;\n\n const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const trimmedContent = content.trim();\n\n const optimisticMessage: Message = {\n id: tempId,\n tempId,\n channel_id: currentActiveChannelId,\n sender_id: currentSession.comms_user_id,\n content: trimmedContent,\n type: (msgOptions.type as Message['type']) || 'text',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n status: 'sending',\n metadata: msgOptions.metadata || {},\n };\n\n setMessages((prev) => [...prev, optimisticMessage]);\n\n const now = new Date().toISOString();\n setChannels((prev) => {\n const updated = prev.map((ch) =>\n ch.id === currentActiveChannelId\n ? {\n ...ch,\n last_message: {\n id: tempId,\n content: trimmedContent,\n created_at: now,\n sender: { id: currentSession.comms_user_id, display_name: 'You', status: 'online' as const },\n },\n }\n : ch\n );\n return updated.sort((a, b) => {\n const timeA = a.last_message?.created_at || '';\n const timeB = b.last_message?.created_at || '';\n return timeB.localeCompare(timeA);\n });\n });\n\n try {\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({ content: trimmedContent, type: msgOptions.type || 'text', parent_id: msgOptions.parent_id, metadata: msgOptions.metadata }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to send message:', error);\n setMessages((prev) =>\n prev.map((m) =>\n m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m\n )\n );\n throw error;\n }\n }, [fetchFromComms]);\n\n const uploadFile = useCallback(async (file: File): Promise<FileAttachment | null> => {\n const currentSession = sessionRef.current;\n if (!currentSession) return null;\n\n const fileId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n\n setUploadProgress((prev) => [...prev, { fileId, fileName: file.name, progress: 0, status: 'pending' }]);\n\n try {\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: 'uploading', progress: 10 } : p));\n\n const uploadUrlResponse = await fetchFromComms<{ upload_url: string; file_id: string; expires_at: string }>('/files/upload-url', {\n method: 'POST',\n body: JSON.stringify({ file_name: file.name, file_type: file.type || 'application/octet-stream', file_size: file.size }),\n });\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, fileId: uploadUrlResponse.file_id, progress: 30 } : p));\n\n const uploadResponse = await fetch(uploadUrlResponse.upload_url, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n\n if (!uploadResponse.ok) throw new Error(`Upload failed: ${uploadResponse.statusText}`);\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: 'confirming', progress: 70 } : p));\n\n const confirmResponse = await fetchFromComms<{ file: FileAttachment }>('/files', {\n method: 'POST',\n body: JSON.stringify({ file_id: uploadUrlResponse.file_id }),\n });\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: 'complete', progress: 100 } : p));\n setTimeout(() => setUploadProgress((prev) => prev.filter((p) => p.fileId !== uploadUrlResponse.file_id)), 2000);\n\n return confirmResponse.file;\n } catch (error) {\n console.error('[AegisChat] Failed to upload file:', error);\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: 'error', error: error instanceof Error ? error.message : 'Upload failed' } : p));\n setTimeout(() => setUploadProgress((prev) => prev.filter((p) => p.fileId !== fileId)), 5000);\n return null;\n }\n }, [fetchFromComms]);\n\n const sendMessageWithFiles = useCallback(async (\n content: string,\n files: File[],\n msgOptions: { type?: string; parent_id?: string; metadata?: Record<string, unknown> } = {}\n ) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n const currentSession = sessionRef.current;\n if (!currentActiveChannelId || (!content.trim() && files.length === 0) || !currentSession) return;\n\n const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const trimmedContent = content.trim();\n\n const optimisticMessage: Message = {\n id: tempId,\n tempId,\n channel_id: currentActiveChannelId,\n sender_id: currentSession.comms_user_id,\n content: trimmedContent || `Uploading ${files.length} file(s)...`,\n type: 'file',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n status: 'sending',\n metadata: { ...msgOptions.metadata, files: files.map((f) => ({ id: `temp-${f.name}`, filename: f.name, mime_type: f.type, size: f.size, url: '' })) },\n };\n\n setMessages((prev) => [...prev, optimisticMessage]);\n\n try {\n const uploadedFiles: FileAttachment[] = [];\n for (const file of files) {\n const attachment = await uploadFile(file);\n if (attachment) uploadedFiles.push(attachment);\n }\n\n const messageType = uploadedFiles.length > 0 && !trimmedContent ? 'file' : 'text';\n\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({\n content: trimmedContent || (uploadedFiles.length > 0 ? `Shared ${uploadedFiles.length} file(s)` : ''),\n type: msgOptions.type || messageType,\n parent_id: msgOptions.parent_id,\n metadata: { ...msgOptions.metadata, files: uploadedFiles },\n file_ids: uploadedFiles.map((f) => f.id),\n }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to send message with files:', error);\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m));\n throw error;\n }\n }, [fetchFromComms, uploadFile]);\n\n const stopTyping = useCallback(() => {\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!currentActiveChannelId || !wsRef.current) return;\n wsRef.current.send(JSON.stringify({ type: 'typing.stop', payload: { channel_id: currentActiveChannelId } }));\n if (typingTimeout.current) {\n clearTimeout(typingTimeout.current);\n typingTimeout.current = null;\n }\n }, []);\n\n const startTyping = useCallback(() => {\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!currentActiveChannelId || !wsRef.current) return;\n wsRef.current.send(JSON.stringify({ type: 'typing.start', payload: { channel_id: currentActiveChannelId } }));\n if (typingTimeout.current) clearTimeout(typingTimeout.current);\n typingTimeout.current = setTimeout(stopTyping, TYPING_TIMEOUT);\n }, [stopTyping]);\n\n const createDMWithUser = useCallback(async (userId: string): Promise<string | null> => {\n try {\n const channel = await fetchFromComms<{ id: string }>('/channels/dm', {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n });\n await refreshChannels();\n return channel.id;\n } catch (error) {\n console.error('[AegisChat] Failed to create DM:', error);\n return null;\n }\n }, [fetchFromComms, refreshChannels]);\n\n const retryMessage = useCallback(async (tempId: string) => {\n const failedMessage = messages.find((m) => m.tempId === tempId && m.status === 'failed');\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!failedMessage || !currentActiveChannelId) return;\n\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'sending', errorMessage: undefined } : m));\n\n try {\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({ content: failedMessage.content, type: failedMessage.type, metadata: failedMessage.metadata }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to retry message:', error);\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m));\n }\n }, [messages, fetchFromComms]);\n\n const deleteFailedMessage = useCallback((tempId: string) => {\n setMessages((prev) => prev.filter((m) => m.tempId !== tempId));\n }, []);\n\n // Note: connect() is called by the wrapper hook, not automatically.\n // This allows the wrapper to fetch the session externally first.\n\n useEffect(() => {\n if (session && !isConnected && !isConnecting && autoConnect) {\n connectWebSocket();\n }\n }, [session, isConnected, isConnecting, autoConnect, connectWebSocket]);\n\n useEffect(() => {\n if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {\n wsRef.current.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n handleWebSocketMessage(data);\n } catch (error) {\n console.error('[AegisChat] Failed to parse WebSocket message:', error);\n }\n };\n }\n }, [handleWebSocketMessage]);\n\n useEffect(() => {\n if (isConnected && channels.length === 0) {\n refreshChannels();\n }\n }, [isConnected, channels.length, refreshChannels]);\n\n useEffect(() => {\n const storedActiveChannel = getActiveChannelId();\n if (storedActiveChannel && !activeChannelId) {\n selectChannel(storedActiveChannel);\n }\n }, [getActiveChannelId, activeChannelId, selectChannel]);\n\n useEffect(() => {\n return () => {\n clearTimers();\n if (typingTimeout.current) clearTimeout(typingTimeout.current);\n if (wsRef.current) {\n isManualDisconnect.current = true;\n wsRef.current.close();\n wsRef.current = null;\n }\n };\n }, [clearTimers]);\n\n return {\n session,\n isConnected,\n isConnecting,\n channels,\n messages,\n activeChannelId,\n typingUsers: activeChannelId ? typingUsers[activeChannelId] || [] : [],\n isLoadingChannels,\n isLoadingMessages,\n hasMoreMessages,\n uploadProgress,\n connect,\n disconnect,\n selectChannel,\n sendMessage,\n sendMessageWithFiles,\n uploadFile,\n loadMoreMessages,\n startTyping,\n stopTyping,\n refreshChannels,\n createDMWithUser,\n retryMessage,\n deleteFailedMessage,\n markAsRead,\n };\n}\n\nexport default useChat;\n","// ============================================================================\n// AegisChat React SDK - API Service\n// ============================================================================\n\nimport type {\n ApiResponse,\n ChatSession,\n ChatConnectParams,\n Channel,\n ChannelListItem,\n Message,\n MessagesResponse,\n UserSummary,\n ReactionSummary,\n FileAttachment,\n UploadUrlResponse,\n} from '../types';\n\nlet baseUrl = '';\nlet getAccessToken: () => Promise<string> | string = () => '';\nlet onUnauthorized: (() => void) | undefined;\n\nexport function configureApiClient(config: {\n baseUrl: string;\n getAccessToken: () => Promise<string> | string;\n onUnauthorized?: () => void;\n}): void {\n baseUrl = config.baseUrl;\n getAccessToken = config.getAccessToken;\n onUnauthorized = config.onUnauthorized;\n}\n\nasync function fetchWithAuth<T>(\n path: string,\n options: RequestInit = {}\n): Promise<T> {\n const token = await (typeof getAccessToken === 'function' \n ? getAccessToken() \n : getAccessToken);\n\n const response = await fetch(`${baseUrl}${path}`, {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n ...options.headers,\n },\n });\n\n if (response.status === 401) {\n onUnauthorized?.();\n throw new Error('Unauthorized');\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n return response.json();\n}\n\nexport const chatApi = {\n /**\n * Connect to chat session\n */\n async connect(\n params: ChatConnectParams,\n signal?: AbortSignal\n ): Promise<ApiResponse<ChatSession>> {\n return fetchWithAuth('/api/v1/chat/connect', {\n method: 'POST',\n body: JSON.stringify(params),\n signal,\n });\n },\n\n /**\n * Refresh access token\n */\n async refreshToken(\n refreshToken: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ access_token: string; expires_in: number }>> {\n return fetchWithAuth('/api/v1/chat/refresh', {\n method: 'POST',\n body: JSON.stringify({ refresh_token: refreshToken }),\n signal,\n });\n },\n};\n\nexport const channelsApi = {\n /**\n * List channels\n */\n async list(\n options: { type?: string; limit?: number } = {},\n signal?: AbortSignal\n ): Promise<ApiResponse<{ channels: ChannelListItem[] }>> {\n const params = new URLSearchParams();\n if (options.type) params.append('type', options.type);\n if (options.limit) params.append('limit', String(options.limit));\n const query = params.toString() ? `?${params.toString()}` : '';\n return fetchWithAuth(`/api/v1/channels${query}`, { signal });\n },\n\n /**\n * Get channel by ID\n */\n async get(channelId: string, signal?: AbortSignal): Promise<ApiResponse<Channel>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}`, { signal });\n },\n\n /**\n * Get or create DM channel\n */\n async getOrCreateDM(\n userId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth('/api/v1/channels/dm', {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n signal,\n });\n },\n\n /**\n * Create channel\n */\n async create(\n data: { name: string; type?: string; description?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth('/api/v1/channels', {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Mark channel as read\n */\n async markAsRead(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ unread_count: number }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/read`, {\n method: 'POST',\n signal,\n });\n },\n\n /**\n * Get channel members\n */\n async getMembers(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ members: UserSummary[] }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/members`, { signal });\n },\n\n /**\n * Update channel\n */\n async update(\n channelId: string,\n data: { name?: string; description?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n signal,\n });\n },\n};\n\nexport const messagesApi = {\n /**\n * List messages in a channel\n */\n async list(\n channelId: string,\n options: { limit?: number; before?: string } = {},\n signal?: AbortSignal\n ): Promise<ApiResponse<MessagesResponse>> {\n const params = new URLSearchParams();\n if (options.limit) params.append('limit', String(options.limit));\n if (options.before) params.append('before', options.before);\n const query = params.toString() ? `?${params.toString()}` : '';\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages${query}`, { signal });\n },\n\n /**\n * Send a message\n */\n async send(\n channelId: string,\n data: { content: string; type?: string; parent_id?: string; metadata?: Record<string, unknown>; file_ids?: string[] },\n signal?: AbortSignal\n ): Promise<ApiResponse<Message>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages`, {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Update a message\n */\n async update(\n channelId: string,\n messageId: string,\n data: { content?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Message>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Delete a message\n */\n async delete(\n channelId: string,\n messageId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {\n method: 'DELETE',\n signal,\n });\n },\n\n /**\n * Mark messages as delivered\n */\n async markDelivered(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/delivered`, {\n method: 'POST',\n signal,\n });\n },\n\n /**\n * Mark messages as read\n */\n async markRead(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/read`, {\n method: 'POST',\n signal,\n });\n },\n};\n\nexport const reactionsApi = {\n /**\n * Add reaction to a message\n */\n async add(\n channelId: string,\n messageId: string,\n emoji: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}/reactions`, {\n method: 'POST',\n body: JSON.stringify({ emoji }),\n signal,\n });\n },\n\n /**\n * Remove reaction from a message\n */\n async remove(\n channelId: string,\n messageId: string,\n emoji: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {\n return fetchWithAuth(\n `/api/v1/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,\n { method: 'DELETE', signal }\n );\n },\n};\n\nexport const filesApi = {\n /**\n * Get upload URL\n */\n async getUploadUrl(\n data: { file_name: string; file_type: string; file_size: number },\n signal?: AbortSignal\n ): Promise<ApiResponse<UploadUrlResponse>> {\n return fetchWithAuth('/api/v1/files/upload-url', {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Confirm file upload\n */\n async confirm(\n fileId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ file: FileAttachment }>> {\n return fetchWithAuth('/api/v1/files', {\n method: 'POST',\n body: JSON.stringify({ file_id: fileId }),\n signal,\n });\n },\n\n /**\n * Get download URL\n */\n async getDownloadUrl(\n fileId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ url: string; expires_at: string }>> {\n return fetchWithAuth(`/api/v1/files/${fileId}/download`, { signal });\n },\n};\n\nexport const usersApi = {\n /**\n * Search users\n */\n async search(\n query: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ users: UserSummary[] }>> {\n return fetchWithAuth(`/api/v1/users/search?q=${encodeURIComponent(query)}`, { signal });\n },\n\n /**\n * Get user by ID\n */\n async get(userId: string, signal?: AbortSignal): Promise<ApiResponse<UserSummary>> {\n return fetchWithAuth(`/api/v1/users/${userId}`, { signal });\n },\n};\n\nexport default {\n chatApi,\n channelsApi,\n messagesApi,\n reactionsApi,\n filesApi,\n usersApi,\n configureApiClient,\n};\n","// ============================================================================\n// AegisChat React SDK - useAutoRead Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { channelsApi } from '../services/api';\n\nconst SESSION_STORAGE_KEY = '@aegischat/activeChannel';\n\nexport interface UseAutoReadOptions {\n onMarkAsRead?: (channelId: string) => void;\n}\n\nexport interface UseAutoReadReturn {\n markAsRead: (channelId: string) => Promise<void>;\n markAllAsRead: () => Promise<void>;\n isFocused: boolean;\n}\n\nexport function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn {\n const [isFocused, setIsFocused] = useState(false);\n\n useEffect(() => {\n setIsFocused(typeof document !== 'undefined' && document.hasFocus());\n\n const handleFocus = () => setIsFocused(true);\n const handleBlur = () => setIsFocused(false);\n\n window.addEventListener('focus', handleFocus);\n window.addEventListener('blur', handleBlur);\n\n return () => {\n window.removeEventListener('focus', handleFocus);\n window.removeEventListener('blur', handleBlur);\n };\n }, []);\n\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n const activeChannelId = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (activeChannelId) {\n markAsRead(activeChannelId);\n }\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => document.removeEventListener('visibilitychange', handleVisibilityChange);\n }, []);\n\n const markAsRead = useCallback(async (channelId: string) => {\n if (!isFocused) return;\n try {\n await channelsApi.markAsRead(channelId);\n options.onMarkAsRead?.(channelId);\n } catch (error) {\n console.error('[AegisChat] useAutoRead: Failed to mark as read:', error);\n }\n }, [isFocused, options.onMarkAsRead]);\n\n const markAllAsRead = useCallback(async () => {\n if (!isFocused) return;\n try {\n const response = await channelsApi.list({});\n const channels = response.data.channels || [];\n await Promise.all(\n channels.filter((ch) => ch.unread_count > 0).map((ch) => channelsApi.markAsRead(ch.id))\n );\n } catch (error) {\n console.error('[AegisChat] useAutoRead: Failed to mark all as read:', error);\n }\n }, [isFocused]);\n\n return { markAsRead, markAllAsRead, isFocused };\n}\n\nexport default useAutoRead;\n","// ============================================================================\n// AegisChat React SDK - useChannels Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { channelsApi } from '../services/api';\nimport type { ChannelListItem, Channel } from '../types';\n\nexport interface UseChannelsOptions {\n type?: 'direct' | 'public' | 'private';\n limit?: number;\n autoFetch?: boolean;\n onChannelCreated?: (channel: Channel) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseChannelsReturn {\n channels: ChannelListItem[];\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n getOrCreateDM: (userId: string) => Promise<Channel>;\n markAsRead: (channelId: string) => Promise<void>;\n}\n\nexport function useChannels(options: UseChannelsOptions = {}): UseChannelsReturn {\n const { type, limit = 20, autoFetch = true, onChannelCreated, onError } = options;\n\n const [channels, setChannels] = useState<ChannelListItem[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const fetchChannels = useCallback(async (signal?: AbortSignal) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const response = await channelsApi.list({ type, limit }, signal);\n if (signal?.aborted) return;\n setChannels(response.data.channels);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') return;\n const error = err instanceof Error ? err : new Error('Failed to fetch channels');\n setError(error);\n onError?.(error);\n } finally {\n if (!signal?.aborted) setIsLoading(false);\n }\n }, [type, limit, onError]);\n\n const refetch = useCallback(async () => {\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n await fetchChannels(controller.signal);\n }, [fetchChannels]);\n\n const getOrCreateDM = useCallback(async (userId: string): Promise<Channel> => {\n try {\n const response = await channelsApi.getOrCreateDM(userId);\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n fetchChannels(controller.signal);\n onChannelCreated?.(response.data);\n return response.data;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create DM');\n onError?.(error);\n throw error;\n }\n }, [fetchChannels, onChannelCreated, onError]);\n\n const markAsRead = useCallback(async (channelId: string): Promise<void> => {\n try {\n await channelsApi.markAsRead(channelId);\n setChannels((prev) => prev.map((ch) => ch.id === channelId ? { ...ch, unread_count: 0 } : ch));\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to mark as read');\n onError?.(error);\n throw error;\n }\n }, [onError]);\n\n useEffect(() => {\n if (autoFetch) {\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n fetchChannels(controller.signal);\n return () => controller.abort();\n }\n }, [autoFetch, fetchChannels]);\n\n return { channels, isLoading, error, refetch, getOrCreateDM, markAsRead };\n}\n\nexport default useChannels;\n","// ============================================================================\n// AegisChat React SDK - useMessages Hook\n// ============================================================================\n\nimport { useCallback, useState } from 'react';\nimport { messagesApi } from '../services/api';\nimport type { Message, MessagesResponse } from '../types';\n\nexport interface UseMessagesOptions {\n channelId: string;\n}\n\nexport interface UseMessagesReturn {\n messages: Message[];\n isLoading: boolean;\n hasMore: boolean;\n sendMessage: (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\nexport function useMessages(_options: UseMessagesOptions): UseMessagesReturn {\n const [messages, setMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasMore, setHasMore] = useState(true);\n\n const sendMessage = useCallback(async (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => {\n // Implementation would go here\n }, []);\n\n const loadMore = useCallback(async () => {\n // Implementation would go here\n }, []);\n\n return { messages, isLoading, hasMore, sendMessage, loadMore };\n}\n\nexport default useMessages;\n","// ============================================================================\n// AegisChat React SDK - useTypingIndicator Hook\n// ============================================================================\n\nimport { useCallback, useState } from 'react';\nimport type { TypingUser } from '../types';\n\nexport interface UseTypingIndicatorOptions {\n channelId: string;\n ws?: WebSocket | null;\n}\n\nexport interface UseTypingIndicatorReturn {\n typingUsers: TypingUser[];\n startTyping: () => void;\n stopTyping: () => void;\n}\n\nexport function useTypingIndicator(_options: UseTypingIndicatorOptions): UseTypingIndicatorReturn {\n const [typingUsers, setTypingUsers] = useState<TypingUser[]>([]);\n\n const startTyping = useCallback(() => {}, []);\n const stopTyping = useCallback(() => {}, []);\n\n return { typingUsers, startTyping, stopTyping };\n}\n\nexport default useTypingIndicator;\n","// ============================================================================\n// AegisChat React SDK - useReactions Hook\n// ============================================================================\n\nimport { useState } from 'react';\nimport type { ReactionSummary } from '../types';\n\nexport interface UseReactionsOptions {\n channelId: string;\n messageId: string;\n}\n\nexport interface UseReactionsReturn {\n reactions: ReactionSummary[];\n addReaction: (emoji: string) => Promise<void>;\n removeReaction: (emoji: string) => Promise<void>;\n}\n\nexport function useReactions(_options: UseReactionsOptions): UseReactionsReturn {\n const [reactions, setReactions] = useState<ReactionSummary[]>([]);\n\n const addReaction = async (_emoji: string) => {};\n const removeReaction = async (_emoji: string) => {};\n\n return { reactions, addReaction, removeReaction };\n}\n\nexport default useReactions;\n","// ============================================================================\n// AegisChat React SDK - useFileUpload Hook\n// ============================================================================\n\nimport { useState } from 'react';\nimport type { FileAttachment, UploadProgress } from '../types';\n\nexport interface UseFileUploadOptions {\n channelId: string;\n}\n\nexport interface UseFileUploadReturn {\n uploadProgress: UploadProgress[];\n upload: (file: File) => Promise<FileAttachment | null>;\n}\n\nexport function useFileUpload(_options: UseFileUploadOptions): UseFileUploadReturn {\n const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);\n\n const upload = async (_file: File): Promise<FileAttachment | null> => null;\n\n return { uploadProgress, upload };\n}\n\nexport default useFileUpload;\n","// ============================================================================\n// AegisChat React SDK - useMentions Hook\n// ============================================================================\n\nexport interface UseMentionsOptions {}\n\nexport interface UseMentionsReturn {\n parseMentions: (content: string) => string[];\n highlightMentions: (content: string) => string;\n isUserMentioned: (content: string, userId: string) => boolean;\n}\n\nexport function useMentions(_options: UseMentionsOptions = {}): UseMentionsReturn {\n const parseMentions = (content: string): string[] => {\n const mentionRegex = /@\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n const mentions: string[] = [];\n let match;\n while ((match = mentionRegex.exec(content)) !== null) {\n mentions.push(match[2]);\n }\n return mentions;\n };\n\n const highlightMentions = (content: string): string => {\n return content.replace(/@\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<span class=\"mention\">@$1</span>');\n };\n\n const isUserMentioned = (content: string, userId: string): boolean => {\n const mentions = parseMentions(content);\n return mentions.includes(userId);\n };\n\n return { parseMentions, highlightMentions, isUserMentioned };\n}\n\nexport default useMentions;\n"],"mappings":"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","names":["import_react","SESSION_STORAGE_KEY","import_react","error","import_react","import_react","import_react","import_react"]}
package/dist/index.mjs CHANGED
@@ -267,16 +267,19 @@ function useChat(options) {
267
267
  const oldestMessageId = useRef(null);
268
268
  const activeChannelIdRef = useRef(null);
269
269
  const configRef = useRef(config);
270
- const sessionRef = useRef(null);
270
+ const sessionRef = useRef(initialSession ?? null);
271
+ if (initialSession && !config) {
272
+ configureApiClient({
273
+ baseUrl: initialSession.api_url,
274
+ getAccessToken: async () => sessionRef.current?.access_token || ""
275
+ });
276
+ }
271
277
  useEffect(() => {
272
278
  configRef.current = config;
273
279
  }, [config]);
274
280
  useEffect(() => {
275
281
  activeChannelIdRef.current = activeChannelId;
276
282
  }, [activeChannelId]);
277
- useEffect(() => {
278
- sessionRef.current = session;
279
- }, [session]);
280
283
  const getActiveChannelId = useCallback(() => {
281
284
  if (typeof window === "undefined") return null;
282
285
  return sessionStorage.getItem(SESSION_STORAGE_KEY);
@@ -440,6 +443,7 @@ function useChat(options) {
440
443
  ws.onopen = () => {
441
444
  console.log("[AegisChat] WebSocket connected");
442
445
  setIsConnected(true);
446
+ setIsConnecting(false);
443
447
  reconnectAttempts.current = 0;
444
448
  onConnectionChange?.(true);
445
449
  pingInterval.current = setInterval(() => {
@@ -462,6 +466,7 @@ function useChat(options) {
462
466
  ws.onclose = () => {
463
467
  console.log("[AegisChat] WebSocket disconnected");
464
468
  setIsConnected(false);
469
+ setIsConnecting(false);
465
470
  clearTimers();
466
471
  onConnectionChange?.(false);
467
472
  if (!isManualDisconnect.current && reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {
@@ -480,7 +485,8 @@ function useChat(options) {
480
485
  }, [clearTimers, handleWebSocketMessage, onConnectionChange]);
481
486
  const connect = useCallback(async () => {
482
487
  console.log("[AegisChat] connect() called");
483
- if (!sessionRef.current && !config) {
488
+ const targetSession = sessionRef.current ?? initialSession;
489
+ if (!targetSession) {
484
490
  throw new Error("Either config or initialSession must be provided");
485
491
  }
486
492
  if (sessionRef.current) {
@@ -488,19 +494,9 @@ function useChat(options) {
488
494
  connectWebSocket();
489
495
  return;
490
496
  }
491
- try {
492
- setIsConnecting(true);
493
- console.log("[AegisChat] Fetching chat session...");
494
- const result = await chatApi.connect({ role, client_id: clientId });
495
- console.log("[AegisChat] Chat session received:", result);
496
- setSession(result.data);
497
- setIsConnecting(false);
498
- } catch (error) {
499
- console.error("[AegisChat] Failed to get chat session:", error);
500
- setIsConnecting(false);
501
- throw error;
502
- }
503
- }, [role, clientId, connectWebSocket]);
497
+ console.log("[AegisChat] Using initialSession, calling connectWebSocket directly");
498
+ connectWebSocket();
499
+ }, [connectWebSocket]);
504
500
  const disconnect = useCallback(() => {
505
501
  isManualDisconnect.current = true;
506
502
  clearTimers();
@@ -754,9 +750,6 @@ function useChat(options) {
754
750
  const deleteFailedMessage = useCallback((tempId) => {
755
751
  setMessages((prev) => prev.filter((m) => m.tempId !== tempId));
756
752
  }, []);
757
- useEffect(() => {
758
- connect();
759
- }, []);
760
753
  useEffect(() => {
761
754
  if (session && !isConnected && !isConnecting && autoConnect) {
762
755
  connectWebSocket();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/useChat.ts","../src/services/api.ts","../src/hooks/useAutoRead.ts","../src/hooks/useChannels.ts","../src/hooks/useMessages.ts","../src/hooks/useTypingIndicator.ts","../src/hooks/useReactions.ts","../src/hooks/useFileUpload.ts","../src/hooks/useMentions.ts"],"sourcesContent":["// ============================================================================\n// AegisChat React SDK - useChat Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { chatApi, channelsApi, messagesApi, filesApi } from '../services/api';\nimport type {\n AegisConfig,\n ChatSession,\n ChannelListItem,\n Message,\n MessagesResponse,\n TypingUser,\n UserSummary,\n FileAttachment,\n UploadProgress,\n MessageSummary,\n} from '../types';\n\nconst TYPING_TIMEOUT = 3000;\nconst RECONNECT_INTERVAL = 3000;\nconst MAX_RECONNECT_ATTEMPTS = 5;\nconst MAX_RECONNECT_DELAY = 30000;\nconst PING_INTERVAL = 30000;\nconst SESSION_STORAGE_KEY = '@aegischat/activeChannel';\n\nexport interface UseChatOptions {\n config?: AegisConfig;\n role: 'lawyer' | 'client';\n clientId?: string;\n \n initialSession?: ChatSession | null;\n autoConnect?: boolean;\n onMessage?: (message: Message) => void;\n onTyping?: (channelId: string, user: TypingUser) => void;\n onConnectionChange?: (connected: boolean) => void;\n}\n\nexport interface UseChatReturn {\n session: ChatSession | null;\n isConnected: boolean;\n isConnecting: boolean;\n channels: ChannelListItem[];\n messages: Message[];\n activeChannelId: string | null;\n typingUsers: TypingUser[];\n isLoadingChannels: boolean;\n isLoadingMessages: boolean;\n hasMoreMessages: boolean;\n uploadProgress: UploadProgress[];\n connect: () => Promise<void>;\n disconnect: () => void;\n selectChannel: (channelId: string) => void;\n sendMessage: (content: string, options?: { type?: string; parent_id?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n sendMessageWithFiles: (content: string, files: File[], options?: { type?: string; parent_id?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n uploadFile: (file: File) => Promise<FileAttachment | null>;\n loadMoreMessages: () => Promise<void>;\n startTyping: () => void;\n stopTyping: () => void;\n refreshChannels: () => Promise<void>;\n createDMWithUser: (userId: string) => Promise<string | null>;\n retryMessage: (tempId: string) => Promise<void>;\n deleteFailedMessage: (tempId: string) => void;\n markAsRead: (channelId: string) => Promise<void>;\n}\n\nexport function useChat(options: UseChatOptions): UseChatReturn {\n const { config, role, clientId, initialSession, autoConnect = true, onMessage, onTyping, onConnectionChange } = options;\n\n const [session, setSession] = useState<ChatSession | null>(initialSession ?? null);\n const [isConnected, setIsConnected] = useState(false);\n const [isConnecting, setIsConnecting] = useState(false);\n const [activeChannelId, setActiveChannelIdState] = useState<string | null>(null);\n const [channels, setChannels] = useState<ChannelListItem[]>([]);\n const [messages, setMessages] = useState<Message[]>([]);\n const [typingUsers, setTypingUsers] = useState<Record<string, TypingUser[]>>({});\n const [isLoadingChannels, setIsLoadingChannels] = useState(false);\n const [isLoadingMessages, setIsLoadingMessages] = useState(false);\n const [hasMoreMessages, setHasMoreMessages] = useState(true);\n const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);\n\n const wsRef = useRef<WebSocket | null>(null);\n const reconnectAttempts = useRef(0);\n const reconnectTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pingInterval = useRef<ReturnType<typeof setInterval> | null>(null);\n const typingTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isManualDisconnect = useRef(false);\n const oldestMessageId = useRef<string | null>(null);\n const activeChannelIdRef = useRef<string | null>(null);\n const configRef = useRef(config);\n const sessionRef = useRef<ChatSession | null>(null);\n\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n useEffect(() => {\n activeChannelIdRef.current = activeChannelId;\n }, [activeChannelId]);\n\n useEffect(() => {\n sessionRef.current = session;\n }, [session]);\n\n const getActiveChannelId = useCallback((): string | null => {\n if (typeof window === 'undefined') return null;\n return sessionStorage.getItem(SESSION_STORAGE_KEY);\n }, []);\n\n const setActiveChannelId = useCallback((id: string | null) => {\n setActiveChannelIdState(id);\n if (typeof window !== 'undefined') {\n if (id) {\n sessionStorage.setItem(SESSION_STORAGE_KEY, id);\n } else {\n sessionStorage.removeItem(SESSION_STORAGE_KEY);\n }\n }\n }, []);\n\n const fetchFromComms = useCallback(async <T>(path: string, fetchOptions: RequestInit = {}): Promise<T> => {\n const currentSession = sessionRef.current;\n if (!currentSession) {\n throw new Error('Chat session not initialized');\n }\n\n const response = await fetch(`${currentSession.api_url}${path}`, {\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${currentSession.access_token}`,\n ...fetchOptions.headers,\n },\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n const data = await response.json();\n return data.data || data;\n }, []);\n\n const clearTimers = useCallback(() => {\n if (reconnectTimeout.current) {\n clearTimeout(reconnectTimeout.current);\n reconnectTimeout.current = null;\n }\n if (pingInterval.current) {\n clearInterval(pingInterval.current);\n pingInterval.current = null;\n }\n }, []);\n\n const handleWebSocketMessage = useCallback((data: { type: string; payload: unknown }) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n console.log('[AegisChat] WebSocket message received:', data.type, data);\n\n switch (data.type) {\n case 'message.new': {\n const newMessage = data.payload as Message;\n if (newMessage.channel_id === currentActiveChannelId) {\n setMessages((prev) => {\n const existingIndex = prev.findIndex(\n (m) => m.tempId && m.content === newMessage.content && m.status === 'sending'\n );\n if (existingIndex !== -1) {\n const updated = [...prev];\n updated[existingIndex] = { ...newMessage, status: 'sent' };\n return updated;\n }\n if (prev.some((m) => m.id === newMessage.id)) return prev;\n return [...prev, { ...newMessage, status: 'delivered' }];\n });\n onMessage?.(newMessage);\n }\n setChannels((prev) => {\n const updated = prev.map((ch) =>\n ch.id === newMessage.channel_id\n ? {\n ...ch,\n last_message: {\n id: newMessage.id,\n content: newMessage.content,\n created_at: newMessage.created_at,\n sender: { id: newMessage.sender_id, display_name: 'Unknown', status: 'online' as const },\n } as MessageSummary,\n unread_count: ch.id === currentActiveChannelId ? 0 : ch.unread_count + 1,\n }\n : ch\n );\n return updated.sort((a, b) => {\n const timeA = a.last_message?.created_at || '';\n const timeB = b.last_message?.created_at || '';\n return timeB.localeCompare(timeA);\n });\n });\n break;\n }\n case 'message.updated': {\n const updatedMessage = data.payload as Message;\n setMessages((prev) => prev.map((m) => (m.id === updatedMessage.id ? updatedMessage : m)));\n break;\n }\n case 'message.deleted': {\n const { message_id } = data.payload as { message_id: string };\n setMessages((prev) => prev.map((m) => (m.id === message_id ? { ...m, deleted: true } : m)));\n break;\n }\n case 'message.delivered':\n case 'message.read': {\n const { message_id, channel_id, status } = data.payload as { message_id: string; channel_id: string; status: string };\n if (channel_id === currentActiveChannelId) {\n setMessages((prev) =>\n prev.map((m) => (m.id === message_id ? { ...m, status: (status as Message['status']) || 'delivered' } : m))\n );\n }\n break;\n }\n case 'message.delivered.batch':\n case 'message.read.batch': {\n const { channel_id } = data.payload as { channel_id: string };\n if (channel_id === currentActiveChannelId) {\n setMessages((prev) =>\n prev.map((m) => (m.status === 'sent' || m.status === 'delivered' ? { ...m, status: data.type === 'message.delivered.batch' ? 'delivered' : 'read' } : m))\n );\n }\n break;\n }\n case 'typing.start': {\n const { channel_id, user } = data.payload as { channel_id: string; user: UserSummary };\n const typingUser: TypingUser = {\n id: user.id,\n displayName: user.display_name,\n avatarUrl: user.avatar_url,\n startedAt: Date.now(),\n };\n setTypingUsers((prev) => ({\n ...prev,\n [channel_id]: [...(prev[channel_id] || []).filter((u) => u.id !== user.id), typingUser],\n }));\n onTyping?.(channel_id, typingUser);\n break;\n }\n case 'typing.stop': {\n const { channel_id, user_id } = data.payload as { channel_id: string; user_id: string };\n setTypingUsers((prev) => ({\n ...prev,\n [channel_id]: (prev[channel_id] || []).filter((u) => u.id !== user_id),\n }));\n break;\n }\n case 'pong':\n break;\n default:\n console.log('[AegisChat] Unhandled message type:', data.type);\n }\n }, [onMessage, onTyping]);\n\n const connectWebSocket = useCallback(() => {\n const currentSession = sessionRef.current;\n if (!currentSession?.websocket_url || !currentSession?.access_token) {\n console.warn('[AegisChat] Cannot connect WebSocket - missing session or token');\n return;\n }\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n console.log('[AegisChat] WebSocket already open, skipping connection');\n return;\n }\n\n setIsConnecting(true);\n isManualDisconnect.current = false;\n\n const wsUrl = `${currentSession.websocket_url}?token=${currentSession.access_token}`;\n console.log('[AegisChat] Creating WebSocket connection to:', wsUrl);\n const ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n console.log('[AegisChat] WebSocket connected');\n setIsConnected(true);\n reconnectAttempts.current = 0;\n onConnectionChange?.(true);\n\n pingInterval.current = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ type: 'ping' }));\n }\n }, PING_INTERVAL);\n\n if (activeChannelIdRef.current) {\n ws.send(JSON.stringify({ type: 'channel.join', payload: { channel_id: activeChannelIdRef.current } }));\n }\n };\n\n ws.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n handleWebSocketMessage(data);\n } catch (error) {\n console.error('[AegisChat] Failed to parse WebSocket message:', error);\n }\n };\n\n ws.onclose = () => {\n console.log('[AegisChat] WebSocket disconnected');\n setIsConnected(false);\n clearTimers();\n onConnectionChange?.(false);\n\n if (!isManualDisconnect.current && reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {\n const delay = Math.min(RECONNECT_INTERVAL * Math.pow(2, reconnectAttempts.current), MAX_RECONNECT_DELAY);\n console.log(`[AegisChat] Reconnecting in ${delay}ms...`);\n reconnectTimeout.current = setTimeout(() => {\n reconnectAttempts.current++;\n connectWebSocket();\n }, delay);\n }\n };\n\n ws.onerror = (error) => {\n console.error('[AegisChat] WebSocket error:', error);\n };\n\n wsRef.current = ws;\n }, [clearTimers, handleWebSocketMessage, onConnectionChange]);\n\n const connect = useCallback(async () => {\n console.log('[AegisChat] connect() called');\n if (!sessionRef.current && !config) {\n throw new Error('Either config or initialSession must be provided');\n }\n if (sessionRef.current) {\n console.log('[AegisChat] Session exists, calling connectWebSocket directly');\n connectWebSocket();\n return;\n }\n\n try {\n setIsConnecting(true);\n console.log('[AegisChat] Fetching chat session...');\n const result = await chatApi.connect({ role, client_id: clientId });\n console.log('[AegisChat] Chat session received:', result);\n setSession(result.data);\n setIsConnecting(false);\n } catch (error) {\n console.error('[AegisChat] Failed to get chat session:', error);\n setIsConnecting(false);\n throw error;\n }\n }, [role, clientId, connectWebSocket]);\n\n const disconnect = useCallback(() => {\n isManualDisconnect.current = true;\n clearTimers();\n if (wsRef.current) {\n wsRef.current.close();\n wsRef.current = null;\n }\n setIsConnected(false);\n setSession(null);\n setChannels([]);\n setMessages([]);\n }, [clearTimers]);\n\n const refreshChannels = useCallback(async () => {\n const currentSession = sessionRef.current;\n if (!currentSession) return;\n\n setIsLoadingChannels(true);\n try {\n const response = await channelsApi.list({});\n setChannels(response.data.channels || []);\n } catch (error) {\n console.error('[AegisChat] Failed to fetch channels:', error);\n } finally {\n setIsLoadingChannels(false);\n }\n }, []);\n\n const selectChannel = useCallback(async (channelId: string) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n setActiveChannelId(channelId);\n setMessages([]);\n setHasMoreMessages(true);\n oldestMessageId.current = null;\n\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n if (currentActiveChannelId) {\n wsRef.current.send(JSON.stringify({ type: 'channel.leave', payload: { channel_id: currentActiveChannelId } }));\n }\n wsRef.current.send(JSON.stringify({ type: 'channel.join', payload: { channel_id: channelId } }));\n }\n\n setIsLoadingMessages(true);\n try {\n const response = await fetchFromComms<MessagesResponse>(`/channels/${channelId}/messages?limit=50`);\n setMessages(response.messages || []);\n setHasMoreMessages(response.has_more);\n if (response.oldest_id) {\n oldestMessageId.current = response.oldest_id;\n }\n\n await markAsRead(channelId);\n\n setChannels((prev) => prev.map((ch) => (ch.id === channelId ? { ...ch, unread_count: 0 } : ch)));\n } catch (error) {\n console.error('[AegisChat] Failed to load messages:', error);\n setMessages([]);\n } finally {\n setIsLoadingMessages(false);\n }\n }, [setActiveChannelId, fetchFromComms]);\n\n const markAsRead = useCallback(async (channelId: string) => {\n try {\n await fetchFromComms(`/channels/${channelId}/read`, { method: 'POST' });\n } catch (error) {\n console.error('[AegisChat] Failed to mark as read:', error);\n }\n }, [fetchFromComms]);\n\n const loadMoreMessages = useCallback(async () => {\n if (!activeChannelId || !hasMoreMessages || isLoadingMessages) return;\n\n setIsLoadingMessages(true);\n try {\n const params = oldestMessageId.current ? `?before=${oldestMessageId.current}&limit=50` : '?limit=50';\n const response = await fetchFromComms<MessagesResponse>(`/channels/${activeChannelId}/messages${params}`);\n setMessages((prev) => [...(response.messages || []), ...prev]);\n setHasMoreMessages(response.has_more);\n if (response.oldest_id) {\n oldestMessageId.current = response.oldest_id;\n }\n } catch (error) {\n console.error('[AegisChat] Failed to load more messages:', error);\n } finally {\n setIsLoadingMessages(false);\n }\n }, [activeChannelId, hasMoreMessages, isLoadingMessages, fetchFromComms]);\n\n const sendMessage = useCallback(async (\n content: string,\n msgOptions: { type?: string; parent_id?: string; metadata?: Record<string, unknown> } = {}\n ) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n const currentSession = sessionRef.current;\n if (!currentActiveChannelId || !content.trim() || !currentSession) return;\n\n const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const trimmedContent = content.trim();\n\n const optimisticMessage: Message = {\n id: tempId,\n tempId,\n channel_id: currentActiveChannelId,\n sender_id: currentSession.comms_user_id,\n content: trimmedContent,\n type: (msgOptions.type as Message['type']) || 'text',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n status: 'sending',\n metadata: msgOptions.metadata || {},\n };\n\n setMessages((prev) => [...prev, optimisticMessage]);\n\n const now = new Date().toISOString();\n setChannels((prev) => {\n const updated = prev.map((ch) =>\n ch.id === currentActiveChannelId\n ? {\n ...ch,\n last_message: {\n id: tempId,\n content: trimmedContent,\n created_at: now,\n sender: { id: currentSession.comms_user_id, display_name: 'You', status: 'online' as const },\n },\n }\n : ch\n );\n return updated.sort((a, b) => {\n const timeA = a.last_message?.created_at || '';\n const timeB = b.last_message?.created_at || '';\n return timeB.localeCompare(timeA);\n });\n });\n\n try {\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({ content: trimmedContent, type: msgOptions.type || 'text', parent_id: msgOptions.parent_id, metadata: msgOptions.metadata }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to send message:', error);\n setMessages((prev) =>\n prev.map((m) =>\n m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m\n )\n );\n throw error;\n }\n }, [fetchFromComms]);\n\n const uploadFile = useCallback(async (file: File): Promise<FileAttachment | null> => {\n const currentSession = sessionRef.current;\n if (!currentSession) return null;\n\n const fileId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n\n setUploadProgress((prev) => [...prev, { fileId, fileName: file.name, progress: 0, status: 'pending' }]);\n\n try {\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: 'uploading', progress: 10 } : p));\n\n const uploadUrlResponse = await fetchFromComms<{ upload_url: string; file_id: string; expires_at: string }>('/files/upload-url', {\n method: 'POST',\n body: JSON.stringify({ file_name: file.name, file_type: file.type || 'application/octet-stream', file_size: file.size }),\n });\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, fileId: uploadUrlResponse.file_id, progress: 30 } : p));\n\n const uploadResponse = await fetch(uploadUrlResponse.upload_url, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n\n if (!uploadResponse.ok) throw new Error(`Upload failed: ${uploadResponse.statusText}`);\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: 'confirming', progress: 70 } : p));\n\n const confirmResponse = await fetchFromComms<{ file: FileAttachment }>('/files', {\n method: 'POST',\n body: JSON.stringify({ file_id: uploadUrlResponse.file_id }),\n });\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: 'complete', progress: 100 } : p));\n setTimeout(() => setUploadProgress((prev) => prev.filter((p) => p.fileId !== uploadUrlResponse.file_id)), 2000);\n\n return confirmResponse.file;\n } catch (error) {\n console.error('[AegisChat] Failed to upload file:', error);\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: 'error', error: error instanceof Error ? error.message : 'Upload failed' } : p));\n setTimeout(() => setUploadProgress((prev) => prev.filter((p) => p.fileId !== fileId)), 5000);\n return null;\n }\n }, [fetchFromComms]);\n\n const sendMessageWithFiles = useCallback(async (\n content: string,\n files: File[],\n msgOptions: { type?: string; parent_id?: string; metadata?: Record<string, unknown> } = {}\n ) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n const currentSession = sessionRef.current;\n if (!currentActiveChannelId || (!content.trim() && files.length === 0) || !currentSession) return;\n\n const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const trimmedContent = content.trim();\n\n const optimisticMessage: Message = {\n id: tempId,\n tempId,\n channel_id: currentActiveChannelId,\n sender_id: currentSession.comms_user_id,\n content: trimmedContent || `Uploading ${files.length} file(s)...`,\n type: 'file',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n status: 'sending',\n metadata: { ...msgOptions.metadata, files: files.map((f) => ({ id: `temp-${f.name}`, filename: f.name, mime_type: f.type, size: f.size, url: '' })) },\n };\n\n setMessages((prev) => [...prev, optimisticMessage]);\n\n try {\n const uploadedFiles: FileAttachment[] = [];\n for (const file of files) {\n const attachment = await uploadFile(file);\n if (attachment) uploadedFiles.push(attachment);\n }\n\n const messageType = uploadedFiles.length > 0 && !trimmedContent ? 'file' : 'text';\n\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({\n content: trimmedContent || (uploadedFiles.length > 0 ? `Shared ${uploadedFiles.length} file(s)` : ''),\n type: msgOptions.type || messageType,\n parent_id: msgOptions.parent_id,\n metadata: { ...msgOptions.metadata, files: uploadedFiles },\n file_ids: uploadedFiles.map((f) => f.id),\n }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to send message with files:', error);\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m));\n throw error;\n }\n }, [fetchFromComms, uploadFile]);\n\n const stopTyping = useCallback(() => {\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!currentActiveChannelId || !wsRef.current) return;\n wsRef.current.send(JSON.stringify({ type: 'typing.stop', payload: { channel_id: currentActiveChannelId } }));\n if (typingTimeout.current) {\n clearTimeout(typingTimeout.current);\n typingTimeout.current = null;\n }\n }, []);\n\n const startTyping = useCallback(() => {\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!currentActiveChannelId || !wsRef.current) return;\n wsRef.current.send(JSON.stringify({ type: 'typing.start', payload: { channel_id: currentActiveChannelId } }));\n if (typingTimeout.current) clearTimeout(typingTimeout.current);\n typingTimeout.current = setTimeout(stopTyping, TYPING_TIMEOUT);\n }, [stopTyping]);\n\n const createDMWithUser = useCallback(async (userId: string): Promise<string | null> => {\n try {\n const channel = await fetchFromComms<{ id: string }>('/channels/dm', {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n });\n await refreshChannels();\n return channel.id;\n } catch (error) {\n console.error('[AegisChat] Failed to create DM:', error);\n return null;\n }\n }, [fetchFromComms, refreshChannels]);\n\n const retryMessage = useCallback(async (tempId: string) => {\n const failedMessage = messages.find((m) => m.tempId === tempId && m.status === 'failed');\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!failedMessage || !currentActiveChannelId) return;\n\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'sending', errorMessage: undefined } : m));\n\n try {\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({ content: failedMessage.content, type: failedMessage.type, metadata: failedMessage.metadata }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to retry message:', error);\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m));\n }\n }, [messages, fetchFromComms]);\n\n const deleteFailedMessage = useCallback((tempId: string) => {\n setMessages((prev) => prev.filter((m) => m.tempId !== tempId));\n }, []);\n\n // Effects\n useEffect(() => {\n connect();\n }, []);\n\n useEffect(() => {\n if (session && !isConnected && !isConnecting && autoConnect) {\n connectWebSocket();\n }\n }, [session, isConnected, isConnecting, autoConnect, connectWebSocket]);\n\n useEffect(() => {\n if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {\n wsRef.current.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n handleWebSocketMessage(data);\n } catch (error) {\n console.error('[AegisChat] Failed to parse WebSocket message:', error);\n }\n };\n }\n }, [handleWebSocketMessage]);\n\n useEffect(() => {\n if (isConnected && channels.length === 0) {\n refreshChannels();\n }\n }, [isConnected, channels.length, refreshChannels]);\n\n useEffect(() => {\n const storedActiveChannel = getActiveChannelId();\n if (storedActiveChannel && !activeChannelId) {\n selectChannel(storedActiveChannel);\n }\n }, [getActiveChannelId, activeChannelId, selectChannel]);\n\n useEffect(() => {\n return () => {\n clearTimers();\n if (typingTimeout.current) clearTimeout(typingTimeout.current);\n if (wsRef.current) {\n isManualDisconnect.current = true;\n wsRef.current.close();\n wsRef.current = null;\n }\n };\n }, [clearTimers]);\n\n return {\n session,\n isConnected,\n isConnecting,\n channels,\n messages,\n activeChannelId,\n typingUsers: activeChannelId ? typingUsers[activeChannelId] || [] : [],\n isLoadingChannels,\n isLoadingMessages,\n hasMoreMessages,\n uploadProgress,\n connect,\n disconnect,\n selectChannel,\n sendMessage,\n sendMessageWithFiles,\n uploadFile,\n loadMoreMessages,\n startTyping,\n stopTyping,\n refreshChannels,\n createDMWithUser,\n retryMessage,\n deleteFailedMessage,\n markAsRead,\n };\n}\n\nexport default useChat;\n","// ============================================================================\n// AegisChat React SDK - API Service\n// ============================================================================\n\nimport type {\n ApiResponse,\n ChatSession,\n ChatConnectParams,\n Channel,\n ChannelListItem,\n Message,\n MessagesResponse,\n UserSummary,\n ReactionSummary,\n FileAttachment,\n UploadUrlResponse,\n} from '../types';\n\nlet baseUrl = '';\nlet getAccessToken: () => Promise<string> | string = () => '';\nlet onUnauthorized: (() => void) | undefined;\n\nexport function configureApiClient(config: {\n baseUrl: string;\n getAccessToken: () => Promise<string> | string;\n onUnauthorized?: () => void;\n}): void {\n baseUrl = config.baseUrl;\n getAccessToken = config.getAccessToken;\n onUnauthorized = config.onUnauthorized;\n}\n\nasync function fetchWithAuth<T>(\n path: string,\n options: RequestInit = {}\n): Promise<T> {\n const token = await (typeof getAccessToken === 'function' \n ? getAccessToken() \n : getAccessToken);\n\n const response = await fetch(`${baseUrl}${path}`, {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n ...options.headers,\n },\n });\n\n if (response.status === 401) {\n onUnauthorized?.();\n throw new Error('Unauthorized');\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n return response.json();\n}\n\nexport const chatApi = {\n /**\n * Connect to chat session\n */\n async connect(\n params: ChatConnectParams,\n signal?: AbortSignal\n ): Promise<ApiResponse<ChatSession>> {\n return fetchWithAuth('/api/v1/chat/connect', {\n method: 'POST',\n body: JSON.stringify(params),\n signal,\n });\n },\n\n /**\n * Refresh access token\n */\n async refreshToken(\n refreshToken: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ access_token: string; expires_in: number }>> {\n return fetchWithAuth('/api/v1/chat/refresh', {\n method: 'POST',\n body: JSON.stringify({ refresh_token: refreshToken }),\n signal,\n });\n },\n};\n\nexport const channelsApi = {\n /**\n * List channels\n */\n async list(\n options: { type?: string; limit?: number } = {},\n signal?: AbortSignal\n ): Promise<ApiResponse<{ channels: ChannelListItem[] }>> {\n const params = new URLSearchParams();\n if (options.type) params.append('type', options.type);\n if (options.limit) params.append('limit', String(options.limit));\n const query = params.toString() ? `?${params.toString()}` : '';\n return fetchWithAuth(`/api/v1/channels${query}`, { signal });\n },\n\n /**\n * Get channel by ID\n */\n async get(channelId: string, signal?: AbortSignal): Promise<ApiResponse<Channel>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}`, { signal });\n },\n\n /**\n * Get or create DM channel\n */\n async getOrCreateDM(\n userId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth('/api/v1/channels/dm', {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n signal,\n });\n },\n\n /**\n * Create channel\n */\n async create(\n data: { name: string; type?: string; description?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth('/api/v1/channels', {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Mark channel as read\n */\n async markAsRead(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ unread_count: number }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/read`, {\n method: 'POST',\n signal,\n });\n },\n\n /**\n * Get channel members\n */\n async getMembers(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ members: UserSummary[] }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/members`, { signal });\n },\n\n /**\n * Update channel\n */\n async update(\n channelId: string,\n data: { name?: string; description?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n signal,\n });\n },\n};\n\nexport const messagesApi = {\n /**\n * List messages in a channel\n */\n async list(\n channelId: string,\n options: { limit?: number; before?: string } = {},\n signal?: AbortSignal\n ): Promise<ApiResponse<MessagesResponse>> {\n const params = new URLSearchParams();\n if (options.limit) params.append('limit', String(options.limit));\n if (options.before) params.append('before', options.before);\n const query = params.toString() ? `?${params.toString()}` : '';\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages${query}`, { signal });\n },\n\n /**\n * Send a message\n */\n async send(\n channelId: string,\n data: { content: string; type?: string; parent_id?: string; metadata?: Record<string, unknown>; file_ids?: string[] },\n signal?: AbortSignal\n ): Promise<ApiResponse<Message>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages`, {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Update a message\n */\n async update(\n channelId: string,\n messageId: string,\n data: { content?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Message>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Delete a message\n */\n async delete(\n channelId: string,\n messageId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {\n method: 'DELETE',\n signal,\n });\n },\n\n /**\n * Mark messages as delivered\n */\n async markDelivered(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/delivered`, {\n method: 'POST',\n signal,\n });\n },\n\n /**\n * Mark messages as read\n */\n async markRead(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/read`, {\n method: 'POST',\n signal,\n });\n },\n};\n\nexport const reactionsApi = {\n /**\n * Add reaction to a message\n */\n async add(\n channelId: string,\n messageId: string,\n emoji: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}/reactions`, {\n method: 'POST',\n body: JSON.stringify({ emoji }),\n signal,\n });\n },\n\n /**\n * Remove reaction from a message\n */\n async remove(\n channelId: string,\n messageId: string,\n emoji: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {\n return fetchWithAuth(\n `/api/v1/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,\n { method: 'DELETE', signal }\n );\n },\n};\n\nexport const filesApi = {\n /**\n * Get upload URL\n */\n async getUploadUrl(\n data: { file_name: string; file_type: string; file_size: number },\n signal?: AbortSignal\n ): Promise<ApiResponse<UploadUrlResponse>> {\n return fetchWithAuth('/api/v1/files/upload-url', {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Confirm file upload\n */\n async confirm(\n fileId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ file: FileAttachment }>> {\n return fetchWithAuth('/api/v1/files', {\n method: 'POST',\n body: JSON.stringify({ file_id: fileId }),\n signal,\n });\n },\n\n /**\n * Get download URL\n */\n async getDownloadUrl(\n fileId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ url: string; expires_at: string }>> {\n return fetchWithAuth(`/api/v1/files/${fileId}/download`, { signal });\n },\n};\n\nexport const usersApi = {\n /**\n * Search users\n */\n async search(\n query: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ users: UserSummary[] }>> {\n return fetchWithAuth(`/api/v1/users/search?q=${encodeURIComponent(query)}`, { signal });\n },\n\n /**\n * Get user by ID\n */\n async get(userId: string, signal?: AbortSignal): Promise<ApiResponse<UserSummary>> {\n return fetchWithAuth(`/api/v1/users/${userId}`, { signal });\n },\n};\n\nexport default {\n chatApi,\n channelsApi,\n messagesApi,\n reactionsApi,\n filesApi,\n usersApi,\n configureApiClient,\n};\n","// ============================================================================\n// AegisChat React SDK - useAutoRead Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { channelsApi } from '../services/api';\n\nconst SESSION_STORAGE_KEY = '@aegischat/activeChannel';\n\nexport interface UseAutoReadOptions {\n onMarkAsRead?: (channelId: string) => void;\n}\n\nexport interface UseAutoReadReturn {\n markAsRead: (channelId: string) => Promise<void>;\n markAllAsRead: () => Promise<void>;\n isFocused: boolean;\n}\n\nexport function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn {\n const [isFocused, setIsFocused] = useState(false);\n\n useEffect(() => {\n setIsFocused(typeof document !== 'undefined' && document.hasFocus());\n\n const handleFocus = () => setIsFocused(true);\n const handleBlur = () => setIsFocused(false);\n\n window.addEventListener('focus', handleFocus);\n window.addEventListener('blur', handleBlur);\n\n return () => {\n window.removeEventListener('focus', handleFocus);\n window.removeEventListener('blur', handleBlur);\n };\n }, []);\n\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n const activeChannelId = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (activeChannelId) {\n markAsRead(activeChannelId);\n }\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => document.removeEventListener('visibilitychange', handleVisibilityChange);\n }, []);\n\n const markAsRead = useCallback(async (channelId: string) => {\n if (!isFocused) return;\n try {\n await channelsApi.markAsRead(channelId);\n options.onMarkAsRead?.(channelId);\n } catch (error) {\n console.error('[AegisChat] useAutoRead: Failed to mark as read:', error);\n }\n }, [isFocused, options.onMarkAsRead]);\n\n const markAllAsRead = useCallback(async () => {\n if (!isFocused) return;\n try {\n const response = await channelsApi.list({});\n const channels = response.data.channels || [];\n await Promise.all(\n channels.filter((ch) => ch.unread_count > 0).map((ch) => channelsApi.markAsRead(ch.id))\n );\n } catch (error) {\n console.error('[AegisChat] useAutoRead: Failed to mark all as read:', error);\n }\n }, [isFocused]);\n\n return { markAsRead, markAllAsRead, isFocused };\n}\n\nexport default useAutoRead;\n","// ============================================================================\n// AegisChat React SDK - useChannels Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { channelsApi } from '../services/api';\nimport type { ChannelListItem, Channel } from '../types';\n\nexport interface UseChannelsOptions {\n type?: 'direct' | 'public' | 'private';\n limit?: number;\n autoFetch?: boolean;\n onChannelCreated?: (channel: Channel) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseChannelsReturn {\n channels: ChannelListItem[];\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n getOrCreateDM: (userId: string) => Promise<Channel>;\n markAsRead: (channelId: string) => Promise<void>;\n}\n\nexport function useChannels(options: UseChannelsOptions = {}): UseChannelsReturn {\n const { type, limit = 20, autoFetch = true, onChannelCreated, onError } = options;\n\n const [channels, setChannels] = useState<ChannelListItem[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const fetchChannels = useCallback(async (signal?: AbortSignal) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const response = await channelsApi.list({ type, limit }, signal);\n if (signal?.aborted) return;\n setChannels(response.data.channels);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') return;\n const error = err instanceof Error ? err : new Error('Failed to fetch channels');\n setError(error);\n onError?.(error);\n } finally {\n if (!signal?.aborted) setIsLoading(false);\n }\n }, [type, limit, onError]);\n\n const refetch = useCallback(async () => {\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n await fetchChannels(controller.signal);\n }, [fetchChannels]);\n\n const getOrCreateDM = useCallback(async (userId: string): Promise<Channel> => {\n try {\n const response = await channelsApi.getOrCreateDM(userId);\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n fetchChannels(controller.signal);\n onChannelCreated?.(response.data);\n return response.data;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create DM');\n onError?.(error);\n throw error;\n }\n }, [fetchChannels, onChannelCreated, onError]);\n\n const markAsRead = useCallback(async (channelId: string): Promise<void> => {\n try {\n await channelsApi.markAsRead(channelId);\n setChannels((prev) => prev.map((ch) => ch.id === channelId ? { ...ch, unread_count: 0 } : ch));\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to mark as read');\n onError?.(error);\n throw error;\n }\n }, [onError]);\n\n useEffect(() => {\n if (autoFetch) {\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n fetchChannels(controller.signal);\n return () => controller.abort();\n }\n }, [autoFetch, fetchChannels]);\n\n return { channels, isLoading, error, refetch, getOrCreateDM, markAsRead };\n}\n\nexport default useChannels;\n","// ============================================================================\n// AegisChat React SDK - useMessages Hook\n// ============================================================================\n\nimport { useCallback, useState } from 'react';\nimport { messagesApi } from '../services/api';\nimport type { Message, MessagesResponse } from '../types';\n\nexport interface UseMessagesOptions {\n channelId: string;\n}\n\nexport interface UseMessagesReturn {\n messages: Message[];\n isLoading: boolean;\n hasMore: boolean;\n sendMessage: (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\nexport function useMessages(_options: UseMessagesOptions): UseMessagesReturn {\n const [messages, setMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasMore, setHasMore] = useState(true);\n\n const sendMessage = useCallback(async (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => {\n // Implementation would go here\n }, []);\n\n const loadMore = useCallback(async () => {\n // Implementation would go here\n }, []);\n\n return { messages, isLoading, hasMore, sendMessage, loadMore };\n}\n\nexport default useMessages;\n","// ============================================================================\n// AegisChat React SDK - useTypingIndicator Hook\n// ============================================================================\n\nimport { useCallback, useState } from 'react';\nimport type { TypingUser } from '../types';\n\nexport interface UseTypingIndicatorOptions {\n channelId: string;\n ws?: WebSocket | null;\n}\n\nexport interface UseTypingIndicatorReturn {\n typingUsers: TypingUser[];\n startTyping: () => void;\n stopTyping: () => void;\n}\n\nexport function useTypingIndicator(_options: UseTypingIndicatorOptions): UseTypingIndicatorReturn {\n const [typingUsers, setTypingUsers] = useState<TypingUser[]>([]);\n\n const startTyping = useCallback(() => {}, []);\n const stopTyping = useCallback(() => {}, []);\n\n return { typingUsers, startTyping, stopTyping };\n}\n\nexport default useTypingIndicator;\n","// ============================================================================\n// AegisChat React SDK - useReactions Hook\n// ============================================================================\n\nimport { useState } from 'react';\nimport type { ReactionSummary } from '../types';\n\nexport interface UseReactionsOptions {\n channelId: string;\n messageId: string;\n}\n\nexport interface UseReactionsReturn {\n reactions: ReactionSummary[];\n addReaction: (emoji: string) => Promise<void>;\n removeReaction: (emoji: string) => Promise<void>;\n}\n\nexport function useReactions(_options: UseReactionsOptions): UseReactionsReturn {\n const [reactions, setReactions] = useState<ReactionSummary[]>([]);\n\n const addReaction = async (_emoji: string) => {};\n const removeReaction = async (_emoji: string) => {};\n\n return { reactions, addReaction, removeReaction };\n}\n\nexport default useReactions;\n","// ============================================================================\n// AegisChat React SDK - useFileUpload Hook\n// ============================================================================\n\nimport { useState } from 'react';\nimport type { FileAttachment, UploadProgress } from '../types';\n\nexport interface UseFileUploadOptions {\n channelId: string;\n}\n\nexport interface UseFileUploadReturn {\n uploadProgress: UploadProgress[];\n upload: (file: File) => Promise<FileAttachment | null>;\n}\n\nexport function useFileUpload(_options: UseFileUploadOptions): UseFileUploadReturn {\n const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);\n\n const upload = async (_file: File): Promise<FileAttachment | null> => null;\n\n return { uploadProgress, upload };\n}\n\nexport default useFileUpload;\n","// ============================================================================\n// AegisChat React SDK - useMentions Hook\n// ============================================================================\n\nexport interface UseMentionsOptions {}\n\nexport interface UseMentionsReturn {\n parseMentions: (content: string) => string[];\n highlightMentions: (content: string) => string;\n isUserMentioned: (content: string, userId: string) => boolean;\n}\n\nexport function useMentions(_options: UseMentionsOptions = {}): UseMentionsReturn {\n const parseMentions = (content: string): string[] => {\n const mentionRegex = /@\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n const mentions: string[] = [];\n let match;\n while ((match = mentionRegex.exec(content)) !== null) {\n mentions.push(match[2]);\n }\n return mentions;\n };\n\n const highlightMentions = (content: string): string => {\n return content.replace(/@\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<span class=\"mention\">@$1</span>');\n };\n\n const isUserMentioned = (content: string, userId: string): boolean => {\n const mentions = parseMentions(content);\n return mentions.includes(userId);\n };\n\n return { parseMentions, highlightMentions, isUserMentioned };\n}\n\nexport default useMentions;\n"],"mappings":"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","names":["useCallback","useEffect","useState","SESSION_STORAGE_KEY","useState","useEffect","useCallback","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","error","useEffect","useCallback","useState","useCallback","useState","useState","useState"]}
1
+ {"version":3,"sources":["../src/hooks/useChat.ts","../src/services/api.ts","../src/hooks/useAutoRead.ts","../src/hooks/useChannels.ts","../src/hooks/useMessages.ts","../src/hooks/useTypingIndicator.ts","../src/hooks/useReactions.ts","../src/hooks/useFileUpload.ts","../src/hooks/useMentions.ts"],"sourcesContent":["// ============================================================================\n// AegisChat React SDK - useChat Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { chatApi, channelsApi, messagesApi, filesApi, configureApiClient } from '../services/api';\nimport type {\n AegisConfig,\n ChatSession,\n ChannelListItem,\n Message,\n MessagesResponse,\n TypingUser,\n UserSummary,\n FileAttachment,\n UploadProgress,\n MessageSummary,\n} from '../types';\n\nconst TYPING_TIMEOUT = 3000;\nconst RECONNECT_INTERVAL = 3000;\nconst MAX_RECONNECT_ATTEMPTS = 5;\nconst MAX_RECONNECT_DELAY = 30000;\nconst PING_INTERVAL = 30000;\nconst SESSION_STORAGE_KEY = '@aegischat/activeChannel';\n\nexport interface UseChatOptions {\n config?: AegisConfig;\n role: 'lawyer' | 'client';\n clientId?: string;\n \n initialSession?: ChatSession | null;\n autoConnect?: boolean;\n onMessage?: (message: Message) => void;\n onTyping?: (channelId: string, user: TypingUser) => void;\n onConnectionChange?: (connected: boolean) => void;\n}\n\nexport interface UseChatReturn {\n session: ChatSession | null;\n isConnected: boolean;\n isConnecting: boolean;\n channels: ChannelListItem[];\n messages: Message[];\n activeChannelId: string | null;\n typingUsers: TypingUser[];\n isLoadingChannels: boolean;\n isLoadingMessages: boolean;\n hasMoreMessages: boolean;\n uploadProgress: UploadProgress[];\n connect: () => Promise<void>;\n disconnect: () => void;\n selectChannel: (channelId: string) => void;\n sendMessage: (content: string, options?: { type?: string; parent_id?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n sendMessageWithFiles: (content: string, files: File[], options?: { type?: string; parent_id?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n uploadFile: (file: File) => Promise<FileAttachment | null>;\n loadMoreMessages: () => Promise<void>;\n startTyping: () => void;\n stopTyping: () => void;\n refreshChannels: () => Promise<void>;\n createDMWithUser: (userId: string) => Promise<string | null>;\n retryMessage: (tempId: string) => Promise<void>;\n deleteFailedMessage: (tempId: string) => void;\n markAsRead: (channelId: string) => Promise<void>;\n}\n\nexport function useChat(options: UseChatOptions): UseChatReturn {\n const { config, role, clientId, initialSession, autoConnect = true, onMessage, onTyping, onConnectionChange } = options;\n\n const [session, setSession] = useState<ChatSession | null>(initialSession ?? null);\n const [isConnected, setIsConnected] = useState(false);\n const [isConnecting, setIsConnecting] = useState(false);\n const [activeChannelId, setActiveChannelIdState] = useState<string | null>(null);\n const [channels, setChannels] = useState<ChannelListItem[]>([]);\n const [messages, setMessages] = useState<Message[]>([]);\n const [typingUsers, setTypingUsers] = useState<Record<string, TypingUser[]>>({});\n const [isLoadingChannels, setIsLoadingChannels] = useState(false);\n const [isLoadingMessages, setIsLoadingMessages] = useState(false);\n const [hasMoreMessages, setHasMoreMessages] = useState(true);\n const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);\n\n const wsRef = useRef<WebSocket | null>(null);\n const reconnectAttempts = useRef(0);\n const reconnectTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pingInterval = useRef<ReturnType<typeof setInterval> | null>(null);\n const typingTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const isManualDisconnect = useRef(false);\n const oldestMessageId = useRef<string | null>(null);\n const activeChannelIdRef = useRef<string | null>(null);\n const configRef = useRef(config);\n const sessionRef = useRef<ChatSession | null>(initialSession ?? null);\n\n // Configure API client when initialSession is provided (so channelsApi/messagesApi/filesApi use correct baseUrl)\n if (initialSession && !config) {\n configureApiClient({\n baseUrl: initialSession.api_url,\n getAccessToken: async () => sessionRef.current?.access_token || \"\",\n });\n }\n\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n useEffect(() => {\n activeChannelIdRef.current = activeChannelId;\n }, [activeChannelId]);\n\n\n const getActiveChannelId = useCallback((): string | null => {\n if (typeof window === 'undefined') return null;\n return sessionStorage.getItem(SESSION_STORAGE_KEY);\n }, []);\n\n const setActiveChannelId = useCallback((id: string | null) => {\n setActiveChannelIdState(id);\n if (typeof window !== 'undefined') {\n if (id) {\n sessionStorage.setItem(SESSION_STORAGE_KEY, id);\n } else {\n sessionStorage.removeItem(SESSION_STORAGE_KEY);\n }\n }\n }, []);\n\n const fetchFromComms = useCallback(async <T>(path: string, fetchOptions: RequestInit = {}): Promise<T> => {\n const currentSession = sessionRef.current;\n if (!currentSession) {\n throw new Error('Chat session not initialized');\n }\n\n const response = await fetch(`${currentSession.api_url}${path}`, {\n ...fetchOptions,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${currentSession.access_token}`,\n ...fetchOptions.headers,\n },\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n const data = await response.json();\n return data.data || data;\n }, []);\n\n const clearTimers = useCallback(() => {\n if (reconnectTimeout.current) {\n clearTimeout(reconnectTimeout.current);\n reconnectTimeout.current = null;\n }\n if (pingInterval.current) {\n clearInterval(pingInterval.current);\n pingInterval.current = null;\n }\n }, []);\n\n const handleWebSocketMessage = useCallback((data: { type: string; payload: unknown }) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n console.log('[AegisChat] WebSocket message received:', data.type, data);\n\n switch (data.type) {\n case 'message.new': {\n const newMessage = data.payload as Message;\n if (newMessage.channel_id === currentActiveChannelId) {\n setMessages((prev) => {\n const existingIndex = prev.findIndex(\n (m) => m.tempId && m.content === newMessage.content && m.status === 'sending'\n );\n if (existingIndex !== -1) {\n const updated = [...prev];\n updated[existingIndex] = { ...newMessage, status: 'sent' };\n return updated;\n }\n if (prev.some((m) => m.id === newMessage.id)) return prev;\n return [...prev, { ...newMessage, status: 'delivered' }];\n });\n onMessage?.(newMessage);\n }\n setChannels((prev) => {\n const updated = prev.map((ch) =>\n ch.id === newMessage.channel_id\n ? {\n ...ch,\n last_message: {\n id: newMessage.id,\n content: newMessage.content,\n created_at: newMessage.created_at,\n sender: { id: newMessage.sender_id, display_name: 'Unknown', status: 'online' as const },\n } as MessageSummary,\n unread_count: ch.id === currentActiveChannelId ? 0 : ch.unread_count + 1,\n }\n : ch\n );\n return updated.sort((a, b) => {\n const timeA = a.last_message?.created_at || '';\n const timeB = b.last_message?.created_at || '';\n return timeB.localeCompare(timeA);\n });\n });\n break;\n }\n case 'message.updated': {\n const updatedMessage = data.payload as Message;\n setMessages((prev) => prev.map((m) => (m.id === updatedMessage.id ? updatedMessage : m)));\n break;\n }\n case 'message.deleted': {\n const { message_id } = data.payload as { message_id: string };\n setMessages((prev) => prev.map((m) => (m.id === message_id ? { ...m, deleted: true } : m)));\n break;\n }\n case 'message.delivered':\n case 'message.read': {\n const { message_id, channel_id, status } = data.payload as { message_id: string; channel_id: string; status: string };\n if (channel_id === currentActiveChannelId) {\n setMessages((prev) =>\n prev.map((m) => (m.id === message_id ? { ...m, status: (status as Message['status']) || 'delivered' } : m))\n );\n }\n break;\n }\n case 'message.delivered.batch':\n case 'message.read.batch': {\n const { channel_id } = data.payload as { channel_id: string };\n if (channel_id === currentActiveChannelId) {\n setMessages((prev) =>\n prev.map((m) => (m.status === 'sent' || m.status === 'delivered' ? { ...m, status: data.type === 'message.delivered.batch' ? 'delivered' : 'read' } : m))\n );\n }\n break;\n }\n case 'typing.start': {\n const { channel_id, user } = data.payload as { channel_id: string; user: UserSummary };\n const typingUser: TypingUser = {\n id: user.id,\n displayName: user.display_name,\n avatarUrl: user.avatar_url,\n startedAt: Date.now(),\n };\n setTypingUsers((prev) => ({\n ...prev,\n [channel_id]: [...(prev[channel_id] || []).filter((u) => u.id !== user.id), typingUser],\n }));\n onTyping?.(channel_id, typingUser);\n break;\n }\n case 'typing.stop': {\n const { channel_id, user_id } = data.payload as { channel_id: string; user_id: string };\n setTypingUsers((prev) => ({\n ...prev,\n [channel_id]: (prev[channel_id] || []).filter((u) => u.id !== user_id),\n }));\n break;\n }\n case 'pong':\n break;\n default:\n console.log('[AegisChat] Unhandled message type:', data.type);\n }\n }, [onMessage, onTyping]);\n\n const connectWebSocket = useCallback(() => {\n const currentSession = sessionRef.current;\n if (!currentSession?.websocket_url || !currentSession?.access_token) {\n console.warn('[AegisChat] Cannot connect WebSocket - missing session or token');\n return;\n }\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n console.log('[AegisChat] WebSocket already open, skipping connection');\n return;\n }\n\n setIsConnecting(true);\n isManualDisconnect.current = false;\n\n const wsUrl = `${currentSession.websocket_url}?token=${currentSession.access_token}`;\n console.log('[AegisChat] Creating WebSocket connection to:', wsUrl);\n const ws = new WebSocket(wsUrl);\n\n ws.onopen = () => {\n console.log('[AegisChat] WebSocket connected');\n setIsConnected(true);\n setIsConnecting(false);\n reconnectAttempts.current = 0;\n onConnectionChange?.(true);\n\n pingInterval.current = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ type: 'ping' }));\n }\n }, PING_INTERVAL);\n\n if (activeChannelIdRef.current) {\n ws.send(JSON.stringify({ type: 'channel.join', payload: { channel_id: activeChannelIdRef.current } }));\n }\n };\n\n ws.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n handleWebSocketMessage(data);\n } catch (error) {\n console.error('[AegisChat] Failed to parse WebSocket message:', error);\n }\n };\n\n ws.onclose = () => {\n console.log('[AegisChat] WebSocket disconnected');\n setIsConnected(false);\n setIsConnecting(false);\n clearTimers();\n onConnectionChange?.(false);\n\n if (!isManualDisconnect.current && reconnectAttempts.current < MAX_RECONNECT_ATTEMPTS) {\n const delay = Math.min(RECONNECT_INTERVAL * Math.pow(2, reconnectAttempts.current), MAX_RECONNECT_DELAY);\n console.log(`[AegisChat] Reconnecting in ${delay}ms...`);\n reconnectTimeout.current = setTimeout(() => {\n reconnectAttempts.current++;\n connectWebSocket();\n }, delay);\n }\n };\n\n ws.onerror = (error) => {\n console.error('[AegisChat] WebSocket error:', error);\n };\n\n wsRef.current = ws;\n }, [clearTimers, handleWebSocketMessage, onConnectionChange]);\n\n const connect = useCallback(async () => {\n console.log('[AegisChat] connect() called');\n const targetSession = sessionRef.current ?? initialSession;\n if (!targetSession) {\n throw new Error('Either config or initialSession must be provided');\n }\n if (sessionRef.current) {\n console.log('[AegisChat] Session exists, calling connectWebSocket directly');\n connectWebSocket();\n return;\n }\n\n // If initialSession was provided but sessionRef wasn't set yet, use it directly\n console.log('[AegisChat] Using initialSession, calling connectWebSocket directly');\n connectWebSocket();\n }, [connectWebSocket]);\n\n const disconnect = useCallback(() => {\n isManualDisconnect.current = true;\n clearTimers();\n if (wsRef.current) {\n wsRef.current.close();\n wsRef.current = null;\n }\n setIsConnected(false);\n setSession(null);\n setChannels([]);\n setMessages([]);\n }, [clearTimers]);\n\n const refreshChannels = useCallback(async () => {\n const currentSession = sessionRef.current;\n if (!currentSession) return;\n\n setIsLoadingChannels(true);\n try {\n const response = await channelsApi.list({});\n setChannels(response.data.channels || []);\n } catch (error) {\n console.error('[AegisChat] Failed to fetch channels:', error);\n } finally {\n setIsLoadingChannels(false);\n }\n }, []);\n\n const selectChannel = useCallback(async (channelId: string) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n setActiveChannelId(channelId);\n setMessages([]);\n setHasMoreMessages(true);\n oldestMessageId.current = null;\n\n if (wsRef.current?.readyState === WebSocket.OPEN) {\n if (currentActiveChannelId) {\n wsRef.current.send(JSON.stringify({ type: 'channel.leave', payload: { channel_id: currentActiveChannelId } }));\n }\n wsRef.current.send(JSON.stringify({ type: 'channel.join', payload: { channel_id: channelId } }));\n }\n\n setIsLoadingMessages(true);\n try {\n const response = await fetchFromComms<MessagesResponse>(`/channels/${channelId}/messages?limit=50`);\n setMessages(response.messages || []);\n setHasMoreMessages(response.has_more);\n if (response.oldest_id) {\n oldestMessageId.current = response.oldest_id;\n }\n\n await markAsRead(channelId);\n\n setChannels((prev) => prev.map((ch) => (ch.id === channelId ? { ...ch, unread_count: 0 } : ch)));\n } catch (error) {\n console.error('[AegisChat] Failed to load messages:', error);\n setMessages([]);\n } finally {\n setIsLoadingMessages(false);\n }\n }, [setActiveChannelId, fetchFromComms]);\n\n const markAsRead = useCallback(async (channelId: string) => {\n try {\n await fetchFromComms(`/channels/${channelId}/read`, { method: 'POST' });\n } catch (error) {\n console.error('[AegisChat] Failed to mark as read:', error);\n }\n }, [fetchFromComms]);\n\n const loadMoreMessages = useCallback(async () => {\n if (!activeChannelId || !hasMoreMessages || isLoadingMessages) return;\n\n setIsLoadingMessages(true);\n try {\n const params = oldestMessageId.current ? `?before=${oldestMessageId.current}&limit=50` : '?limit=50';\n const response = await fetchFromComms<MessagesResponse>(`/channels/${activeChannelId}/messages${params}`);\n setMessages((prev) => [...(response.messages || []), ...prev]);\n setHasMoreMessages(response.has_more);\n if (response.oldest_id) {\n oldestMessageId.current = response.oldest_id;\n }\n } catch (error) {\n console.error('[AegisChat] Failed to load more messages:', error);\n } finally {\n setIsLoadingMessages(false);\n }\n }, [activeChannelId, hasMoreMessages, isLoadingMessages, fetchFromComms]);\n\n const sendMessage = useCallback(async (\n content: string,\n msgOptions: { type?: string; parent_id?: string; metadata?: Record<string, unknown> } = {}\n ) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n const currentSession = sessionRef.current;\n if (!currentActiveChannelId || !content.trim() || !currentSession) return;\n\n const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const trimmedContent = content.trim();\n\n const optimisticMessage: Message = {\n id: tempId,\n tempId,\n channel_id: currentActiveChannelId,\n sender_id: currentSession.comms_user_id,\n content: trimmedContent,\n type: (msgOptions.type as Message['type']) || 'text',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n status: 'sending',\n metadata: msgOptions.metadata || {},\n };\n\n setMessages((prev) => [...prev, optimisticMessage]);\n\n const now = new Date().toISOString();\n setChannels((prev) => {\n const updated = prev.map((ch) =>\n ch.id === currentActiveChannelId\n ? {\n ...ch,\n last_message: {\n id: tempId,\n content: trimmedContent,\n created_at: now,\n sender: { id: currentSession.comms_user_id, display_name: 'You', status: 'online' as const },\n },\n }\n : ch\n );\n return updated.sort((a, b) => {\n const timeA = a.last_message?.created_at || '';\n const timeB = b.last_message?.created_at || '';\n return timeB.localeCompare(timeA);\n });\n });\n\n try {\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({ content: trimmedContent, type: msgOptions.type || 'text', parent_id: msgOptions.parent_id, metadata: msgOptions.metadata }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to send message:', error);\n setMessages((prev) =>\n prev.map((m) =>\n m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m\n )\n );\n throw error;\n }\n }, [fetchFromComms]);\n\n const uploadFile = useCallback(async (file: File): Promise<FileAttachment | null> => {\n const currentSession = sessionRef.current;\n if (!currentSession) return null;\n\n const fileId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n\n setUploadProgress((prev) => [...prev, { fileId, fileName: file.name, progress: 0, status: 'pending' }]);\n\n try {\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: 'uploading', progress: 10 } : p));\n\n const uploadUrlResponse = await fetchFromComms<{ upload_url: string; file_id: string; expires_at: string }>('/files/upload-url', {\n method: 'POST',\n body: JSON.stringify({ file_name: file.name, file_type: file.type || 'application/octet-stream', file_size: file.size }),\n });\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, fileId: uploadUrlResponse.file_id, progress: 30 } : p));\n\n const uploadResponse = await fetch(uploadUrlResponse.upload_url, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': file.type || 'application/octet-stream' },\n });\n\n if (!uploadResponse.ok) throw new Error(`Upload failed: ${uploadResponse.statusText}`);\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: 'confirming', progress: 70 } : p));\n\n const confirmResponse = await fetchFromComms<{ file: FileAttachment }>('/files', {\n method: 'POST',\n body: JSON.stringify({ file_id: uploadUrlResponse.file_id }),\n });\n\n setUploadProgress((prev) => prev.map((p) => p.fileId === uploadUrlResponse.file_id ? { ...p, status: 'complete', progress: 100 } : p));\n setTimeout(() => setUploadProgress((prev) => prev.filter((p) => p.fileId !== uploadUrlResponse.file_id)), 2000);\n\n return confirmResponse.file;\n } catch (error) {\n console.error('[AegisChat] Failed to upload file:', error);\n setUploadProgress((prev) => prev.map((p) => p.fileId === fileId ? { ...p, status: 'error', error: error instanceof Error ? error.message : 'Upload failed' } : p));\n setTimeout(() => setUploadProgress((prev) => prev.filter((p) => p.fileId !== fileId)), 5000);\n return null;\n }\n }, [fetchFromComms]);\n\n const sendMessageWithFiles = useCallback(async (\n content: string,\n files: File[],\n msgOptions: { type?: string; parent_id?: string; metadata?: Record<string, unknown> } = {}\n ) => {\n const currentActiveChannelId = activeChannelIdRef.current;\n const currentSession = sessionRef.current;\n if (!currentActiveChannelId || (!content.trim() && files.length === 0) || !currentSession) return;\n\n const tempId = `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n const trimmedContent = content.trim();\n\n const optimisticMessage: Message = {\n id: tempId,\n tempId,\n channel_id: currentActiveChannelId,\n sender_id: currentSession.comms_user_id,\n content: trimmedContent || `Uploading ${files.length} file(s)...`,\n type: 'file',\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString(),\n status: 'sending',\n metadata: { ...msgOptions.metadata, files: files.map((f) => ({ id: `temp-${f.name}`, filename: f.name, mime_type: f.type, size: f.size, url: '' })) },\n };\n\n setMessages((prev) => [...prev, optimisticMessage]);\n\n try {\n const uploadedFiles: FileAttachment[] = [];\n for (const file of files) {\n const attachment = await uploadFile(file);\n if (attachment) uploadedFiles.push(attachment);\n }\n\n const messageType = uploadedFiles.length > 0 && !trimmedContent ? 'file' : 'text';\n\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({\n content: trimmedContent || (uploadedFiles.length > 0 ? `Shared ${uploadedFiles.length} file(s)` : ''),\n type: msgOptions.type || messageType,\n parent_id: msgOptions.parent_id,\n metadata: { ...msgOptions.metadata, files: uploadedFiles },\n file_ids: uploadedFiles.map((f) => f.id),\n }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to send message with files:', error);\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m));\n throw error;\n }\n }, [fetchFromComms, uploadFile]);\n\n const stopTyping = useCallback(() => {\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!currentActiveChannelId || !wsRef.current) return;\n wsRef.current.send(JSON.stringify({ type: 'typing.stop', payload: { channel_id: currentActiveChannelId } }));\n if (typingTimeout.current) {\n clearTimeout(typingTimeout.current);\n typingTimeout.current = null;\n }\n }, []);\n\n const startTyping = useCallback(() => {\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!currentActiveChannelId || !wsRef.current) return;\n wsRef.current.send(JSON.stringify({ type: 'typing.start', payload: { channel_id: currentActiveChannelId } }));\n if (typingTimeout.current) clearTimeout(typingTimeout.current);\n typingTimeout.current = setTimeout(stopTyping, TYPING_TIMEOUT);\n }, [stopTyping]);\n\n const createDMWithUser = useCallback(async (userId: string): Promise<string | null> => {\n try {\n const channel = await fetchFromComms<{ id: string }>('/channels/dm', {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n });\n await refreshChannels();\n return channel.id;\n } catch (error) {\n console.error('[AegisChat] Failed to create DM:', error);\n return null;\n }\n }, [fetchFromComms, refreshChannels]);\n\n const retryMessage = useCallback(async (tempId: string) => {\n const failedMessage = messages.find((m) => m.tempId === tempId && m.status === 'failed');\n const currentActiveChannelId = activeChannelIdRef.current;\n if (!failedMessage || !currentActiveChannelId) return;\n\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'sending', errorMessage: undefined } : m));\n\n try {\n await fetchFromComms<Message>(`/channels/${currentActiveChannelId}/messages`, {\n method: 'POST',\n body: JSON.stringify({ content: failedMessage.content, type: failedMessage.type, metadata: failedMessage.metadata }),\n });\n } catch (error) {\n console.error('[AegisChat] Failed to retry message:', error);\n setMessages((prev) => prev.map((m) => m.tempId === tempId ? { ...m, status: 'failed', errorMessage: error instanceof Error ? error.message : 'Failed to send' } : m));\n }\n }, [messages, fetchFromComms]);\n\n const deleteFailedMessage = useCallback((tempId: string) => {\n setMessages((prev) => prev.filter((m) => m.tempId !== tempId));\n }, []);\n\n // Note: connect() is called by the wrapper hook, not automatically.\n // This allows the wrapper to fetch the session externally first.\n\n useEffect(() => {\n if (session && !isConnected && !isConnecting && autoConnect) {\n connectWebSocket();\n }\n }, [session, isConnected, isConnecting, autoConnect, connectWebSocket]);\n\n useEffect(() => {\n if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {\n wsRef.current.onmessage = (event) => {\n try {\n const data = JSON.parse(event.data);\n handleWebSocketMessage(data);\n } catch (error) {\n console.error('[AegisChat] Failed to parse WebSocket message:', error);\n }\n };\n }\n }, [handleWebSocketMessage]);\n\n useEffect(() => {\n if (isConnected && channels.length === 0) {\n refreshChannels();\n }\n }, [isConnected, channels.length, refreshChannels]);\n\n useEffect(() => {\n const storedActiveChannel = getActiveChannelId();\n if (storedActiveChannel && !activeChannelId) {\n selectChannel(storedActiveChannel);\n }\n }, [getActiveChannelId, activeChannelId, selectChannel]);\n\n useEffect(() => {\n return () => {\n clearTimers();\n if (typingTimeout.current) clearTimeout(typingTimeout.current);\n if (wsRef.current) {\n isManualDisconnect.current = true;\n wsRef.current.close();\n wsRef.current = null;\n }\n };\n }, [clearTimers]);\n\n return {\n session,\n isConnected,\n isConnecting,\n channels,\n messages,\n activeChannelId,\n typingUsers: activeChannelId ? typingUsers[activeChannelId] || [] : [],\n isLoadingChannels,\n isLoadingMessages,\n hasMoreMessages,\n uploadProgress,\n connect,\n disconnect,\n selectChannel,\n sendMessage,\n sendMessageWithFiles,\n uploadFile,\n loadMoreMessages,\n startTyping,\n stopTyping,\n refreshChannels,\n createDMWithUser,\n retryMessage,\n deleteFailedMessage,\n markAsRead,\n };\n}\n\nexport default useChat;\n","// ============================================================================\n// AegisChat React SDK - API Service\n// ============================================================================\n\nimport type {\n ApiResponse,\n ChatSession,\n ChatConnectParams,\n Channel,\n ChannelListItem,\n Message,\n MessagesResponse,\n UserSummary,\n ReactionSummary,\n FileAttachment,\n UploadUrlResponse,\n} from '../types';\n\nlet baseUrl = '';\nlet getAccessToken: () => Promise<string> | string = () => '';\nlet onUnauthorized: (() => void) | undefined;\n\nexport function configureApiClient(config: {\n baseUrl: string;\n getAccessToken: () => Promise<string> | string;\n onUnauthorized?: () => void;\n}): void {\n baseUrl = config.baseUrl;\n getAccessToken = config.getAccessToken;\n onUnauthorized = config.onUnauthorized;\n}\n\nasync function fetchWithAuth<T>(\n path: string,\n options: RequestInit = {}\n): Promise<T> {\n const token = await (typeof getAccessToken === 'function' \n ? getAccessToken() \n : getAccessToken);\n\n const response = await fetch(`${baseUrl}${path}`, {\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n ...options.headers,\n },\n });\n\n if (response.status === 401) {\n onUnauthorized?.();\n throw new Error('Unauthorized');\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.message || `HTTP ${response.status}`);\n }\n\n return response.json();\n}\n\nexport const chatApi = {\n /**\n * Connect to chat session\n */\n async connect(\n params: ChatConnectParams,\n signal?: AbortSignal\n ): Promise<ApiResponse<ChatSession>> {\n return fetchWithAuth('/api/v1/chat/connect', {\n method: 'POST',\n body: JSON.stringify(params),\n signal,\n });\n },\n\n /**\n * Refresh access token\n */\n async refreshToken(\n refreshToken: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ access_token: string; expires_in: number }>> {\n return fetchWithAuth('/api/v1/chat/refresh', {\n method: 'POST',\n body: JSON.stringify({ refresh_token: refreshToken }),\n signal,\n });\n },\n};\n\nexport const channelsApi = {\n /**\n * List channels\n */\n async list(\n options: { type?: string; limit?: number } = {},\n signal?: AbortSignal\n ): Promise<ApiResponse<{ channels: ChannelListItem[] }>> {\n const params = new URLSearchParams();\n if (options.type) params.append('type', options.type);\n if (options.limit) params.append('limit', String(options.limit));\n const query = params.toString() ? `?${params.toString()}` : '';\n return fetchWithAuth(`/api/v1/channels${query}`, { signal });\n },\n\n /**\n * Get channel by ID\n */\n async get(channelId: string, signal?: AbortSignal): Promise<ApiResponse<Channel>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}`, { signal });\n },\n\n /**\n * Get or create DM channel\n */\n async getOrCreateDM(\n userId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth('/api/v1/channels/dm', {\n method: 'POST',\n body: JSON.stringify({ user_id: userId }),\n signal,\n });\n },\n\n /**\n * Create channel\n */\n async create(\n data: { name: string; type?: string; description?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth('/api/v1/channels', {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Mark channel as read\n */\n async markAsRead(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ unread_count: number }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/read`, {\n method: 'POST',\n signal,\n });\n },\n\n /**\n * Get channel members\n */\n async getMembers(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ members: UserSummary[] }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/members`, { signal });\n },\n\n /**\n * Update channel\n */\n async update(\n channelId: string,\n data: { name?: string; description?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Channel>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n signal,\n });\n },\n};\n\nexport const messagesApi = {\n /**\n * List messages in a channel\n */\n async list(\n channelId: string,\n options: { limit?: number; before?: string } = {},\n signal?: AbortSignal\n ): Promise<ApiResponse<MessagesResponse>> {\n const params = new URLSearchParams();\n if (options.limit) params.append('limit', String(options.limit));\n if (options.before) params.append('before', options.before);\n const query = params.toString() ? `?${params.toString()}` : '';\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages${query}`, { signal });\n },\n\n /**\n * Send a message\n */\n async send(\n channelId: string,\n data: { content: string; type?: string; parent_id?: string; metadata?: Record<string, unknown>; file_ids?: string[] },\n signal?: AbortSignal\n ): Promise<ApiResponse<Message>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages`, {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Update a message\n */\n async update(\n channelId: string,\n messageId: string,\n data: { content?: string; metadata?: Record<string, unknown> },\n signal?: AbortSignal\n ): Promise<ApiResponse<Message>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {\n method: 'PATCH',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Delete a message\n */\n async delete(\n channelId: string,\n messageId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}`, {\n method: 'DELETE',\n signal,\n });\n },\n\n /**\n * Mark messages as delivered\n */\n async markDelivered(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/delivered`, {\n method: 'POST',\n signal,\n });\n },\n\n /**\n * Mark messages as read\n */\n async markRead(\n channelId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ success: boolean }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/read`, {\n method: 'POST',\n signal,\n });\n },\n};\n\nexport const reactionsApi = {\n /**\n * Add reaction to a message\n */\n async add(\n channelId: string,\n messageId: string,\n emoji: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {\n return fetchWithAuth(`/api/v1/channels/${channelId}/messages/${messageId}/reactions`, {\n method: 'POST',\n body: JSON.stringify({ emoji }),\n signal,\n });\n },\n\n /**\n * Remove reaction from a message\n */\n async remove(\n channelId: string,\n messageId: string,\n emoji: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ reactions: ReactionSummary[] }>> {\n return fetchWithAuth(\n `/api/v1/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,\n { method: 'DELETE', signal }\n );\n },\n};\n\nexport const filesApi = {\n /**\n * Get upload URL\n */\n async getUploadUrl(\n data: { file_name: string; file_type: string; file_size: number },\n signal?: AbortSignal\n ): Promise<ApiResponse<UploadUrlResponse>> {\n return fetchWithAuth('/api/v1/files/upload-url', {\n method: 'POST',\n body: JSON.stringify(data),\n signal,\n });\n },\n\n /**\n * Confirm file upload\n */\n async confirm(\n fileId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ file: FileAttachment }>> {\n return fetchWithAuth('/api/v1/files', {\n method: 'POST',\n body: JSON.stringify({ file_id: fileId }),\n signal,\n });\n },\n\n /**\n * Get download URL\n */\n async getDownloadUrl(\n fileId: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ url: string; expires_at: string }>> {\n return fetchWithAuth(`/api/v1/files/${fileId}/download`, { signal });\n },\n};\n\nexport const usersApi = {\n /**\n * Search users\n */\n async search(\n query: string,\n signal?: AbortSignal\n ): Promise<ApiResponse<{ users: UserSummary[] }>> {\n return fetchWithAuth(`/api/v1/users/search?q=${encodeURIComponent(query)}`, { signal });\n },\n\n /**\n * Get user by ID\n */\n async get(userId: string, signal?: AbortSignal): Promise<ApiResponse<UserSummary>> {\n return fetchWithAuth(`/api/v1/users/${userId}`, { signal });\n },\n};\n\nexport default {\n chatApi,\n channelsApi,\n messagesApi,\n reactionsApi,\n filesApi,\n usersApi,\n configureApiClient,\n};\n","// ============================================================================\n// AegisChat React SDK - useAutoRead Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { channelsApi } from '../services/api';\n\nconst SESSION_STORAGE_KEY = '@aegischat/activeChannel';\n\nexport interface UseAutoReadOptions {\n onMarkAsRead?: (channelId: string) => void;\n}\n\nexport interface UseAutoReadReturn {\n markAsRead: (channelId: string) => Promise<void>;\n markAllAsRead: () => Promise<void>;\n isFocused: boolean;\n}\n\nexport function useAutoRead(options: UseAutoReadOptions = {}): UseAutoReadReturn {\n const [isFocused, setIsFocused] = useState(false);\n\n useEffect(() => {\n setIsFocused(typeof document !== 'undefined' && document.hasFocus());\n\n const handleFocus = () => setIsFocused(true);\n const handleBlur = () => setIsFocused(false);\n\n window.addEventListener('focus', handleFocus);\n window.addEventListener('blur', handleBlur);\n\n return () => {\n window.removeEventListener('focus', handleFocus);\n window.removeEventListener('blur', handleBlur);\n };\n }, []);\n\n useEffect(() => {\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n const activeChannelId = sessionStorage.getItem(SESSION_STORAGE_KEY);\n if (activeChannelId) {\n markAsRead(activeChannelId);\n }\n }\n };\n\n document.addEventListener('visibilitychange', handleVisibilityChange);\n return () => document.removeEventListener('visibilitychange', handleVisibilityChange);\n }, []);\n\n const markAsRead = useCallback(async (channelId: string) => {\n if (!isFocused) return;\n try {\n await channelsApi.markAsRead(channelId);\n options.onMarkAsRead?.(channelId);\n } catch (error) {\n console.error('[AegisChat] useAutoRead: Failed to mark as read:', error);\n }\n }, [isFocused, options.onMarkAsRead]);\n\n const markAllAsRead = useCallback(async () => {\n if (!isFocused) return;\n try {\n const response = await channelsApi.list({});\n const channels = response.data.channels || [];\n await Promise.all(\n channels.filter((ch) => ch.unread_count > 0).map((ch) => channelsApi.markAsRead(ch.id))\n );\n } catch (error) {\n console.error('[AegisChat] useAutoRead: Failed to mark all as read:', error);\n }\n }, [isFocused]);\n\n return { markAsRead, markAllAsRead, isFocused };\n}\n\nexport default useAutoRead;\n","// ============================================================================\n// AegisChat React SDK - useChannels Hook\n// ============================================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { channelsApi } from '../services/api';\nimport type { ChannelListItem, Channel } from '../types';\n\nexport interface UseChannelsOptions {\n type?: 'direct' | 'public' | 'private';\n limit?: number;\n autoFetch?: boolean;\n onChannelCreated?: (channel: Channel) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface UseChannelsReturn {\n channels: ChannelListItem[];\n isLoading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n getOrCreateDM: (userId: string) => Promise<Channel>;\n markAsRead: (channelId: string) => Promise<void>;\n}\n\nexport function useChannels(options: UseChannelsOptions = {}): UseChannelsReturn {\n const { type, limit = 20, autoFetch = true, onChannelCreated, onError } = options;\n\n const [channels, setChannels] = useState<ChannelListItem[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const fetchChannels = useCallback(async (signal?: AbortSignal) => {\n setIsLoading(true);\n setError(null);\n\n try {\n const response = await channelsApi.list({ type, limit }, signal);\n if (signal?.aborted) return;\n setChannels(response.data.channels);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') return;\n const error = err instanceof Error ? err : new Error('Failed to fetch channels');\n setError(error);\n onError?.(error);\n } finally {\n if (!signal?.aborted) setIsLoading(false);\n }\n }, [type, limit, onError]);\n\n const refetch = useCallback(async () => {\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n await fetchChannels(controller.signal);\n }, [fetchChannels]);\n\n const getOrCreateDM = useCallback(async (userId: string): Promise<Channel> => {\n try {\n const response = await channelsApi.getOrCreateDM(userId);\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n fetchChannels(controller.signal);\n onChannelCreated?.(response.data);\n return response.data;\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to create DM');\n onError?.(error);\n throw error;\n }\n }, [fetchChannels, onChannelCreated, onError]);\n\n const markAsRead = useCallback(async (channelId: string): Promise<void> => {\n try {\n await channelsApi.markAsRead(channelId);\n setChannels((prev) => prev.map((ch) => ch.id === channelId ? { ...ch, unread_count: 0 } : ch));\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to mark as read');\n onError?.(error);\n throw error;\n }\n }, [onError]);\n\n useEffect(() => {\n if (autoFetch) {\n if (abortControllerRef.current) abortControllerRef.current.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n fetchChannels(controller.signal);\n return () => controller.abort();\n }\n }, [autoFetch, fetchChannels]);\n\n return { channels, isLoading, error, refetch, getOrCreateDM, markAsRead };\n}\n\nexport default useChannels;\n","// ============================================================================\n// AegisChat React SDK - useMessages Hook\n// ============================================================================\n\nimport { useCallback, useState } from 'react';\nimport { messagesApi } from '../services/api';\nimport type { Message, MessagesResponse } from '../types';\n\nexport interface UseMessagesOptions {\n channelId: string;\n}\n\nexport interface UseMessagesReturn {\n messages: Message[];\n isLoading: boolean;\n hasMore: boolean;\n sendMessage: (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => Promise<void>;\n loadMore: () => Promise<void>;\n}\n\nexport function useMessages(_options: UseMessagesOptions): UseMessagesReturn {\n const [messages, setMessages] = useState<Message[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasMore, setHasMore] = useState(true);\n\n const sendMessage = useCallback(async (params: { content: string; type?: string; metadata?: Record<string, unknown> }) => {\n // Implementation would go here\n }, []);\n\n const loadMore = useCallback(async () => {\n // Implementation would go here\n }, []);\n\n return { messages, isLoading, hasMore, sendMessage, loadMore };\n}\n\nexport default useMessages;\n","// ============================================================================\n// AegisChat React SDK - useTypingIndicator Hook\n// ============================================================================\n\nimport { useCallback, useState } from 'react';\nimport type { TypingUser } from '../types';\n\nexport interface UseTypingIndicatorOptions {\n channelId: string;\n ws?: WebSocket | null;\n}\n\nexport interface UseTypingIndicatorReturn {\n typingUsers: TypingUser[];\n startTyping: () => void;\n stopTyping: () => void;\n}\n\nexport function useTypingIndicator(_options: UseTypingIndicatorOptions): UseTypingIndicatorReturn {\n const [typingUsers, setTypingUsers] = useState<TypingUser[]>([]);\n\n const startTyping = useCallback(() => {}, []);\n const stopTyping = useCallback(() => {}, []);\n\n return { typingUsers, startTyping, stopTyping };\n}\n\nexport default useTypingIndicator;\n","// ============================================================================\n// AegisChat React SDK - useReactions Hook\n// ============================================================================\n\nimport { useState } from 'react';\nimport type { ReactionSummary } from '../types';\n\nexport interface UseReactionsOptions {\n channelId: string;\n messageId: string;\n}\n\nexport interface UseReactionsReturn {\n reactions: ReactionSummary[];\n addReaction: (emoji: string) => Promise<void>;\n removeReaction: (emoji: string) => Promise<void>;\n}\n\nexport function useReactions(_options: UseReactionsOptions): UseReactionsReturn {\n const [reactions, setReactions] = useState<ReactionSummary[]>([]);\n\n const addReaction = async (_emoji: string) => {};\n const removeReaction = async (_emoji: string) => {};\n\n return { reactions, addReaction, removeReaction };\n}\n\nexport default useReactions;\n","// ============================================================================\n// AegisChat React SDK - useFileUpload Hook\n// ============================================================================\n\nimport { useState } from 'react';\nimport type { FileAttachment, UploadProgress } from '../types';\n\nexport interface UseFileUploadOptions {\n channelId: string;\n}\n\nexport interface UseFileUploadReturn {\n uploadProgress: UploadProgress[];\n upload: (file: File) => Promise<FileAttachment | null>;\n}\n\nexport function useFileUpload(_options: UseFileUploadOptions): UseFileUploadReturn {\n const [uploadProgress, setUploadProgress] = useState<UploadProgress[]>([]);\n\n const upload = async (_file: File): Promise<FileAttachment | null> => null;\n\n return { uploadProgress, upload };\n}\n\nexport default useFileUpload;\n","// ============================================================================\n// AegisChat React SDK - useMentions Hook\n// ============================================================================\n\nexport interface UseMentionsOptions {}\n\nexport interface UseMentionsReturn {\n parseMentions: (content: string) => string[];\n highlightMentions: (content: string) => string;\n isUserMentioned: (content: string, userId: string) => boolean;\n}\n\nexport function useMentions(_options: UseMentionsOptions = {}): UseMentionsReturn {\n const parseMentions = (content: string): string[] => {\n const mentionRegex = /@\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n const mentions: string[] = [];\n let match;\n while ((match = mentionRegex.exec(content)) !== null) {\n mentions.push(match[2]);\n }\n return mentions;\n };\n\n const highlightMentions = (content: string): string => {\n return content.replace(/@\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<span class=\"mention\">@$1</span>');\n };\n\n const isUserMentioned = (content: string, userId: string): boolean => {\n const mentions = parseMentions(content);\n return mentions.includes(userId);\n };\n\n return { parseMentions, highlightMentions, isUserMentioned };\n}\n\nexport default useMentions;\n"],"mappings":"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","names":["useCallback","useEffect","useState","SESSION_STORAGE_KEY","useState","useEffect","useCallback","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","error","useEffect","useCallback","useState","useCallback","useState","useState","useState"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thrillee/aegischat",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "AegisChat React SDK - Real-time chat and messaging for your applications",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -3,7 +3,7 @@
3
3
  // ============================================================================
4
4
 
5
5
  import { useCallback, useEffect, useRef, useState } from 'react';
6
- import { chatApi, channelsApi, messagesApi, filesApi } from '../services/api';
6
+ import { chatApi, channelsApi, messagesApi, filesApi, configureApiClient } from '../services/api';
7
7
  import type {
8
8
  AegisConfig,
9
9
  ChatSession,
@@ -88,7 +88,15 @@ export function useChat(options: UseChatOptions): UseChatReturn {
88
88
  const oldestMessageId = useRef<string | null>(null);
89
89
  const activeChannelIdRef = useRef<string | null>(null);
90
90
  const configRef = useRef(config);
91
- const sessionRef = useRef<ChatSession | null>(null);
91
+ const sessionRef = useRef<ChatSession | null>(initialSession ?? null);
92
+
93
+ // Configure API client when initialSession is provided (so channelsApi/messagesApi/filesApi use correct baseUrl)
94
+ if (initialSession && !config) {
95
+ configureApiClient({
96
+ baseUrl: initialSession.api_url,
97
+ getAccessToken: async () => sessionRef.current?.access_token || "",
98
+ });
99
+ }
92
100
 
93
101
  useEffect(() => {
94
102
  configRef.current = config;
@@ -98,9 +106,6 @@ export function useChat(options: UseChatOptions): UseChatReturn {
98
106
  activeChannelIdRef.current = activeChannelId;
99
107
  }, [activeChannelId]);
100
108
 
101
- useEffect(() => {
102
- sessionRef.current = session;
103
- }, [session]);
104
109
 
105
110
  const getActiveChannelId = useCallback((): string | null => {
106
111
  if (typeof window === 'undefined') return null;
@@ -279,6 +284,7 @@ export function useChat(options: UseChatOptions): UseChatReturn {
279
284
  ws.onopen = () => {
280
285
  console.log('[AegisChat] WebSocket connected');
281
286
  setIsConnected(true);
287
+ setIsConnecting(false);
282
288
  reconnectAttempts.current = 0;
283
289
  onConnectionChange?.(true);
284
290
 
@@ -305,6 +311,7 @@ export function useChat(options: UseChatOptions): UseChatReturn {
305
311
  ws.onclose = () => {
306
312
  console.log('[AegisChat] WebSocket disconnected');
307
313
  setIsConnected(false);
314
+ setIsConnecting(false);
308
315
  clearTimers();
309
316
  onConnectionChange?.(false);
310
317
 
@@ -327,7 +334,8 @@ export function useChat(options: UseChatOptions): UseChatReturn {
327
334
 
328
335
  const connect = useCallback(async () => {
329
336
  console.log('[AegisChat] connect() called');
330
- if (!sessionRef.current && !config) {
337
+ const targetSession = sessionRef.current ?? initialSession;
338
+ if (!targetSession) {
331
339
  throw new Error('Either config or initialSession must be provided');
332
340
  }
333
341
  if (sessionRef.current) {
@@ -336,19 +344,10 @@ export function useChat(options: UseChatOptions): UseChatReturn {
336
344
  return;
337
345
  }
338
346
 
339
- try {
340
- setIsConnecting(true);
341
- console.log('[AegisChat] Fetching chat session...');
342
- const result = await chatApi.connect({ role, client_id: clientId });
343
- console.log('[AegisChat] Chat session received:', result);
344
- setSession(result.data);
345
- setIsConnecting(false);
346
- } catch (error) {
347
- console.error('[AegisChat] Failed to get chat session:', error);
348
- setIsConnecting(false);
349
- throw error;
350
- }
351
- }, [role, clientId, connectWebSocket]);
347
+ // If initialSession was provided but sessionRef wasn't set yet, use it directly
348
+ console.log('[AegisChat] Using initialSession, calling connectWebSocket directly');
349
+ connectWebSocket();
350
+ }, [connectWebSocket]);
352
351
 
353
352
  const disconnect = useCallback(() => {
354
353
  isManualDisconnect.current = true;
@@ -655,10 +654,8 @@ export function useChat(options: UseChatOptions): UseChatReturn {
655
654
  setMessages((prev) => prev.filter((m) => m.tempId !== tempId));
656
655
  }, []);
657
656
 
658
- // Effects
659
- useEffect(() => {
660
- connect();
661
- }, []);
657
+ // Note: connect() is called by the wrapper hook, not automatically.
658
+ // This allows the wrapper to fetch the session externally first.
662
659
 
663
660
  useEffect(() => {
664
661
  if (session && !isConnected && !isConnecting && autoConnect) {