@nexo-labs/chat-agent 1.9.9 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react.d.mts +39 -39
- package/dist/react.d.mts.map +1 -1
- package/dist/react.mjs +2 -2
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/NexoPayloadChatAdapter.ts +17 -17
- package/src/components/SourcesList.tsx +1 -11
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react.mjs","names":["requestBody: Record<string, unknown>","MarkdownText: FC<MarkdownTextProps>","defaultCollectionResolver: Required<CollectionTypeResolver>","data: ChunkData","DefaultLink: LinkComponent","DefaultImage: ImageComponent","itemVariants","metadata: { section?: string; path?: string }","RelevanceBar: React.FC<{ score: number }>","BreadcrumbPath: React.FC<{ path: string }>","SectionPill: React.FC<{ section: string }>","MetadataPills: React.FC<{ metadata: { section?: string; path?: string } | null }>","ExpandedContentBody: React.FC<{\n isLoading: boolean\n error: string | null\n displayContent: string\n cleanContent: string\n metadata: { section?: string; path?: string } | null\n expandedSource: Source\n getContentType: (type: string) => string\n renderViewMore?: SourcesListProps['renderViewMore']\n handleViewMore: () => void\n generateHref: SourcesListProps['generateHref']\n LinkComponent?: LinkComponent\n}>","ExpandedSourceCard: React.FC<{\n expandedSource: Source\n expandedSourceId: string\n loadedContent: string\n getChunkState: ReturnType<typeof useChunkLoader>['getChunkState']\n getIcon: (type: string) => React.ReactNode\n getLabel: (type: string) => string\n getContentType: (type: string) => string\n onClose: () => void\n handleViewMore: () => void\n renderViewMore?: SourcesListProps['renderViewMore']\n generateHref: SourcesListProps['generateHref']\n LinkComponent?: LinkComponent\n}>","SourceListItem: React.FC<{\n source: Source\n idx: number\n getIcon: (type: string) => React.ReactNode\n getLabel: (type: string) => string\n onClick: (id: string) => void\n}>","SourcesList: React.FC<SourcesListProps>","Thread: FC<ThreadProps>","ThreadWelcome: FC<ThreadWelcomeProps>","SuggestionCard: FC<SuggestionCardProps>","LimitAlert: FC","TokenUsageBar: FC","TypingIndicator: FC","Composer: FC","UserMessage: FC","AssistantMessage: FC","userMessage: Message","receivedSources: Source[]","err: unknown","document","DocumentSelector","ChatMenuDropdown","ChatInterface","FloatingChatButton","FloatingChatPanel"],"sources":["../src/adapters/MockAdapter.ts","../src/adapters/NexoPayloadChatAdapter.ts","../src/components/assistant-ui/markdown-text.tsx","../src/lib/utils.ts","../src/hooks/useChatSession.ts","../src/components/chat-context.tsx","../src/hooks/useChunkLoader.ts","../src/types/components.tsx","../src/components/buttons/ViewMoreLink.tsx","../src/components/SourcesList.tsx","../src/components/assistant-ui/thread.tsx","../src/components/buttons/FloatingChatButton.tsx","../src/hooks/useAssistantRuntime.ts","../src/components/useDocumentSelector.ts","../src/components/DocumentSelector.tsx","../src/components/ChatInterface.tsx","../src/components/ChatHistoryList.tsx","../src/components/ChatMenuDropdown.tsx","../src/components/FloatingChatPanel.tsx","../src/components/FloatingChatManager.tsx"],"sourcesContent":["import type {\n ChatAdapter,\n Message,\n PublicAgentInfo,\n SendMessageContext,\n SessionSummary,\n StreamCallbacks\n} from './ChatAdapter'\n\nexport class MockAdapter implements ChatAdapter {\n private sessions: Map<string, { id: string; title: string; messages: Message[] }> = new Map()\n\n constructor() {\n // Initial mock data\n this.sessions.set('mock-session-1', {\n id: 'mock-session-1',\n title: 'Mock Conversation',\n messages: [\n {\n role: 'assistant',\n content: 'Hello! I am a mock assistant. How can I help you?',\n timestamp: new Date(Date.now() - 1000000)\n }\n ]\n })\n }\n\n async sendMessage(\n message: string,\n context: SendMessageContext,\n callbacks: StreamCallbacks,\n signal?: AbortSignal\n ): Promise<void> {\n const convoId = context.conversationId || `mock-session-${Date.now()}`\n\n // Simulate initial delay\n await new Promise(resolve => setTimeout(resolve, 500))\n if (context.conversationId !== convoId) {\n callbacks.onConversationId?.(convoId)\n }\n\n if (!context.conversationId) {\n // Create new session if null\n this.sessions.set(convoId, {\n id: convoId,\n title: 'New Mock Chat',\n messages: []\n })\n }\n\n // Simulate streaming\n const responseText = `This is a mock response to: \"${message}\". \\n\\nI am simulating a stream of tokens.`\n const tokens = responseText.split(/(?=[\\s\\S])/) // Split by chars for smooth effect\n\n for (const token of tokens) {\n if (signal?.aborted) return\n await new Promise(resolve => setTimeout(resolve, 30))\n callbacks.onToken?.(token)\n }\n\n // Simulate sources\n callbacks.onSources?.([\n {\n id: 'mock-source-1',\n title: 'Mock Source Article',\n slug: 'mock-source-article',\n type: 'posts',\n chunkIndex: 0,\n relevanceScore: 0.95,\n content: 'Mock content for the source.'\n }\n ])\n\n // Mock usage\n callbacks.onUsage?.({\n daily_limit: 1000,\n daily_used: 150,\n daily_remaining: 850,\n reset_at: new Date(Date.now() + 86400000).toISOString()\n })\n\n callbacks.onDone?.()\n }\n\n async getActiveSession(): Promise<{\n conversationId: string\n messages: Message[]\n } | null> {\n // Return last session or null\n if (this.sessions.size > 0) {\n const last = Array.from(this.sessions.values()).pop()\n return last ? { conversationId: last.id, messages: last.messages } : null\n }\n return null\n }\n\n async getHistory(): Promise<SessionSummary[]> {\n return Array.from(this.sessions.values()).map(s => ({\n conversation_id: s.id,\n title: s.title,\n last_activity: new Date().toISOString(), // Mock\n status: 'active'\n }))\n }\n\n async loadSession(id: string): Promise<{ conversationId: string; messages: Message[] } | null> {\n const session = this.sessions.get(id)\n return session ? { conversationId: session.id, messages: session.messages } : null\n }\n\n async renameSession(id: string, newTitle: string): Promise<boolean> {\n const session = this.sessions.get(id)\n if (session) {\n session.title = newTitle\n return true\n }\n return false\n }\n\n async deleteSession(id: string): Promise<boolean> {\n return this.sessions.delete(id)\n }\n\n async getAgents(): Promise<PublicAgentInfo[]> {\n return [\n { slug: 'default-mock-agent', name: 'Mock Agent' },\n { slug: 'advanced-mock-agent', name: 'Advanced Mock Agent' }\n ]\n }\n}\n","import type {\n ChatAdapter,\n Message,\n PublicAgentInfo,\n SendMessageContext,\n SessionSummary,\n StreamCallbacks\n} from './ChatAdapter'\n\ninterface SSEEvent {\n type: string\n data?: unknown\n}\n\nfunction handleSSEEvent(event: SSEEvent, callbacks: StreamCallbacks): void {\n switch (event.type) {\n case 'conversation_id':\n callbacks.onConversationId?.(event.data as string)\n break\n\n case 'token':\n callbacks.onToken?.(event.data as string)\n break\n\n case 'sources':\n if (Array.isArray(event.data)) {\n callbacks.onSources?.(event.data)\n }\n break\n\n case 'done':\n callbacks.onDone?.()\n break\n\n case 'usage':\n callbacks.onUsage?.(\n event.data as { daily_limit: number; daily_used: number; daily_remaining: number; reset_at: string }\n )\n break\n\n case 'error':\n handleSSEError(event)\n break\n }\n}\n\nfunction handleSSEError(event: SSEEvent): never {\n const errorData = event.data as Record<string, unknown> | undefined\n if (errorData?.error === 'EXPIRED_CONVERSATION') {\n const error = new Error((errorData?.message as string) || 'Esta conversación ha expirado') as Error & {\n code: string\n chatId: string\n }\n error.code = 'EXPIRED_CONVERSATION'\n error.chatId = errorData?.chatId as string\n throw error\n }\n throw new Error((errorData?.error as string) || 'Streaming error')\n}\n\nfunction parseSSELine(line: string): { done: boolean; event: SSEEvent | null } {\n if (!line.startsWith('data: ')) {\n return { done: false, event: null }\n }\n\n const data = line.slice(6)\n if (data === '[DONE]') {\n return { done: true, event: null }\n }\n\n try {\n const event = JSON.parse(data) as SSEEvent\n return { done: false, event }\n } catch (e) {\n if (!(e instanceof SyntaxError)) throw e\n console.warn('Failed to parse SSE event:', data)\n return { done: false, event: null }\n }\n}\n\nexport class NexoPayloadChatAdapter implements ChatAdapter {\n async sendMessage(\n message: string,\n context: SendMessageContext,\n callbacks: StreamCallbacks,\n signal?: AbortSignal\n ): Promise<void> {\n const requestBody: Record<string, unknown> = {\n message: message,\n agentSlug: context.agentSlug || undefined\n }\n\n if (context.selectedDocuments.length > 0) {\n requestBody.selectedDocuments = context.selectedDocuments\n }\n\n if (context.conversationId) {\n requestBody.chatId = context.conversationId\n }\n\n try {\n const response = await fetch('/api/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody),\n signal\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Error al procesar' }))\n\n // Handle 429\n if (response.status === 429 && errorData.limit_info && callbacks.onUsage) {\n callbacks.onUsage({\n daily_limit: errorData.limit_info.limit,\n daily_used: errorData.limit_info.used,\n daily_remaining: errorData.limit_info.remaining,\n reset_at: errorData.limit_info.reset_at\n })\n throw new Error(errorData.error || 'Has alcanzado tu límite diario de tokens.')\n }\n\n throw new Error(errorData.error || 'Error al procesar')\n }\n\n await this.processStream(response, callbacks)\n } catch (err) {\n if (callbacks.onError) {\n callbacks.onError(err instanceof Error ? err : new Error('Unknown error'))\n } else {\n throw err\n }\n }\n }\n\n private async processStream(response: Response, callbacks: StreamCallbacks) {\n const reader = response.body?.getReader()\n const decoder = new TextDecoder()\n\n if (!reader) throw new Error('No stream reader')\n\n let buffer = ''\n let streamDone = false\n\n while (!streamDone) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n const result = parseSSELine(line)\n if (result.done) {\n streamDone = true\n break\n }\n if (result.event) {\n handleSSEEvent(result.event, callbacks)\n }\n }\n }\n }\n\n async getActiveSession(): Promise<{\n conversationId: string\n messages: Message[]\n } | null> {\n try {\n const response = await fetch('/api/chat/session?active=true')\n if (response.ok) {\n const sessionData = await response.json()\n // Don't load if session is closed/expired\n if (sessionData.status === 'closed') {\n console.warn('[NexoPayloadChatAdapter] Active session is closed/expired, clearing')\n return null\n }\n return {\n conversationId: sessionData.conversation_id,\n messages: this.parseBackendMessages(sessionData.messages)\n }\n }\n return null\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error loading active session:', error)\n return null\n }\n }\n\n async getHistory(): Promise<SessionSummary[]> {\n try {\n const response = await fetch('/api/chat/sessions')\n if (response.ok) {\n const data = await response.json()\n const sessions = data.sessions || []\n // Filter out closed/expired sessions\n return sessions.filter((session: SessionSummary) => session.status === 'active')\n }\n return []\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error loading history:', error)\n return []\n }\n }\n\n async loadSession(id: string): Promise<{ conversationId: string; messages: Message[] } | null> {\n try {\n const response = await fetch(`/api/chat/session?conversationId=${encodeURIComponent(id)}`)\n if (response.ok) {\n const sessionData = await response.json()\n // Don't load if session is closed/expired\n if (sessionData.status === 'closed') {\n console.warn('[NexoPayloadChatAdapter] Session is closed/expired:', id)\n return null\n }\n return {\n conversationId: sessionData.conversation_id,\n messages: this.parseBackendMessages(sessionData.messages)\n }\n }\n return null\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error loading session:', error)\n return null\n }\n }\n\n async renameSession(id: string, newTitle: string): Promise<boolean> {\n try {\n const response = await fetch(`/api/chat/session?conversationId=${encodeURIComponent(id)}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ title: newTitle })\n })\n return response.ok\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error renaming session:', error)\n return false\n }\n }\n\n async deleteSession(id: string): Promise<boolean> {\n try {\n const response = await fetch(`/api/chat/session?conversationId=${encodeURIComponent(id)}`, {\n method: 'DELETE'\n })\n return response.ok\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error deleting session:', error)\n return false\n }\n }\n\n async getAgents(): Promise<PublicAgentInfo[]> {\n try {\n const response = await fetch('/api/chat/agents')\n if (response.ok) {\n const data = await response.json()\n return data.agents || []\n }\n return []\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error loading agents:', error)\n return []\n }\n }\n\n private parseBackendMessages(backendMessages: Record<string, unknown>[]): Message[] {\n if (!backendMessages) return []\n return backendMessages.map((msg: Record<string, unknown>) => ({\n role: msg.role as Message['role'],\n content: msg.content as string,\n timestamp: new Date(msg.timestamp as string),\n sources: (msg.sources as Record<string, unknown>[])?.map((s: Record<string, unknown>) => ({\n id: s.id as string,\n title: s.title as string,\n slug: s.slug as string,\n type: (s.type as string) || 'document',\n chunkIndex: (s.chunk_index as number) || 0,\n relevanceScore: 0,\n content: ''\n }))\n }))\n }\n}\n","'use client'\n\nimport type { FC } from 'react'\nimport ReactMarkdown from 'react-markdown'\n\nexport interface MarkdownTextProps {\n text: string\n}\n\nexport const MarkdownText: FC<MarkdownTextProps> = ({ text }) => {\n return (\n <div className=\"prose prose-sm max-w-none dark:prose-invert text-current leading-relaxed\">\n <ReactMarkdown>{text}</ReactMarkdown>\n </div>\n )\n}\n","import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { useCallback, useEffect, useState } from 'react'\nimport type { ChatAdapter, Message, SessionSummary } from '../adapters/ChatAdapter'\n\nexport type { Message, SessionSummary }\n\ninterface UseChatSessionReturn {\n conversationId: string | null\n setConversationId: (id: string | null) => void\n messages: Message[]\n setMessages: React.Dispatch<React.SetStateAction<Message[]>>\n isLoadingSession: boolean\n handleNewConversation: () => Promise<void>\n // History management\n sessionsHistory: SessionSummary[]\n isLoadingHistory: boolean\n loadHistory: () => Promise<void>\n loadSession: (conversationId: string) => Promise<void>\n renameSession: (conversationId: string, newTitle: string) => Promise<boolean>\n deleteSession: (conversationId: string) => Promise<boolean>\n}\n\n/**\n * Hook to manage chat session state and persistence\n */\nexport function useChatSession(adapter: ChatAdapter): UseChatSessionReturn {\n const [conversationId, setConversationId] = useState<string | null>(null)\n const [messages, setMessages] = useState<Message[]>([])\n const [isLoadingSession, setIsLoadingSession] = useState(true)\n\n // History state\n const [sessionsHistory, setSessionsHistory] = useState<SessionSummary[]>([])\n const [isLoadingHistory, setIsLoadingHistory] = useState(false)\n\n // Load active session from backend on mount\n useEffect(() => {\n const loadActiveSession = async () => {\n try {\n console.log('[useChatSession] 🔄 Loading active session through adapter...')\n\n const sessionData = await adapter.getActiveSession()\n\n if (sessionData) {\n console.log('[useChatSession] ✅ Active session found:', sessionData.conversationId)\n // Restore conversation state\n setConversationId(sessionData.conversationId)\n // Restore messages\n if (sessionData.messages && sessionData.messages.length > 0) {\n setMessages(sessionData.messages)\n console.log('[useChatSession] ✅ Session restored with', sessionData.messages.length, 'messages')\n }\n } else {\n // No active session found\n console.log('[useChatSession] ℹ️ No active session found (adapter returned null)')\n }\n } catch (error) {\n console.error('[useChatSession] ❌ Error loading session:', error)\n } finally {\n setIsLoadingSession(false)\n }\n }\n\n loadActiveSession()\n }, [adapter])\n\n // Load history\n const loadHistory = useCallback(async () => {\n try {\n setIsLoadingHistory(true)\n const sessions = await adapter.getHistory()\n setSessionsHistory(sessions)\n } catch (error) {\n console.error('[useChatSession] ❌ Error loading history:', error)\n } finally {\n setIsLoadingHistory(false)\n }\n }, [adapter])\n\n // Load a specific session\n const loadSession = useCallback(\n async (id: string) => {\n try {\n setIsLoadingSession(true)\n console.log('[useChatSession] 🔄 Loading session:', id)\n\n const sessionData = await adapter.loadSession(id)\n\n if (sessionData) {\n setConversationId(sessionData.conversationId)\n setMessages(sessionData.messages)\n } else {\n console.error('[useChatSession] ❌ Failed to load session (adapter returned null)')\n }\n } catch (error) {\n console.error('[useChatSession] ❌ Error loading session:', error)\n } finally {\n setIsLoadingSession(false)\n }\n },\n [adapter]\n )\n\n // Rename session\n const renameSession = useCallback(\n async (id: string, newTitle: string) => {\n try {\n const success = await adapter.renameSession(id, newTitle)\n\n if (success) {\n // Update local history\n setSessionsHistory(prev => prev.map(s => (s.conversation_id === id ? { ...s, title: newTitle } : s)))\n return true\n }\n return false\n } catch (error) {\n console.error('[useChatSession] ❌ Error renaming session:', error)\n return false\n }\n },\n [adapter]\n )\n\n // Delete session\n const deleteSession = useCallback(\n async (id: string) => {\n try {\n const success = await adapter.deleteSession(id)\n\n if (success) {\n // Update local history\n setSessionsHistory(prev => prev.filter(s => s.conversation_id !== id))\n // If current session was deleted, clear it\n if (id === conversationId) {\n setConversationId(null)\n setMessages([])\n }\n return true\n }\n return false\n } catch (error) {\n console.error('[useChatSession] ❌ Error deleting session:', error)\n return false\n }\n },\n [conversationId, adapter]\n )\n\n // Clear conversation and start new one\n const handleNewConversation = useCallback(async () => {\n // Just clear local state, don't close the session on backend (backend keeps history)\n setMessages([])\n setConversationId(null)\n console.log('[useChatSession] 🆕 Started new conversation')\n }, [])\n\n return {\n conversationId,\n setConversationId,\n messages,\n setMessages,\n isLoadingSession,\n handleNewConversation,\n sessionsHistory,\n isLoadingHistory,\n loadHistory,\n loadSession,\n renameSession,\n deleteSession\n }\n}\n","'use client'\n\nimport { createContext, type ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react'\nimport type { ChatAdapter, Message, PublicAgentInfo, SessionSummary, TokenUsage } from '../adapters/ChatAdapter'\nimport { NexoPayloadChatAdapter } from '../adapters/NexoPayloadChatAdapter'\nimport { useChatSession } from '../hooks/useChatSession'\n\n/**\n * Resolves collection type metadata for UI rendering.\n * All fields are optional — sensible defaults are applied.\n */\nexport interface CollectionTypeResolver {\n /** Human-readable label (e.g. 'posts' → 'Artículo') */\n label?: (type: string) => string\n /** Icon component (e.g. 'books' → <BookOpen />) */\n icon?: (type: string) => React.ReactNode\n /** URL route segment for link generation (e.g. 'posts' → 'articulos') */\n contentType?: (type: string) => string\n /** Typesense chunk collection name (e.g. 'posts' → 'posts_chunk') */\n chunkCollection?: (type: string) => string\n}\n\nconst defaultCollectionResolver: Required<CollectionTypeResolver> = {\n label: (type: string) => type.charAt(0).toUpperCase() + type.slice(1),\n icon: () => null,\n contentType: (type: string) => type,\n chunkCollection: (type: string) => `${type}_chunk`\n}\n\ninterface ChatContextType {\n adapter: ChatAdapter\n /** Typesense collection names available for document search */\n searchCollections: string[]\n /** Resolved collection type config (with defaults applied) */\n collectionResolver: Required<CollectionTypeResolver>\n isPanelOpen: boolean\n isMaximized: boolean\n openPanel: () => void\n closePanel: () => void\n setMaximized: (value: boolean) => void\n tokenUsage: TokenUsage | null\n isLoadingTokens: boolean\n updateTokenUsage: (newUsage: Partial<TokenUsage>) => void\n // Limit error (when 429 is received)\n limitError: string | null\n setLimitError: (error: string | null) => void\n // Agent management\n agents: PublicAgentInfo[]\n selectedAgent: string | null\n setSelectedAgent: (slug: string) => void\n isLoadingAgents: boolean\n // Session & History (from useChatSession)\n conversationId: string | null\n setConversationId: (id: string | null) => void\n messages: Message[]\n setMessages: React.Dispatch<React.SetStateAction<Message[]>>\n isLoadingSession: boolean\n handleNewConversation: () => Promise<void>\n sessionsHistory: SessionSummary[]\n isLoadingHistory: boolean\n loadHistory: () => Promise<void>\n loadSession: (conversationId: string) => Promise<void>\n renameSession: (conversationId: string, newTitle: string) => Promise<boolean>\n deleteSession: (conversationId: string) => Promise<boolean>\n}\n\nconst ChatContext = createContext<ChatContextType | undefined>(undefined)\n\ninterface ChatProviderProps {\n children: ReactNode\n adapter?: ChatAdapter\n /** Typesense collection names available for document search */\n searchCollections?: string[]\n /** Collection type resolver for UI labels, icons, and URL mapping */\n collectionResolver?: CollectionTypeResolver\n}\n\nexport const ChatProvider = ({\n children,\n adapter: customAdapter,\n searchCollections = [],\n collectionResolver: customResolver\n}: ChatProviderProps) => {\n const [isPanelOpen, setIsPanelOpen] = useState(false)\n const [isMaximized, setIsMaximized] = useState(false)\n\n // Initialize adapter (memoize default to avoid re-creation)\n const adapter = useMemo(() => customAdapter || new NexoPayloadChatAdapter(), [customAdapter])\n\n // Merge custom resolver with defaults\n const collectionResolver = useMemo<Required<CollectionTypeResolver>>(\n () => ({ ...defaultCollectionResolver, ...customResolver }),\n [customResolver]\n )\n\n // Use session hook with adapter\n const chatSession = useChatSession(adapter)\n\n // Token usage management - lazy loaded from SSE events\n const [tokenUsage, setTokenUsage] = useState<TokenUsage | null>(null)\n const [limitError, setLimitError] = useState<string | null>(null)\n const isLoadingTokens = false // No initial fetch needed\n\n // Agent management\n const [agents, setAgents] = useState<PublicAgentInfo[]>([])\n const [selectedAgent, setSelectedAgent] = useState<string | null>(null)\n const [isLoadingAgents, setIsLoadingAgents] = useState(false)\n\n // Load agents on mount\n useEffect(() => {\n const loadAgents = async () => {\n try {\n setIsLoadingAgents(true)\n const loadedAgents = await adapter.getAgents()\n setAgents(loadedAgents)\n if (loadedAgents.length > 0 && !selectedAgent) {\n setSelectedAgent(loadedAgents[0]?.slug || null)\n }\n } catch (error) {\n console.error('[ChatContext] Error loading agents:', error)\n } finally {\n setIsLoadingAgents(false)\n }\n }\n\n loadAgents()\n }, [adapter, selectedAgent])\n\n // Check if device is mobile or tablet (not desktop)\n const isMobileOrTablet = () => {\n if (typeof window === 'undefined') return false\n return window.innerWidth < 1024 // Tailwind lg breakpoint\n }\n\n const openPanel = () => {\n setIsPanelOpen(true)\n // Auto-maximize on mobile and tablet\n if (isMobileOrTablet()) {\n setIsMaximized(true)\n }\n }\n\n const closePanel = () => {\n setIsPanelOpen(false)\n setIsMaximized(false)\n }\n\n const setMaximized = (value: boolean) => setIsMaximized(value)\n\n // Update token usage (called from SSE events)\n // Memoized to prevent infinite loops in useEffect dependencies\n const updateTokenUsage = useCallback((newUsage: Partial<TokenUsage>) => {\n setTokenUsage(prev => {\n if (!prev) {\n // First time: create full object from partial\n return newUsage as TokenUsage\n }\n // Subsequent updates: merge\n return {\n ...prev,\n ...newUsage\n }\n })\n }, [])\n\n // Block body scroll when chat is maximized and open\n useEffect(() => {\n // Restore scroll when not maximized OR not open\n if (!isMaximized || !isPanelOpen) {\n document.body.style.overflow = ''\n document.body.style.position = ''\n document.body.style.top = ''\n document.body.style.width = ''\n return\n }\n\n // Save current scroll position\n const scrollY = window.scrollY\n\n // Prevent body scroll\n document.body.style.overflow = 'hidden'\n document.body.style.position = 'fixed'\n document.body.style.top = `-${scrollY}px`\n document.body.style.width = '100%'\n\n return () => {\n // Restore body scroll\n document.body.style.overflow = ''\n document.body.style.position = ''\n document.body.style.top = ''\n document.body.style.width = ''\n\n // Restore scroll position\n window.scrollTo(0, scrollY)\n }\n }, [isMaximized, isPanelOpen])\n\n return (\n <ChatContext.Provider\n value={{\n adapter,\n searchCollections,\n collectionResolver,\n isPanelOpen,\n isMaximized,\n openPanel,\n closePanel,\n setMaximized,\n tokenUsage,\n isLoadingTokens,\n updateTokenUsage,\n limitError,\n setLimitError,\n agents,\n selectedAgent,\n setSelectedAgent,\n isLoadingAgents,\n ...chatSession // Spread useChatSession return values\n }}\n >\n {children}\n </ChatContext.Provider>\n )\n}\n\nexport const useChat = () => {\n const context = useContext(ChatContext)\n if (!context) {\n throw new Error('useChat must be used within a ChatProvider')\n }\n return context\n}\n","import { useCallback, useState } from 'react'\nimport { useChat } from '../components/chat-context'\n\ninterface ChunkData {\n chunk_text: string\n title: string\n slug: string\n chunk_index: number\n collection: string\n}\n\ninterface ChunkCache {\n [key: string]: {\n content: string\n isLoading: boolean\n error: string | null\n }\n}\n\n/**\n * Hook to lazy-load chunk content from the API\n */\nexport function useChunkLoader() {\n const [chunkCache, setChunkCache] = useState<ChunkCache>({})\n const { collectionResolver } = useChat()\n\n /**\n * Load chunk content from the API\n */\n const loadChunkContent = useCallback(\n async (chunkId: string, type: string): Promise<string> => {\n const cacheKey = `${type}_${chunkId}`\n\n // Return cached content if available\n if (chunkCache[cacheKey]?.content) {\n return chunkCache[cacheKey].content\n }\n\n // Return empty if already loading\n if (chunkCache[cacheKey]?.isLoading) {\n return ''\n }\n\n // Mark as loading\n setChunkCache(prev => ({\n ...prev,\n [cacheKey]: {\n content: '',\n isLoading: true,\n error: null\n }\n }))\n\n try {\n const collectionName = collectionResolver.chunkCollection(type)\n const url = `/api/chat/chunks/${encodeURIComponent(chunkId)}?collection=${encodeURIComponent(collectionName)}`\n\n const response = await fetch(url)\n\n if (!response.ok) {\n const errorData = await response.json()\n throw new Error(errorData.error || 'Error al cargar el chunk')\n }\n\n const data: ChunkData = await response.json()\n\n // Update cache with loaded content\n setChunkCache(prev => ({\n ...prev,\n [cacheKey]: {\n content: data.chunk_text,\n isLoading: false,\n error: null\n }\n }))\n\n return data.chunk_text\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Error desconocido al cargar el chunk'\n\n // Update cache with error\n setChunkCache(prev => ({\n ...prev,\n [cacheKey]: {\n content: '',\n isLoading: false,\n error: errorMessage\n }\n }))\n\n console.error('[useChunkLoader] Error loading chunk:', error)\n return ''\n }\n },\n [chunkCache, collectionResolver]\n )\n\n /**\n * Get the current state of a chunk\n */\n const getChunkState = useCallback(\n (chunkId: string, type: string) => {\n const cacheKey = `${type}_${chunkId}`\n return (\n chunkCache[cacheKey] || {\n content: '',\n isLoading: false,\n error: null\n }\n )\n },\n [chunkCache]\n )\n\n return {\n loadChunkContent,\n getChunkState\n }\n}\n","import type React from 'react'\n\n/**\n * Props for a Link component that can be injected\n * Compatible with next/link and regular <a> tags\n */\nexport interface LinkComponentProps {\n href: string\n children: React.ReactNode\n onClick?: () => void\n className?: string\n target?: string\n 'aria-label'?: string\n title?: string\n}\n\n/**\n * Type for a Link component that can be injected from outside\n * Default fallback is a regular <a> tag\n */\nexport type LinkComponent = React.ComponentType<LinkComponentProps>\n\n/**\n * Props for an Image component that can be injected\n * Compatible with next/image and regular <img> tags\n */\nexport interface ImageComponentProps {\n src: string\n alt: string\n width?: number\n height?: number\n className?: string\n}\n\n/**\n * Type for an Image component that can be injected from outside\n * Default fallback is a regular <img> tag\n */\nexport type ImageComponent = React.ComponentType<ImageComponentProps>\n\n/**\n * Default Link component fallback (regular <a> tag)\n */\nexport const DefaultLink: LinkComponent = ({\n href,\n children,\n onClick,\n className,\n target,\n 'aria-label': ariaLabel,\n title\n}) => (\n <a href={href} onClick={onClick} className={className} target={target} aria-label={ariaLabel} title={title}>\n {children}\n </a>\n)\n\n/**\n * Default Image component fallback (regular <img> tag)\n */\nexport const DefaultImage: ImageComponent = ({ src, alt, width, height, className }) => (\n <img src={src} alt={alt} width={width} height={height} className={className} />\n)\n","'use client'\n\nimport { ArrowRight } from 'lucide-react'\nimport { DefaultLink, type LinkComponent } from '../../types/components'\n\ninterface ViewMoreLinkProps {\n contentType: string\n slug: string\n title: string\n onClick?: () => void\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n}\n\nexport const ViewMoreLink = ({\n contentType,\n slug,\n title,\n onClick,\n generateHref,\n LinkComponent: Link = DefaultLink\n}: ViewMoreLinkProps) => {\n const href = generateHref({\n type: contentType,\n value: { id: parseInt(slug.split('-')?.[0] || '0', 10), slug }\n })\n if (!href) return null\n\n return (\n <Link\n href={href}\n onClick={onClick}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:text-primary/80 transition-colors mt-2\"\n title={`Ver más sobre ${title}`}\n >\n Ver documento completo\n <ArrowRight className=\"w-4 h-4\" />\n </Link>\n )\n}\n","'use client'\n\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { ChevronDown, FileText, List, Loader2, X } from 'lucide-react'\nimport React, { useState } from 'react'\nimport { useChunkLoader } from '../hooks/useChunkLoader'\nimport { cn } from '../lib/utils'\nimport type { LinkComponent } from '../types/components'\nimport { MarkdownText } from './assistant-ui/markdown-text'\nimport { ViewMoreLink } from './buttons/ViewMoreLink'\nimport { useChat } from './chat-context'\n\ninterface Source {\n id: string\n title: string\n slug: string\n type: string\n chunkIndex: number\n relevanceScore: number\n content: string\n excerpt?: string\n}\n\ninterface SourcesListProps {\n sources: Source[]\n isMaximized?: boolean\n onMinimize?: () => void\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n renderSourceIcon?: (type: string) => React.ReactNode\n renderViewMore?: (props: { type: string; slug: string; title: string; onClick?: () => void }) => React.ReactNode\n}\n\n// Animation variants\nconst listVariants = {\n hidden: { opacity: 0, height: 0, transition: { duration: 0.2, ease: 'easeInOut' as const } },\n visible: { opacity: 1, height: 'auto', transition: { duration: 0.3, ease: 'easeOut' as const } }\n}\n\nconst expandedCardVariants = {\n hidden: { opacity: 0, scale: 0.95, y: -10, transition: { duration: 0.2, ease: 'easeInOut' as const } },\n visible: { opacity: 1, scale: 1, y: 0, transition: { duration: 0.3, ease: [0.4, 0, 0.2, 1] as const } },\n exit: { opacity: 0, scale: 0.95, y: -10, transition: { duration: 0.2, ease: 'easeInOut' as const } }\n}\n\nconst contentVariants = {\n hidden: { opacity: 0, y: 10 },\n visible: { opacity: 1, y: 0, transition: { delay: 0.1, duration: 0.3, ease: 'easeOut' as const } }\n}\n\nconst itemVariants = {\n hidden: { opacity: 0, x: -10 },\n visible: (i: number) => ({\n opacity: 1,\n x: 0,\n transition: {\n delay: i * 0.05,\n duration: 0.2,\n ease: 'easeOut' as const\n }\n })\n}\n\n// Helper to parse chunk content\nconst parseChunkContent = (content: string) => {\n const separator = '.________________________________________.'\n if (!content || !content.includes(separator)) {\n return { text: content, metadata: null }\n }\n\n const [text = '', metadataRaw] = content.split(separator)\n const metadata: { section?: string; path?: string } = {}\n\n if (metadataRaw) {\n const parts = metadataRaw.split('|')\n parts.forEach(part => {\n const trimmed = part.trim()\n if (trimmed.toLowerCase().startsWith('section:')) {\n metadata.section = trimmed.substring('section:'.length).trim()\n } else if (trimmed.toLowerCase().startsWith('path:')) {\n metadata.path = trimmed.substring('path:'.length).trim()\n }\n })\n }\n\n return { text: text.trim(), metadata }\n}\n\n// Relevance bar component\nconst RelevanceBar: React.FC<{ score: number }> = ({ score }) => {\n const percentage = Math.min(Math.max(score * 100, 0), 100)\n const getColor = () => {\n if (percentage >= 80) return 'bg-green-500'\n if (percentage >= 60) return 'bg-primary'\n if (percentage >= 40) return 'bg-yellow-500'\n return 'bg-muted-foreground'\n }\n\n return (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-1 w-12 rounded-full bg-secondary overflow-hidden\">\n <motion.div\n className={cn('h-full rounded-full', getColor())}\n initial={{ width: 0 }}\n animate={{ width: `${percentage}%` }}\n transition={{ duration: 0.5, ease: 'easeOut' }}\n />\n </div>\n <span className=\"text-[10px] text-muted-foreground\">{Math.round(percentage)}%</span>\n </div>\n )\n}\n\n// Fallback icon when collectionResolver.icon returns null\nconst fallbackIcon = () => <FileText className=\"w-full h-full\" />\n\n// Breadcrumb path segments component\nconst BreadcrumbPath: React.FC<{ path: string }> = ({ path }) => (\n <>\n {path.split('>').map((segment, index, arr) => {\n const text = segment.trim()\n const truncated = text.length > 25 ? `${text.slice(0, 25)}...` : text\n return (\n // biome-ignore lint/suspicious/noArrayIndexKey: breadcrumb segments can repeat\n <React.Fragment key={`${text}-${index}`}>\n <motion.span\n className=\"inline-flex items-center rounded-full bg-primary/10 border border-primary/20 px-2.5 py-0.5 text-xs font-medium text-foreground max-w-[150px]\"\n initial={{ opacity: 0, scale: 0.9 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ delay: index * 0.05 }}\n title={text}\n >\n <span className=\"truncate\">{truncated}</span>\n </motion.span>\n {index < arr.length - 1 && <span className=\"text-muted-foreground text-xs\">/</span>}\n </React.Fragment>\n )\n })}\n </>\n)\n\n// Section pill component\nconst SectionPill: React.FC<{ section: string }> = ({ section }) => (\n <span\n className=\"inline-flex items-center rounded-full bg-primary/10 border border-primary/20 px-2.5 py-0.5 text-xs font-medium text-foreground max-w-[150px]\"\n title={section}\n >\n <span className=\"truncate\">{section.length > 25 ? `${section.slice(0, 25)}...` : section}</span>\n </span>\n)\n\n// Metadata pills component\nconst MetadataPills: React.FC<{ metadata: { section?: string; path?: string } | null }> = ({ metadata }) => {\n if (!metadata || (!metadata.path && !metadata.section)) return null\n return (\n <div className=\"mt-4 flex flex-wrap items-center gap-2 mb-3\">\n <span className=\"text-xs text-muted-foreground\">Ubicacion:</span>\n {metadata.path ? (\n <BreadcrumbPath path={metadata.path} />\n ) : metadata.section ? (\n <SectionPill section={metadata.section} />\n ) : null}\n </div>\n )\n}\n\n// Expanded content body component\nconst ExpandedContentBody: React.FC<{\n isLoading: boolean\n error: string | null\n displayContent: string\n cleanContent: string\n metadata: { section?: string; path?: string } | null\n expandedSource: Source\n getContentType: (type: string) => string\n renderViewMore?: SourcesListProps['renderViewMore']\n handleViewMore: () => void\n generateHref: SourcesListProps['generateHref']\n LinkComponent?: LinkComponent\n}> = ({\n isLoading,\n error,\n displayContent,\n cleanContent,\n metadata,\n expandedSource,\n getContentType,\n renderViewMore,\n handleViewMore,\n generateHref,\n LinkComponent\n}) => {\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center gap-2 py-4 text-sm text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n Cargando contenido...\n </div>\n )\n }\n if (error) {\n return <div className=\"text-sm text-destructive py-2\">Error: {error}</div>\n }\n if (!displayContent) {\n return <div className=\"text-sm text-muted-foreground py-2\">No hay contenido disponible para este fragmento</div>\n }\n return (\n <>\n <MarkdownText text={cleanContent} />\n <MetadataPills metadata={metadata} />\n {renderViewMore ? (\n renderViewMore({\n type: expandedSource.type,\n slug: expandedSource.slug,\n title: expandedSource.title,\n onClick: handleViewMore\n })\n ) : (\n <ViewMoreLink\n contentType={getContentType(expandedSource.type)}\n slug={expandedSource.slug}\n title={expandedSource.title}\n onClick={handleViewMore}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n />\n )}\n </>\n )\n}\n\n// Expanded source card component\nconst ExpandedSourceCard: React.FC<{\n expandedSource: Source\n expandedSourceId: string\n loadedContent: string\n getChunkState: ReturnType<typeof useChunkLoader>['getChunkState']\n getIcon: (type: string) => React.ReactNode\n getLabel: (type: string) => string\n getContentType: (type: string) => string\n onClose: () => void\n handleViewMore: () => void\n renderViewMore?: SourcesListProps['renderViewMore']\n generateHref: SourcesListProps['generateHref']\n LinkComponent?: LinkComponent\n}> = ({\n expandedSource,\n expandedSourceId,\n loadedContent,\n getChunkState,\n getIcon,\n getLabel,\n getContentType,\n onClose,\n handleViewMore,\n renderViewMore,\n generateHref,\n LinkComponent\n}) => {\n const chunkState = getChunkState(expandedSource.id, expandedSource.type)\n const displayContent = loadedContent || expandedSource.content\n const { text: cleanContent, metadata } = parseChunkContent(displayContent)\n\n return (\n <div className=\"mt-3 pt-3 border-t border-border\">\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={expandedSourceId}\n variants={expandedCardVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n layoutId={`source-${expandedSourceId}`}\n >\n <div className=\"p-4 bg-muted rounded-lg border border-border shadow-sm\">\n <div className=\"flex items-start justify-between mb-3\">\n <div className=\"flex items-start gap-2 flex-1\">\n <motion.div\n className=\"flex-shrink-0 w-5 h-5 text-foreground mt-0.5\"\n initial={{ rotate: -10 }}\n animate={{ rotate: 0 }}\n transition={{ type: 'spring', stiffness: 300 }}\n >\n {getIcon(expandedSource.type)}\n </motion.div>\n <div>\n <div className=\"text-foreground font-semibold text-sm\">{expandedSource.title}</div>\n <div className=\"text-muted-foreground text-xs mt-1 flex items-center gap-2\">\n <span>\n {getLabel(expandedSource.type)}\n {expandedSource.chunkIndex !== undefined && <> - Parte {expandedSource.chunkIndex + 1}</>}\n </span>\n {expandedSource.relevanceScore && <RelevanceBar score={expandedSource.relevanceScore} />}\n </div>\n </div>\n </div>\n <motion.button\n onClick={onClose}\n className=\"flex-shrink-0 ml-2 h-8 w-8 inline-flex items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors\"\n aria-label=\"Cerrar\"\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n <X className=\"w-4 h-4\" />\n </motion.button>\n </div>\n\n <motion.div variants={contentVariants} initial=\"hidden\" animate=\"visible\">\n <ExpandedContentBody\n isLoading={chunkState.isLoading}\n error={chunkState.error}\n displayContent={displayContent}\n cleanContent={cleanContent}\n metadata={metadata}\n expandedSource={expandedSource}\n getContentType={getContentType}\n renderViewMore={renderViewMore}\n handleViewMore={handleViewMore}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n />\n </motion.div>\n </div>\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\n// Source list item component\nconst SourceListItem: React.FC<{\n source: Source\n idx: number\n getIcon: (type: string) => React.ReactNode\n getLabel: (type: string) => string\n onClick: (id: string) => void\n}> = ({ source, idx, getIcon, getLabel, onClick }) => (\n <motion.button\n key={source.id || idx}\n custom={idx}\n variants={itemVariants}\n initial=\"hidden\"\n animate=\"visible\"\n onClick={() => onClick(source.id)}\n className=\"w-full text-left text-xs rounded-lg p-3 transition-all border border-transparent hover:border-primary/20 hover:bg-muted/50 group\"\n whileHover={{ x: 4 }}\n layoutId={`source-${source.id}`}\n >\n <div className=\"flex items-start gap-2\">\n <motion.div className=\"flex-shrink-0 w-4 h-4 text-foreground mt-0.5\" whileHover={{ scale: 1.1, rotate: 5 }}>\n {getIcon(source.type)}\n </motion.div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-foreground font-medium truncate group-hover:text-primary transition-colors\">\n {source.title}\n </div>\n\n <div className=\"text-muted-foreground mt-0.5 flex items-center gap-2 flex-wrap\">\n <span className=\"text-xs\">{getLabel(source.type)}</span>\n {source.chunkIndex !== undefined && (\n <>\n <span>-</span>\n <span className=\"text-xs\">Parte {source.chunkIndex + 1}</span>\n </>\n )}\n {source.relevanceScore && <RelevanceBar score={source.relevanceScore} />}\n </div>\n\n {source.excerpt && (\n <div className=\"text-muted-foreground mt-1 text-xs line-clamp-2 italic\">\n <MarkdownText text={`\"${source.excerpt}\"`} />\n </div>\n )}\n </div>\n\n <motion.span\n className=\"text-primary flex-shrink-0 text-xs opacity-0 group-hover:opacity-100 transition-opacity\"\n initial={{ x: -5 }}\n whileHover={{ x: 0 }}\n >\n Ver mas\n </motion.span>\n </div>\n </motion.button>\n)\n\nexport const SourcesList: React.FC<SourcesListProps> = ({\n sources,\n isMaximized = false,\n onMinimize,\n generateHref,\n LinkComponent,\n renderSourceIcon,\n renderViewMore\n}) => {\n const [isExpanded, setIsExpanded] = useState(false)\n const [expandedSourceId, setExpandedSourceId] = useState<string | null>(null)\n const [loadedContent, setLoadedContent] = useState<string>('')\n\n const { loadChunkContent, getChunkState } = useChunkLoader()\n const { collectionResolver } = useChat()\n\n const handleViewMore = () => {\n if (isMaximized && onMinimize) {\n onMinimize()\n }\n }\n\n if (!sources || sources.length === 0) {\n return null\n }\n\n const getIcon = (type: string) => {\n if (renderSourceIcon) return renderSourceIcon(type)\n return collectionResolver.icon(type) || fallbackIcon()\n }\n\n const getLabel = collectionResolver.label\n const getContentType = collectionResolver.contentType\n\n const handleSourceClick = async (sourceId: string) => {\n setExpandedSourceId(sourceId)\n setLoadedContent('')\n\n const source = sources.find(s => s.id === sourceId)\n if (!source) return\n\n if (source.content) {\n setLoadedContent(source.content)\n return\n }\n\n const content = await loadChunkContent(sourceId, source.type)\n setLoadedContent(content)\n }\n\n const handleCloseExpanded = () => {\n setExpandedSourceId(null)\n setLoadedContent('')\n }\n\n // If a source is expanded, show only that one\n if (expandedSourceId) {\n const expandedSource = sources.find(s => s.id === expandedSourceId)\n if (!expandedSource) return null\n\n return (\n <ExpandedSourceCard\n expandedSource={expandedSource}\n expandedSourceId={expandedSourceId}\n loadedContent={loadedContent}\n getChunkState={getChunkState}\n getIcon={getIcon}\n getLabel={getLabel}\n getContentType={getContentType}\n onClose={handleCloseExpanded}\n handleViewMore={handleViewMore}\n renderViewMore={renderViewMore}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n />\n )\n }\n\n // Show collapsed list\n return (\n <div className=\"mt-3 pt-3 border-t border-border\">\n {/* Header - clickable to expand/collapse */}\n <motion.button\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"w-full text-left flex items-center justify-between hover:opacity-75 transition-opacity group\"\n whileTap={{ scale: 0.98 }}\n >\n <div className=\"flex items-center gap-2\">\n <List className=\"w-4 h-4 text-foreground\" />\n <p className=\"text-xs font-semibold text-foreground\">Fuentes consultadas</p>\n <motion.span\n className=\"inline-flex items-center justify-center h-5 w-5 rounded-full bg-primary/10 text-primary text-xs font-medium\"\n initial={{ scale: 1 }}\n animate={{ scale: [1, 1.1, 1] }}\n transition={{ duration: 0.3 }}\n key={sources.length}\n >\n {sources.length}\n </motion.span>\n </div>\n <motion.div animate={{ rotate: isExpanded ? 0 : -90 }} transition={{ duration: 0.2 }}>\n <ChevronDown className=\"w-4 h-4 text-muted-foreground\" />\n </motion.div>\n </motion.button>\n\n {/* Sources list - shown when expanded */}\n <AnimatePresence>\n {isExpanded && (\n <motion.div\n variants={listVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"hidden\"\n className=\"mt-2 space-y-2 overflow-hidden\"\n >\n {sources.map((source, idx) => (\n <SourceListItem\n key={source.id || idx}\n source={source}\n idx={idx}\n getIcon={getIcon}\n getLabel={getLabel}\n onClick={handleSourceClick}\n />\n ))}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\n","import {\n AssistantRuntimeProvider,\n ComposerPrimitive,\n MessagePrimitive,\n ThreadPrimitive,\n useMessageRuntime,\n useThreadRuntime\n} from '@assistant-ui/react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { AlertTriangle, ArrowRight, ArrowUpIcon, Sparkles, SquareIcon, X } from 'lucide-react'\nimport type { FC } from 'react'\nimport { createContext, useContext, useEffect, useState } from 'react'\nimport type { Source } from '../../adapters/ChatAdapter'\nimport { cn } from '../../lib/utils'\nimport type { LinkComponent } from '../../types/components'\nimport { useChat } from '../chat-context'\nimport { SourcesList } from '../SourcesList'\nimport { MarkdownText } from './markdown-text'\n\ninterface ThreadContextValue {\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n agentName?: string\n}\n\nconst ThreadContext = createContext<ThreadContextValue | null>(null)\n\ninterface ThreadProps {\n runtime: ReturnType<typeof import('@assistant-ui/react').useExternalStoreRuntime>\n welcomeTitle?: string\n welcomeSubtitle?: string\n suggestedQuestions?: Array<{\n prompt: string\n title: string\n description: string\n }>\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n agentName?: string\n}\n\n/**\n * Main Thread component styled for Oraculo de Escohotado\n */\nexport const Thread: FC<ThreadProps> = ({\n runtime,\n welcomeTitle,\n welcomeSubtitle,\n suggestedQuestions,\n generateHref,\n LinkComponent,\n agentName\n}) => {\n return (\n <AssistantRuntimeProvider runtime={runtime}>\n <ThreadContext.Provider value={{ generateHref, LinkComponent, agentName }}>\n <ThreadPrimitive.Root className=\"flex h-full flex-col bg-background\">\n {/* Limit Error Alert */}\n <LimitAlert />\n\n <ThreadPrimitive.Viewport className=\"flex-1 overflow-y-auto px-4 pt-4\">\n <ThreadPrimitive.Empty>\n {welcomeTitle && welcomeSubtitle && (\n <ThreadWelcome\n title={welcomeTitle}\n subtitle={welcomeSubtitle}\n suggestedQuestions={suggestedQuestions}\n />\n )}\n </ThreadPrimitive.Empty>\n\n <ThreadPrimitive.Messages\n components={{\n UserMessage,\n AssistantMessage\n }}\n />\n\n <TypingIndicator />\n </ThreadPrimitive.Viewport>\n\n <div className=\"sticky bottom-0 border-t border-border bg-gradient-to-t from-background via-background to-background/80 backdrop-blur-sm\">\n {/* Token Usage Bar */}\n <TokenUsageBar />\n <div className=\"p-4 pt-2\">\n <Composer />\n </div>\n </div>\n </ThreadPrimitive.Root>\n </ThreadContext.Provider>\n </AssistantRuntimeProvider>\n )\n}\n\ninterface ThreadWelcomeProps {\n title: string\n subtitle: string\n suggestedQuestions?: Array<{\n prompt: string\n title: string\n description: string\n }>\n}\n\nconst containerVariants = {\n hidden: { opacity: 0 },\n visible: {\n opacity: 1,\n transition: {\n staggerChildren: 0.1,\n delayChildren: 0.1\n }\n }\n} as const\n\nconst itemVariants = {\n hidden: { opacity: 0, y: 20 },\n visible: {\n opacity: 1,\n y: 0,\n transition: {\n type: 'spring' as const,\n stiffness: 300,\n damping: 24\n }\n }\n}\n\nconst ThreadWelcome: FC<ThreadWelcomeProps> = ({ title, subtitle, suggestedQuestions }) => {\n if (!title) return null\n\n return (\n <motion.div\n className=\"flex h-full flex-col items-center justify-center p-4 max-w-2xl mx-auto\"\n variants={containerVariants}\n initial=\"hidden\"\n animate=\"visible\"\n >\n <motion.div className=\"text-center mb-8 space-y-3\" variants={itemVariants}>\n <motion.div\n className=\"inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4\"\n animate={{\n rotate: [0, 5, -5, 0],\n scale: [1, 1.05, 1]\n }}\n transition={{\n duration: 4,\n repeat: Infinity,\n ease: 'easeInOut'\n }}\n >\n <Sparkles className=\"w-8 h-8 text-primary\" />\n </motion.div>\n <h1 className=\"text-3xl font-semibold tracking-tight text-foreground\">{title}</h1>\n <p className=\"text-lg text-muted-foreground\">{subtitle}</p>\n </motion.div>\n\n {suggestedQuestions && suggestedQuestions.length > 0 && (\n <motion.div className=\"grid grid-cols-1 md:grid-cols-2 gap-3 w-full\" variants={containerVariants}>\n {suggestedQuestions.map(question => (\n <SuggestionCard\n key={question.prompt}\n prompt={question.prompt}\n title={question.title}\n description={question.description}\n />\n ))}\n </motion.div>\n )}\n </motion.div>\n )\n}\n\ninterface SuggestionCardProps {\n prompt: string\n title: string\n description: string\n}\n\nconst SuggestionCard: FC<SuggestionCardProps> = ({ prompt, title, description }) => {\n return (\n <motion.div variants={itemVariants}>\n <ThreadPrimitive.Suggestion\n prompt={prompt}\n className=\"group relative flex flex-col items-start gap-1 rounded-xl border border-border bg-card p-4 text-start cursor-pointer overflow-hidden transition-colors duration-200 hover:border-primary/30\"\n >\n <motion.div\n className=\"absolute inset-0 bg-gradient-to-r from-primary/5 to-transparent\"\n initial={{ x: '-100%' }}\n whileHover={{ x: 0 }}\n transition={{ duration: 0.3 }}\n />\n <div className=\"relative flex items-center justify-between w-full\">\n <span className=\"font-medium text-sm text-foreground\">{title}</span>\n <motion.div initial={{ opacity: 0, x: -10 }} whileHover={{ opacity: 1, x: 0 }} className=\"text-primary\">\n <ArrowRight className=\"w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity\" />\n </motion.div>\n </div>\n <span className=\"relative text-xs text-muted-foreground\">{description}</span>\n </ThreadPrimitive.Suggestion>\n </motion.div>\n )\n}\n\n/**\n * Alert shown when token limit is exceeded\n */\nconst LimitAlert: FC = () => {\n const { limitError, setLimitError, tokenUsage } = useChat()\n\n if (!limitError) return null\n\n const resetTime = tokenUsage?.reset_at\n ? new Date(tokenUsage.reset_at).toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' })\n : null\n\n return (\n <motion.div\n className=\"mx-4 mt-4 rounded-lg border border-destructive/30 bg-destructive/10 p-4\"\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10 }}\n >\n <div className=\"flex items-start gap-3\">\n <AlertTriangle className=\"h-5 w-5 text-destructive shrink-0 mt-0.5\" />\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium text-destructive\">{limitError}</p>\n {resetTime && <p className=\"text-xs text-muted-foreground\">Tu límite se restablecerá a las {resetTime}</p>}\n </div>\n <button\n type=\"button\"\n onClick={() => setLimitError(null)}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n </motion.div>\n )\n}\n\n/**\n * Token usage progress bar\n */\nconst TokenUsageBar: FC = () => {\n const { tokenUsage } = useChat()\n\n if (!tokenUsage) return null\n\n const percentage = Math.min(tokenUsage.percentage, 100)\n const isWarning = percentage > 50\n const isCritical = percentage > 80\n\n const getGradientClass = () => {\n if (isCritical) return 'from-red-500 to-red-600'\n if (isWarning) return 'from-yellow-500 to-orange-500'\n return 'from-primary to-primary/80'\n }\n\n return (\n <div className=\"px-4 py-2 border-b border-border/50\">\n <div className=\"flex items-center justify-between text-xs text-muted-foreground mb-1\">\n <span>\n {tokenUsage.used.toLocaleString()} / {tokenUsage.limit.toLocaleString()} tokens\n </span>\n <span>{percentage.toFixed(0)}%</span>\n </div>\n <div className=\"h-1.5 w-full rounded-full bg-secondary overflow-hidden\">\n <motion.div\n className={cn('h-full rounded-full bg-gradient-to-r', getGradientClass())}\n initial={{ width: 0 }}\n animate={{ width: `${percentage}%` }}\n transition={{ duration: 0.5, ease: 'easeOut' }}\n />\n </div>\n </div>\n )\n}\n\nconst TypingIndicator: FC = () => {\n const threadRuntime = useThreadRuntime()\n const [isRunning, setIsRunning] = useState(false)\n const context = useContext(ThreadContext)\n\n useEffect(() => {\n // Subscribe to thread state changes\n const unsubscribe = threadRuntime.subscribe(() => {\n const state = threadRuntime.getState()\n setIsRunning(state.isRunning)\n })\n\n // Check initial state\n setIsRunning(threadRuntime.getState().isRunning)\n\n return unsubscribe\n }, [threadRuntime])\n\n const agentName = context?.agentName || 'El asistente'\n\n return (\n <AnimatePresence>\n {isRunning && (\n <motion.div\n className=\"flex justify-start py-4 w-full\"\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"max-w-[85%] rounded-2xl rounded-bl-md border-l-4 border-l-primary/30 bg-card/80 backdrop-blur-sm px-5 py-4 text-card-foreground shadow-sm\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-1\">\n <span className=\"typing-dot w-2 h-2 rounded-full bg-primary/60\" />\n <span className=\"typing-dot w-2 h-2 rounded-full bg-primary/60\" />\n <span className=\"typing-dot w-2 h-2 rounded-full bg-primary/60\" />\n </div>\n <span className=\"text-sm text-muted-foreground\">{agentName} está pensando...</span>\n </div>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n )\n}\n\nconst Composer: FC = () => {\n return (\n <ComposerPrimitive.Root className=\"flex w-full items-end max-w-4xl mx-auto\">\n <motion.div\n className=\"relative flex w-full flex-col rounded-3xl border border-input bg-background/50 px-4 py-2 shadow-sm transition-all focus-within:ring-2 focus-within:ring-primary/20 focus-within:border-primary/50\"\n style={\n {\n '--tw-shadow': 'var(--shadow-glow-primary)'\n } as React.CSSProperties\n }\n whileFocus={{ scale: 1.01 }}\n >\n <ComposerPrimitive.Input\n placeholder=\"Escribe tu pregunta...\"\n className=\"w-full resize-none border-none bg-transparent px-2 py-3 text-base text-foreground outline-none placeholder:text-muted-foreground focus:ring-0 max-h-[200px] min-h-[52px] pr-12\"\n rows={1}\n autoFocus\n />\n\n <div className=\"absolute bottom-3 right-3 flex items-center justify-center p-1\">\n <ComposerPrimitive.Send asChild>\n <motion.button\n type=\"submit\"\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-md shadow-primary/25 transition-all disabled:opacity-50 disabled:shadow-none\"\n aria-label=\"Enviar mensaje\"\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n <motion.div\n animate={{ y: [0, -2, 0] }}\n transition={{ duration: 1.5, repeat: Infinity, ease: 'easeInOut' }}\n >\n <ArrowUpIcon className=\"h-5 w-5\" />\n </motion.div>\n </motion.button>\n </ComposerPrimitive.Send>\n\n <ComposerPrimitive.Cancel asChild>\n <motion.button\n type=\"button\"\n className=\"hidden data-[running=true]:inline-flex h-9 w-9 items-center justify-center rounded-full bg-destructive text-destructive-foreground shadow-sm transition-all\"\n aria-label=\"Cancelar\"\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n <SquareIcon className=\"h-3 w-3 fill-current\" />\n </motion.button>\n </ComposerPrimitive.Cancel>\n </div>\n </motion.div>\n </ComposerPrimitive.Root>\n )\n}\n\nconst UserMessage: FC = () => {\n return (\n <MessagePrimitive.Root className=\"flex justify-end py-2 w-full\">\n <motion.div\n className=\"max-w-[80%] rounded-2xl rounded-br-md bg-primary px-4 py-2.5 text-primary-foreground shadow-md shadow-primary/20\"\n initial={{ opacity: 0, scale: 0.95, y: 10 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n transition={{\n type: 'spring',\n stiffness: 400,\n damping: 25\n }}\n >\n <MessagePrimitive.Content\n components={{\n Text: ({ text }) => <span className=\"whitespace-pre-wrap font-medium\">{text}</span>\n }}\n />\n </motion.div>\n </MessagePrimitive.Root>\n )\n}\n\nconst AssistantMessage: FC = () => {\n const messageRuntime = useMessageRuntime()\n const context = useContext(ThreadContext)\n\n // Extract sources from metadata with type-safe access\n const messageState = messageRuntime.getState()\n const metadata = messageState.metadata as { custom?: { sources?: Source[] } } | undefined\n const sources = metadata?.custom?.sources\n\n return (\n <MessagePrimitive.Root className=\"flex justify-start py-4 w-full\">\n <motion.div\n className=\"max-w-[85%] rounded-2xl rounded-bl-md border-l-4 border-l-primary/30 bg-card/80 backdrop-blur-sm px-5 py-4 text-card-foreground shadow-sm\"\n initial={{ opacity: 0, scale: 0.95, y: 10 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n transition={{\n type: 'spring',\n stiffness: 400,\n damping: 25\n }}\n >\n <MessagePrimitive.Content\n components={{\n Text: MarkdownText\n }}\n />\n\n {sources && sources.length > 0 && context && (\n <SourcesList sources={sources} generateHref={context.generateHref} LinkComponent={context.LinkComponent} />\n )}\n </motion.div>\n </MessagePrimitive.Root>\n )\n}\n\nexport { AssistantMessage, Composer, ThreadWelcome, TypingIndicator, UserMessage }\n","'use client'\n\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { MessageCircle } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport { DefaultImage, type ImageComponent as ImageComponentType } from '../../types/components'\n\ninterface FloatingChatButtonProps {\n onOpen: () => void\n aiIcon?: string\n isOpen?: boolean\n className?: string\n ImageComponent?: ImageComponentType\n}\n\nexport const FloatingChatButton = ({\n onOpen,\n aiIcon,\n isOpen = false,\n className,\n ImageComponent = DefaultImage\n}: FloatingChatButtonProps) => {\n return (\n <AnimatePresence>\n {!isOpen && (\n <motion.button\n onClick={onOpen}\n className={cn(\n 'fixed bottom-6 left-6 z-50 h-[60px] w-[60px] rounded-full shadow-lg focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 overflow-hidden animate-pulse-glow',\n !aiIcon && 'bg-primary text-primary-foreground flex items-center justify-center',\n className\n )}\n initial={{ scale: 0, rotate: -180, opacity: 0 }}\n animate={{\n scale: 1,\n rotate: 0,\n opacity: 1\n }}\n exit={{\n scale: 0,\n rotate: 180,\n opacity: 0,\n transition: { duration: 0.3, ease: 'easeInOut' }\n }}\n transition={{\n type: 'spring' as const,\n stiffness: 260,\n damping: 20\n }}\n whileHover={{\n scale: 1.08,\n boxShadow: 'var(--shadow-glow-lg)'\n }}\n whileTap={{ scale: 0.95 }}\n aria-label=\"Abrir chat\"\n >\n {aiIcon ? (\n <motion.div\n className=\"h-full w-full rounded-full p-[3px]\"\n style={{ background: 'var(--chat-border-color)' }}\n animate={{\n opacity: [0.85, 1, 0.85]\n }}\n transition={{\n duration: 2,\n repeat: Infinity,\n ease: 'easeInOut'\n }}\n >\n <div className=\"h-full w-full rounded-full overflow-hidden bg-background\">\n <ImageComponent\n src={aiIcon}\n alt=\"Chat Avatar\"\n className=\"h-full w-full object-cover\"\n width={60}\n height={60}\n />\n </div>\n </motion.div>\n ) : (\n <motion.div\n animate={{\n scale: [1, 1.1, 1]\n }}\n transition={{\n duration: 2,\n repeat: Infinity,\n ease: 'easeInOut'\n }}\n >\n <MessageCircle className=\"w-8 h-8\" />\n </motion.div>\n )}\n </motion.button>\n )}\n </AnimatePresence>\n )\n}\n\nexport default FloatingChatButton\n","'use client'\n\nimport { type AppendMessage, type ThreadMessage, useExternalStoreRuntime } from '@assistant-ui/react'\nimport { useCallback, useMemo, useState } from 'react'\nimport type { Message, Source } from '../adapters/ChatAdapter'\nimport { useChat } from '../components/chat-context'\nimport type { Document } from '../components/useDocumentSelector'\n\ninterface UseAssistantRuntimeProps {\n messages: Message[]\n setMessages: React.Dispatch<React.SetStateAction<Message[]>>\n conversationId: string | null\n setConversationId: (id: string | null) => void\n selectedDocuments: Document[]\n selectedAgent: string | null\n}\n\n/**\n * Convert internal messages to assistant-ui format\n * Only includes the custom metadata field with sources\n */\nfunction toThreadMessages(messages: Message[]): ThreadMessage[] {\n return messages.map((msg, index) => {\n // Only include custom metadata - other fields are optional and should be undefined\n const metadata = {\n custom: msg.sources ? { sources: msg.sources } : {}\n }\n\n if (msg.role === 'user') {\n return {\n id: `msg-${index}`,\n role: 'user' as const,\n content: [{ type: 'text' as const, text: msg.content }],\n createdAt: msg.timestamp,\n attachments: [],\n metadata\n }\n }\n\n return {\n id: `msg-${index}`,\n role: 'assistant' as const,\n content: [{ type: 'text' as const, text: msg.content }],\n createdAt: msg.timestamp,\n status: { type: 'complete' as const, reason: 'stop' as const },\n attachments: [],\n metadata\n }\n }) as ThreadMessage[]\n}\n\n/**\n * Hook that creates an assistant-ui runtime from existing chat hooks\n */\nexport function useAssistantRuntime({\n messages,\n setMessages,\n conversationId,\n setConversationId,\n selectedDocuments,\n selectedAgent\n}: UseAssistantRuntimeProps) {\n const { updateTokenUsage, setLimitError, adapter, loadHistory } = useChat()\n const [isRunning, setIsRunning] = useState(false)\n const threadMessages = useMemo(() => toThreadMessages(messages), [messages])\n\n const onNew = useCallback(\n async (message: AppendMessage) => {\n // Extract text content from the message\n const textContent = message.content\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map(part => part.text)\n .join('')\n\n if (!textContent.trim()) return\n\n // Set loading state\n setIsRunning(true)\n\n // Add user message and placeholder for assistant response in a single update\n const userMessage: Message = {\n role: 'user',\n content: textContent.trim(),\n timestamp: new Date()\n }\n\n setMessages(prev => [\n ...prev,\n userMessage,\n {\n role: 'assistant',\n content: '',\n timestamp: new Date()\n }\n ])\n\n let accumulatedContent = ''\n let receivedSources: Source[] = []\n\n try {\n // Use adapter to send message\n await adapter.sendMessage(\n textContent.trim(),\n {\n conversationId,\n selectedDocuments: selectedDocuments.map(doc => doc.id),\n agentSlug: selectedAgent\n },\n {\n onConversationId: id => setConversationId(id),\n onToken: token => {\n accumulatedContent += token\n setMessages(prev => {\n const updated = [...prev]\n const lastIdx = updated.length - 1\n if (lastIdx >= 0 && updated[lastIdx]?.role === 'assistant') {\n updated[lastIdx] = {\n ...updated[lastIdx],\n content: accumulatedContent\n }\n }\n return updated\n })\n },\n onSources: sources => {\n receivedSources = sources\n setMessages(prev => {\n const updated = [...prev]\n const lastIdx = updated.length - 1\n if (lastIdx >= 0 && updated[lastIdx]?.role === 'assistant') {\n updated[lastIdx] = { ...updated[lastIdx], sources: sources }\n }\n return updated\n })\n },\n onUsage: usage => {\n if (usage) {\n updateTokenUsage({\n limit: usage.daily_limit,\n used: usage.daily_used,\n remaining: usage.daily_remaining,\n percentage: usage.daily_limit > 0 ? (usage.daily_used / usage.daily_limit) * 100 : 0,\n reset_at: usage.reset_at\n })\n }\n },\n onDone: () => {\n // Final update to ensure consistency\n setMessages(prev => {\n const updated = [...prev]\n const lastIdx = updated.length - 1\n if (lastIdx >= 0 && updated[lastIdx]?.role === 'assistant') {\n updated[lastIdx] = {\n ...updated[lastIdx],\n content: accumulatedContent,\n sources: updated[lastIdx].sources || receivedSources\n }\n }\n return updated\n })\n },\n onError: error => {\n throw error\n }\n }\n )\n\n // Clear any previous limit error\n setLimitError(null)\n } catch (err: unknown) {\n const error = err as Error & { code?: string; chatId?: string; message: string }\n console.error('[useAssistantRuntime] Error:', error)\n\n // Handle expired conversation error\n if (error.code === 'EXPIRED_CONVERSATION') {\n console.warn('[useAssistantRuntime] Conversation expired:', error.chatId)\n\n // Replace placeholder with error message\n setMessages(prev => {\n const updated = [...prev]\n const lastIdx = updated.length - 1\n if (lastIdx >= 0 && updated[lastIdx]?.role === 'assistant') {\n updated[lastIdx] = {\n ...updated[lastIdx],\n content: `⚠️ **Conversación Expirada**\\n\\n${error.message}\\n\\nLas conversaciones expiran después de 24 horas de inactividad por motivos de seguridad y privacidad.`\n }\n }\n return updated\n })\n\n // Clear conversation ID so user can start a new conversation\n setConversationId(null)\n\n // Reload history to remove expired conversation from sidebar\n if (loadHistory) {\n loadHistory().catch(console.error)\n }\n\n return\n }\n\n // Handle 429 - Token limit exceeded (propagated from adapter)\n if (error.message === 'Has alcanzado tu límite diario de tokens.') {\n setLimitError(error.message)\n // Remove placeholder message\n setMessages(prev => prev.slice(0, -1))\n return\n }\n\n // Remove placeholder on other errors\n setMessages(prev => prev.slice(0, -1))\n } finally {\n // Clear loading state\n setIsRunning(false)\n }\n },\n [\n setMessages,\n conversationId,\n setConversationId,\n selectedDocuments,\n selectedAgent,\n setLimitError,\n updateTokenUsage,\n adapter,\n loadHistory\n ]\n )\n\n const runtime = useExternalStoreRuntime({\n messages: threadMessages,\n isRunning,\n onNew\n })\n\n return runtime\n}\n","import { debounce } from 'lodash'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nconst isDev = process.env.NODE_ENV === 'development'\n\nexport interface Document {\n id: string\n title: string\n slug: string\n type: string\n collection: string\n}\n\nfunction buildSearchParams(query: string, collections: string[]): URLSearchParams {\n const params = new URLSearchParams({\n q: query,\n exclude_fields: 'embedding,content,lexical_richtext',\n query_by: 'title',\n simple: 'true',\n mode: 'simple',\n per_page: '15'\n })\n for (const collection of collections) {\n params.append('collection', collection)\n }\n return params\n}\n\nfunction logSearchResponse(data: { documents?: Document[] }): void {\n console.log('[DocumentSelector] API response:', data)\n console.log('[DocumentSelector] Documents found:', data.documents?.length || 0)\n\n if (data.documents && data.documents.length > 0) {\n console.log(\n '[DocumentSelector] Sample document titles:',\n data.documents.slice(0, 3).map((d: Document) => d.title)\n )\n }\n}\n\nasync function fetchSearchResults(query: string, collections: string[]): Promise<Document[]> {\n const params = buildSearchParams(query, collections)\n const url = `/api/search?${params}`\n if (isDev) console.log('[DocumentSelector] Fetching URL:', url)\n\n const response = await fetch(url)\n if (isDev) console.log('[DocumentSelector] Response status:', response.status)\n\n if (!response.ok) {\n const errorText = await response.text()\n if (isDev) console.error('[DocumentSelector] API error:', response.status, errorText)\n throw new Error(`Error ${response.status}: ${errorText}`)\n }\n\n const data = await response.json()\n if (isDev) logSearchResponse(data)\n\n return data.documents || []\n}\n\n/**\n * Hook for managing document search functionality\n * @param collections - Typesense collection names to search in\n */\nexport function useDocumentSearch(collections: string[]) {\n const [searchQuery, setSearchQuery] = useState('')\n const [searchResults, setSearchResults] = useState<Document[]>([])\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n // Search function (not debounced)\n const performSearch = useCallback(\n async (query: string) => {\n if (query.trim().length < 2) {\n setSearchResults([])\n setIsLoading(false)\n return\n }\n\n if (isDev) console.log('[DocumentSelector] Searching for:', query)\n setIsLoading(true)\n setError(null)\n\n try {\n const documents = await fetchSearchResults(query, collections)\n setSearchResults(documents)\n } catch (err) {\n if (isDev) console.error('[DocumentSelector] Error searching documents:', err)\n setError(err instanceof Error ? err.message : 'Error desconocido')\n setSearchResults([])\n } finally {\n setIsLoading(false)\n }\n },\n [collections]\n )\n\n // Create debounced version using useMemo\n const debouncedSearch = useMemo(() => debounce(performSearch, 300), [performSearch])\n\n // Cleanup debounced function on unmount\n useEffect(() => {\n return () => {\n debouncedSearch.cancel()\n }\n }, [debouncedSearch])\n\n // Handle search query changes\n const handleSearchChange = useCallback(\n (query: string) => {\n setSearchQuery(query)\n if (query.trim().length >= 2) {\n setIsLoading(true)\n debouncedSearch(query)\n } else {\n setSearchResults([])\n setIsLoading(false)\n }\n },\n [debouncedSearch]\n )\n\n return {\n searchQuery,\n searchResults,\n isLoading,\n error,\n handleSearchChange\n }\n}\n\n/**\n * Hook for managing document selection\n */\nexport function useDocumentSelection(onSelectionChange?: (selectedDocuments: Document[]) => void) {\n const [selectedDocuments, setSelectedDocuments] = useState<Document[]>([])\n\n // Keep a ref to the latest onSelectionChange to avoid unnecessary effect runs\n const onSelectionChangeRef = useRef(onSelectionChange)\n useEffect(() => {\n onSelectionChangeRef.current = onSelectionChange\n }, [onSelectionChange])\n\n // Notify parent when selection changes\n useEffect(() => {\n onSelectionChangeRef.current?.(selectedDocuments)\n }, [selectedDocuments])\n\n // Toggle document selection\n const toggleDocument = useCallback((document: Document) => {\n setSelectedDocuments(prev => {\n const isSelected = prev.some(d => d.id === document.id)\n return isSelected ? prev.filter(d => d.id !== document.id) : [...prev, document]\n })\n }, [])\n\n // Remove document from selection\n const removeDocument = useCallback((documentId: string, e: React.MouseEvent) => {\n e.stopPropagation()\n setSelectedDocuments(prev => prev.filter(d => d.id !== documentId))\n }, [])\n\n // Clear all selections\n const clearAllSelections = useCallback(() => {\n setSelectedDocuments([])\n }, [])\n\n return {\n selectedDocuments,\n toggleDocument,\n removeDocument,\n clearAllSelections\n }\n}\n\n/**\n * Hook for combining selected documents with search results\n * Optimized with Set for O(n) complexity instead of O(n*m)\n */\nexport function useCombinedDocuments(selectedDocuments: Document[], searchResults: Document[]) {\n return useMemo(() => {\n const combined = [...selectedDocuments]\n const selectedIds = new Set(selectedDocuments.map(doc => doc.id))\n\n // Add search results that aren't already selected\n searchResults.forEach(doc => {\n if (!selectedIds.has(doc.id)) {\n combined.push(doc)\n }\n })\n\n return combined\n }, [selectedDocuments, searchResults])\n}\n","'use client'\n\nimport { BookOpen, ChevronDown, FileText, Loader2, Search, X } from 'lucide-react'\nimport { useCallback, useState } from 'react'\nimport { cn } from '../lib/utils'\nimport { useChat } from './chat-context'\nimport { type Document, useCombinedDocuments, useDocumentSearch, useDocumentSelection } from './useDocumentSelector'\n\ninterface DocumentSelectorProps {\n onSelectionChange?: (selectedDocuments: Document[]) => void\n isMaximized?: boolean\n isSidePanel?: boolean\n}\n\nconst DocumentSelector = ({ onSelectionChange, isSidePanel = false }: DocumentSelectorProps) => {\n const [isExpanded, setIsExpanded] = useState(isSidePanel)\n const { searchCollections } = useChat()\n\n const {\n searchQuery,\n searchResults,\n isLoading,\n error,\n handleSearchChange: baseHandleSearchChange\n } = useDocumentSearch(searchCollections)\n const { selectedDocuments, toggleDocument, clearAllSelections } = useDocumentSelection(onSelectionChange)\n const allDocuments = useCombinedDocuments(selectedDocuments, searchResults)\n\n const handleSearchChange = useCallback(\n (query: string) => {\n baseHandleSearchChange(query)\n if (query.trim().length >= 2) {\n setIsExpanded(true)\n }\n },\n [baseHandleSearchChange]\n )\n\n const getDocumentIcon = (type: Document['type']) => {\n return type === 'book' ? <BookOpen className=\"w-4 h-4\" /> : <FileText className=\"w-4 h-4\" />\n }\n\n if (isSidePanel) {\n return (\n <div className=\"h-full flex flex-col bg-background border-r border-border\">\n <div className=\"p-4 border-b border-border\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Buscar contenido...\"\n value={searchQuery}\n onChange={e => handleSearchChange(e.target.value)}\n className=\"flex h-9 w-full rounded-md border border-input bg-background pl-10 pr-3 py-1 text-sm text-foreground shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n />\n </div>\n </div>\n\n {selectedDocuments.length > 0 && (\n <div className=\"flex items-center justify-between p-4 border-b border-border bg-muted/50\">\n <span className=\"text-sm font-medium text-foreground\">\n {selectedDocuments.length} documento{selectedDocuments.length !== 1 ? 's' : ''}\n </span>\n <button\n type=\"button\"\n onClick={clearAllSelections}\n className=\"text-sm text-destructive hover:text-destructive/80\"\n >\n Limpiar\n </button>\n </div>\n )}\n\n <div className=\"flex-1 overflow-y-auto\">\n {isLoading && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin mx-auto mb-2\" />\n Buscando...\n </div>\n )}\n\n {error && <div className=\"p-4 text-center text-sm text-destructive\">{error}</div>}\n\n {!isLoading && !error && searchQuery.length >= 2 && searchResults.length === 0 && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">Sin resultados para \"{searchQuery}\"</div>\n )}\n\n {!isLoading && !error && searchQuery.length < 2 && selectedDocuments.length === 0 && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">Busca libros o artículos para filtrar</div>\n )}\n\n {!isLoading && !error && allDocuments.length > 0 && (\n <div>\n {allDocuments.map(doc => {\n const isSelected = selectedDocuments.some(d => d.id === doc.id)\n return (\n <button\n type=\"button\"\n key={doc.id}\n onClick={() => toggleDocument(doc)}\n className={cn(\n 'w-full flex items-center justify-between p-4 text-left border-b border-border/50 hover:bg-muted/50 transition-colors',\n isSelected && 'bg-primary/10'\n )}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span className=\"text-muted-foreground\">{getDocumentIcon(doc.type)}</span>\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium text-foreground truncate\">{doc.title}</div>\n <div className=\"text-xs text-muted-foreground\">\n {doc.type === 'book' ? 'Libro' : 'Artículo'}\n </div>\n </div>\n </div>\n {isSelected && <X className=\"w-4 h-4 text-destructive flex-shrink-0\" />}\n </button>\n )\n })}\n </div>\n )}\n </div>\n </div>\n )\n }\n\n // Dropdown mode\n return (\n <div className=\"space-y-3\">\n <div className=\"relative\">\n <div className=\"flex\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Buscar libros o artículos...\"\n value={searchQuery}\n onChange={e => handleSearchChange(e.target.value)}\n className=\"flex h-9 w-full rounded-l-md border border-input bg-background pl-10 pr-3 py-1 text-sm text-foreground shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n />\n </div>\n <button\n type=\"button\"\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"inline-flex items-center gap-1 h-9 rounded-r-md border border-l-0 border-input bg-background px-3 text-sm text-foreground hover:bg-accent transition-colors\"\n >\n {selectedDocuments.length > 0 ? `${selectedDocuments.length} filtros` : 'Filtros'}\n <ChevronDown className={cn('w-4 h-4 transition-transform', isExpanded && 'rotate-180')} />\n </button>\n </div>\n\n {isExpanded && (\n <>\n <button\n type=\"button\"\n className=\"fixed inset-0 z-40 cursor-default bg-transparent border-none\"\n onClick={() => setIsExpanded(false)}\n aria-label=\"Cerrar selector\"\n />\n <div className=\"absolute top-full left-0 right-0 mt-2 bg-popover border border-border rounded-lg shadow-lg max-h-80 overflow-hidden z-50\">\n {selectedDocuments.length > 0 && (\n <div className=\"flex items-center justify-between p-3 border-b border-border bg-muted/50\">\n <span className=\"text-sm font-medium text-foreground\">\n {selectedDocuments.length} seleccionado{selectedDocuments.length !== 1 ? 's' : ''}\n </span>\n <button\n type=\"button\"\n onClick={clearAllSelections}\n className=\"text-sm text-destructive hover:text-destructive/80\"\n >\n Limpiar\n </button>\n </div>\n )}\n\n <div className=\"max-h-64 overflow-y-auto\">\n {isLoading && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin mx-auto mb-2\" />\n Buscando...\n </div>\n )}\n\n {!isLoading && !error && allDocuments.length > 0 && (\n <div>\n {allDocuments.map(doc => {\n const isSelected = selectedDocuments.some(d => d.id === doc.id)\n return (\n <button\n type=\"button\"\n key={doc.id}\n onClick={() => toggleDocument(doc)}\n className={cn(\n 'w-full flex items-center justify-between p-3 text-left border-b border-border/50 hover:bg-muted/50 transition-colors',\n isSelected && 'bg-primary/10'\n )}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span className=\"text-muted-foreground\">{getDocumentIcon(doc.type)}</span>\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium text-foreground truncate\">{doc.title}</div>\n <div className=\"text-xs text-muted-foreground\">\n {doc.type === 'book' ? 'Libro' : 'Artículo'}\n </div>\n </div>\n </div>\n {isSelected && <X className=\"w-4 h-4 text-destructive flex-shrink-0\" />}\n </button>\n )\n })}\n </div>\n )}\n\n {!isLoading && !error && searchQuery.length < 2 && selectedDocuments.length === 0 && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">Busca libros o artículos</div>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n </div>\n )\n}\n\nexport default DocumentSelector\n","'use client'\n\nimport { forwardRef, useEffect, useImperativeHandle, useState } from 'react'\nimport { useAssistantRuntime } from '../hooks/useAssistantRuntime'\nimport type { LinkComponent } from '../types/components'\nimport { Thread } from './assistant-ui/thread'\nimport { useChat } from './chat-context'\nimport DocumentSelector from './DocumentSelector'\nimport type { Document } from './useDocumentSelector'\n\nexport interface ChatInterfaceRef {\n handleNewConversation: () => void\n}\n\ninterface ChatInterfaceProps {\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n}\n\nconst ChatInterface = forwardRef<ChatInterfaceRef, ChatInterfaceProps>(({ generateHref, LinkComponent }, ref) => {\n const {\n isMaximized,\n selectedAgent,\n agents,\n // Session props from context\n conversationId,\n setConversationId,\n messages,\n setMessages,\n isLoadingSession,\n handleNewConversation,\n isLoadingAgents\n } = useChat()\n const [selectedDocuments, setSelectedDocuments] = useState<Document[]>([])\n const [isDesktop, setIsDesktop] = useState(false)\n\n // Find the full agent configuration\n const currentAgent = agents.find(agent => agent.slug === selectedAgent)\n\n // Create assistant-ui runtime\n const runtime = useAssistantRuntime({\n messages,\n setMessages,\n conversationId,\n setConversationId,\n selectedDocuments,\n selectedAgent\n })\n\n // Detect if device is desktop (window width >= 1024px)\n useEffect(() => {\n const checkIsDesktop = () => {\n setIsDesktop(window.innerWidth >= 1024)\n }\n\n checkIsDesktop()\n window.addEventListener('resize', checkIsDesktop)\n\n return () => window.removeEventListener('resize', checkIsDesktop)\n }, [])\n\n // Determine if we should use side panel layout\n const shouldUseSidePanel = isMaximized && isDesktop\n\n // Expose handleNewConversation to parent via ref\n useImperativeHandle(ref, () => ({\n handleNewConversation\n }))\n\n // Show loading state while restoring session or loading agents\n if (isLoadingSession || isLoadingAgents || (agents.length > 0 && !selectedAgent)) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center\">\n <div className=\"flex space-x-2 justify-center mb-4\">\n <div className=\"w-3 h-3 rounded-full bg-primary animate-bounce\" />\n <div className=\"w-3 h-3 rounded-full bg-primary animate-bounce delay-75\" />\n <div className=\"w-3 h-3 rounded-full bg-primary animate-bounce delay-150\" />\n </div>\n <p className=\"text-muted-foreground\">\n {isLoadingSession ? 'Cargando conversación...' : 'Cargando asistente...'}\n </p>\n </div>\n </div>\n )\n }\n\n // Show error/empty state if no agents loaded\n if (!isLoadingAgents && agents.length === 0) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center max-w-md p-6\">\n <p className=\"text-lg font-medium text-muted-foreground mb-2\">No hay asistentes disponibles</p>\n <p className=\"text-sm text-muted-foreground\">Por favor, contacta con el administrador del sistema.</p>\n </div>\n </div>\n )\n }\n\n if (shouldUseSidePanel) {\n // Desktop maximized mode: Side panel layout (1/4 + 3/4)\n return (\n <div className=\"flex h-full\">\n {/* Document Selector Side Panel (1/4 width) */}\n <div className=\"w-1/4 flex-shrink-0\">\n <DocumentSelector onSelectionChange={setSelectedDocuments} isMaximized={isMaximized} isSidePanel={true} />\n </div>\n\n {/* Chat Area (3/4 width) */}\n <div className=\"flex-1 flex flex-col\">\n <Thread\n runtime={runtime}\n welcomeTitle={currentAgent?.welcomeTitle || undefined}\n welcomeSubtitle={currentAgent?.welcomeSubtitle || undefined}\n suggestedQuestions={currentAgent?.suggestedQuestions}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n agentName={currentAgent?.name}\n />\n </div>\n </div>\n )\n }\n\n // Default layout: Mobile/tablet or desktop non-maximized (dropdown mode)\n return (\n <div className=\"flex flex-col h-full\">\n {/* Document Selector */}\n <div className=\"border-b border-border p-4 bg-background\">\n <DocumentSelector onSelectionChange={setSelectedDocuments} isMaximized={isMaximized} isSidePanel={false} />\n </div>\n\n {/* Chat Thread */}\n <div className=\"flex-1 min-h-0\">\n <Thread\n runtime={runtime}\n welcomeTitle={currentAgent?.welcomeTitle || undefined}\n welcomeSubtitle={currentAgent?.welcomeSubtitle || undefined}\n suggestedQuestions={currentAgent?.suggestedQuestions}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n agentName={currentAgent?.name}\n />\n </div>\n </div>\n )\n})\n\nChatInterface.displayName = 'ChatInterface'\n\nexport default ChatInterface\n","'use client'\nimport { formatDistanceToNow } from 'date-fns'\nimport { es } from 'date-fns/locale'\nimport { Check, Edit2, Loader2, MessageSquare, MoreHorizontal, Trash2, X } from 'lucide-react'\nimport type React from 'react'\nimport { useEffect, useState } from 'react'\nimport type { PublicAgentInfo } from '../adapters/ChatAdapter'\nimport type { SessionSummary } from '../hooks/useChatSession'\nimport { cn } from '../lib/utils'\n\ninterface ChatHistoryListProps {\n sessions: SessionSummary[]\n activeSessionId: string | null\n isLoading: boolean\n onSelectSession: (id: string) => void\n onRenameSession: (id: string, newTitle: string) => Promise<boolean>\n onDeleteSession: (id: string) => Promise<boolean>\n onLoadHistory: () => Promise<void>\n agents?: PublicAgentInfo[]\n}\n\nexport const ChatHistoryList = ({\n sessions,\n activeSessionId,\n isLoading,\n onSelectSession,\n onRenameSession,\n onDeleteSession,\n onLoadHistory,\n agents = []\n}: ChatHistoryListProps) => {\n const [editingId, setEditingId] = useState<string | null>(null)\n const [editTitle, setEditTitle] = useState('')\n const [deletingId, setDeletingId] = useState<string | null>(null)\n const [menuOpenId, setMenuOpenId] = useState<string | null>(null)\n\n // Helper to get agent name from slug\n const getAgentName = (agentSlug?: string) => {\n if (!agentSlug) return null\n const agent = agents.find(a => a.slug === agentSlug)\n return agent?.name || null\n }\n\n useEffect(() => {\n onLoadHistory()\n }, [onLoadHistory])\n\n const handleStartEdit = (session: SessionSummary, e: React.MouseEvent) => {\n e.stopPropagation()\n setEditingId(session.conversation_id)\n setEditTitle(session.title || 'Nueva conversación')\n setMenuOpenId(null)\n }\n\n const handleSaveEdit = async (e: React.MouseEvent | React.FormEvent) => {\n e.stopPropagation()\n if (!editingId) return\n\n await onRenameSession(editingId, editTitle)\n setEditingId(null)\n }\n\n const handleCancelEdit = () => {\n setEditingId(null)\n }\n\n const handleDeleteClick = (id: string, e: React.MouseEvent) => {\n e.stopPropagation()\n setDeletingId(id)\n setMenuOpenId(null)\n }\n\n const handleConfirmDelete = async (e: React.MouseEvent) => {\n e.stopPropagation()\n if (deletingId) {\n await onDeleteSession(deletingId)\n setDeletingId(null)\n }\n }\n\n const handleCancelDelete = (e: React.MouseEvent) => {\n e.stopPropagation()\n setDeletingId(null)\n }\n\n const toggleMenu = (id: string, e: React.MouseEvent) => {\n e.stopPropagation()\n setMenuOpenId(menuOpenId === id ? null : id)\n }\n\n if (isLoading && sessions.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-muted-foreground\">\n <Loader2 className=\"h-6 w-6 animate-spin mb-2\" />\n <span className=\"text-sm\">Cargando historial...</span>\n </div>\n )\n }\n\n if (sessions.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-muted-foreground\">\n <MessageSquare className=\"h-8 w-8 mb-2 opacity-20\" />\n <span className=\"text-sm\">No hay conversaciones anteriores</span>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-1\">\n {sessions.map((session, _index) => (\n <button\n type=\"button\"\n key={session.conversation_id}\n className={cn(\n 'group relative flex items-center rounded-md px-2 py-2 text-sm transition-colors hover:bg-accent/50 cursor-pointer w-full text-left',\n activeSessionId === session.conversation_id ? 'bg-accent text-accent-foreground' : 'text-foreground'\n )}\n onClick={() => onSelectSession(session.conversation_id)}\n >\n <MessageSquare className=\"mr-2 h-4 w-4 opacity-70 flex-shrink-0\" />\n\n {editingId === session.conversation_id ? (\n <div className=\"flex items-center flex-1 gap-1\">\n <input\n type=\"text\"\n value={editTitle}\n onChange={e => setEditTitle(e.target.value)}\n onClick={e => e.stopPropagation()}\n className=\"h-6 flex-1 rounded-sm border border-input bg-transparent px-1 text-xs outline-none focus:ring-1 focus:ring-ring\"\n onKeyDown={e => {\n e.stopPropagation()\n if (e.key === 'Enter') handleSaveEdit(e)\n if (e.key === 'Escape') {\n setEditingId(null)\n setEditTitle('')\n }\n }}\n />\n <button\n type=\"button\"\n onClick={e => {\n e.stopPropagation()\n handleSaveEdit(e)\n }}\n className=\"p-1 hover:text-green-500\"\n >\n <Check className=\"h-3 w-3\" />\n </button>\n <button\n type=\"button\"\n onClick={e => {\n e.stopPropagation()\n handleCancelEdit()\n }}\n className=\"p-1 hover:text-red-500\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </div>\n ) : deletingId === session.conversation_id ? (\n <div className=\"flex items-center flex-1 justify-between bg-destructive/10 -mx-2 px-2 py-1 rounded\">\n <span className=\"text-xs text-destructive font-medium\">¿Borrar?</span>\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={handleConfirmDelete}\n className=\"p-1 text-destructive hover:bg-destructive/20 rounded\"\n >\n <Check className=\"h-3 w-3\" />\n </button>\n <button\n type=\"button\"\n onClick={handleCancelDelete}\n className=\"p-1 text-muted-foreground hover:bg-black/5 rounded\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </div>\n </div>\n ) : (\n <>\n <div className=\"flex-1 overflow-hidden\">\n <div className=\"truncate font-medium\">{session.title || 'Conversación sin título'}</div>\n {(() => {\n const agentName = getAgentName(session.agentSlug)\n return agentName && <div className=\"truncate text-xs text-primary/80 font-medium\">{agentName}</div>\n })()}\n <div className=\"truncate text-xs text-muted-foreground opacity-70\">\n {formatDistanceToNow(new Date(session.last_activity), { addSuffix: true, locale: es })}\n </div>\n </div>\n\n <div className=\"relative flex items-center\">\n {menuOpenId === session.conversation_id ? (\n <div className=\"flex items-center gap-1 animate-in fade-in slide-in-from-right-4 duration-200 bg-background/80 backdrop-blur-sm rounded-md p-1 pl-2 ml-[-8px]\">\n <button\n type=\"button\"\n onClick={e => handleStartEdit(session, e)}\n className=\"p-1.5 text-foreground/70 hover:text-foreground hover:bg-accent rounded-sm transition-colors\"\n title=\"Renombrar\"\n >\n <Edit2 className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={e => handleDeleteClick(session.conversation_id, e)}\n className=\"p-1.5 text-destructive/70 hover:text-destructive hover:bg-destructive/10 rounded-sm transition-colors\"\n title=\"Eliminar\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={e => {\n e.stopPropagation()\n setMenuOpenId(null)\n }}\n className=\"p-1.5 text-muted-foreground hover:text-foreground hover:bg-accent rounded-sm transition-colors\"\n title=\"Cerrar menú\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={e => toggleMenu(session.conversation_id, e)}\n className={cn(\n 'ml-1 rounded p-1 text-muted-foreground hover:bg-accent transition-all',\n 'opacity-70 group-hover:opacity-100'\n )}\n title=\"Opciones\"\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n </>\n )}\n </button>\n ))}\n </div>\n )\n}\n","'use client'\n\nimport { Check, ChevronDown, ChevronLeft, History, MessageSquarePlus } from 'lucide-react'\nimport { useState } from 'react'\nimport { cn } from '../lib/utils'\nimport { ChatHistoryList } from './ChatHistoryList'\nimport { useChat } from './chat-context'\n\ninterface ChatMenuDropdownProps {\n title: string\n onNewConversation: () => void\n}\n\nconst ChatMenuDropdown = ({ title, onNewConversation }: ChatMenuDropdownProps) => {\n const {\n agents,\n selectedAgent,\n setSelectedAgent,\n // History props\n sessionsHistory,\n isLoadingHistory,\n loadHistory,\n loadSession,\n renameSession,\n deleteSession,\n conversationId\n } = useChat()\n const [isOpen, setIsOpen] = useState(false)\n const [pendingAgentSlug, setPendingAgentSlug] = useState<string | null>(null)\n const [menuView, setMenuView] = useState<'main' | 'history'>('main')\n\n // Import History List (using dynamic import might be cleaner but inline here is fine if imported at top)\n // Assuming ChatHistoryList is imported at top. If not, I need to add import.\n\n const handleNewConversationClick = () => {\n onNewConversation()\n setIsOpen(false)\n }\n\n const handleAgentSelect = (agentSlug: string) => {\n if (agentSlug === selectedAgent) {\n setIsOpen(false)\n return\n }\n setPendingAgentSlug(agentSlug)\n }\n\n const confirmAgentChange = () => {\n if (pendingAgentSlug) {\n setSelectedAgent(pendingAgentSlug)\n onNewConversation()\n setPendingAgentSlug(null)\n setIsOpen(false)\n }\n }\n\n const cancelAgentChange = () => {\n setPendingAgentSlug(null)\n }\n\n const handleOpenHistory = () => {\n setMenuView('history')\n }\n\n const handleBackToMenu = () => {\n setMenuView('main')\n }\n\n const handleSelectHistorySession = async (id: string) => {\n await loadSession(id)\n setIsOpen(false)\n }\n\n // Reset view when closing\n const toggleOpen = () => {\n if (isOpen) {\n setIsOpen(false)\n // Small delay to reset view after animation could be nice, but instant is fine\n setTimeout(() => setMenuView('main'), 200)\n } else {\n setIsOpen(true)\n }\n }\n\n // Find current agent name for title\n const currentAgent = agents.find(a => a.slug === selectedAgent)\n const displayTitle = currentAgent?.name || currentAgent?.slug || title\n const pendingAgent = agents.find(a => a.slug === pendingAgentSlug)\n\n return (\n <>\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={toggleOpen}\n className=\"flex items-center gap-2 text-xl font-bold text-foreground hover:text-foreground/80 transition-colors\"\n aria-label=\"Menú de chat\"\n >\n <span>{displayTitle}</span>\n <ChevronDown className={cn('w-4 h-4 transition-transform', isOpen && 'rotate-180')} />\n </button>\n\n {isOpen && (\n <>\n <button\n type=\"button\"\n className=\"fixed inset-0 z-40 cursor-default bg-transparent border-none\"\n onClick={toggleOpen}\n aria-label=\"Cerrar menú\"\n />\n\n {/* Dropdown */}\n <div className=\"absolute top-full left-0 mt-2 z-50 min-w-[16rem] max-w-[20rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\">\n {menuView === 'main' ? (\n <>\n <button\n type=\"button\"\n onClick={handleNewConversationClick}\n className=\"relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground\"\n >\n <MessageSquarePlus className=\"w-4 h-4\" />\n Nueva conversación\n </button>\n\n <button\n type=\"button\"\n onClick={handleOpenHistory}\n className=\"relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground\"\n >\n <History className=\"w-4 h-4\" />\n Historial de chats\n </button>\n\n {agents.length > 1 && (\n <>\n <div className=\"-mx-1 my-1 h-px bg-border\" />\n <div className=\"px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Cambiar Agente\n </div>\n {agents.map(agent => (\n <button\n type=\"button\"\n key={agent.slug}\n onClick={() => handleAgentSelect(agent.slug)}\n className={cn(\n 'relative flex w-full cursor-pointer select-none items-center justify-between gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground',\n selectedAgent === agent.slug && 'text-primary font-medium bg-primary/10'\n )}\n >\n <span className=\"truncate\">{agent.name || agent.slug}</span>\n {selectedAgent === agent.slug && <Check className=\"w-4 h-4 flex-shrink-0\" />}\n </button>\n ))}\n </>\n )}\n </>\n ) : (\n <div className=\"flex flex-col max-h-[60vh]\">\n <div className=\"flex items-center gap-2 px-2 py-1.5 border-b border-border mb-1\">\n <button type=\"button\" onClick={handleBackToMenu} className=\"p-1 hover:bg-accent rounded-sm\">\n <ChevronLeft className=\"w-4 h-4\" />\n </button>\n <span className=\"text-sm font-semibold\">Historial</span>\n </div>\n\n <div className=\"overflow-y-auto custom-scrollbar p-1\">\n <ChatHistoryList\n sessions={sessionsHistory}\n activeSessionId={conversationId}\n isLoading={isLoadingHistory}\n onSelectSession={handleSelectHistorySession}\n onRenameSession={renameSession}\n onDeleteSession={deleteSession}\n onLoadHistory={loadHistory}\n agents={agents}\n />\n </div>\n </div>\n )}\n </div>\n </>\n )}\n </div>\n\n {/* Confirmation Dialog */}\n {pendingAgentSlug && (\n <>\n <button\n type=\"button\"\n className=\"fixed inset-0 z-50 bg-black/50 backdrop-blur-sm cursor-default border-none\"\n onClick={cancelAgentChange}\n aria-label=\"Cerrar diálogo\"\n />\n <div className=\"fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 w-full max-w-md border border-border bg-background p-6 shadow-lg rounded-xl\">\n <h3 className=\"text-lg font-semibold\">¿Cambiar a {pendingAgent?.name || pendingAgent?.slug}?</h3>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n Se iniciará una nueva conversación con este agente. La conversación actual se guardará en el historial.\n </p>\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={cancelAgentChange}\n className=\"inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground\"\n >\n Cancelar\n </button>\n <button\n type=\"button\"\n onClick={confirmAgentChange}\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90\"\n >\n Sí, cambiar\n </button>\n </div>\n </div>\n </>\n )}\n </>\n )\n}\n\nexport default ChatMenuDropdown\n","'use client'\n\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { Bot, Maximize2, Minimize2, X } from 'lucide-react'\nimport { useRef } from 'react'\nimport { cn } from '../lib/utils'\nimport { DefaultImage, DefaultLink, type ImageComponent, type LinkComponent } from '../types/components'\nimport ChatInterface, { type ChatInterfaceRef } from './ChatInterface'\nimport ChatMenuDropdown from './ChatMenuDropdown'\nimport { useChat } from './chat-context'\n\ninterface FloatingChatPanelProps {\n isOpen: boolean\n onClose: () => void\n aiIcon?: string\n agentName?: string\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n ImageComponent?: ImageComponent\n LinkComponent?: LinkComponent\n}\n\nconst FloatingChatPanel = ({\n isOpen,\n onClose,\n aiIcon,\n agentName = 'Asistente',\n generateHref,\n ImageComponent: Image = DefaultImage,\n LinkComponent: Link = DefaultLink\n}: FloatingChatPanelProps) => {\n const { isMaximized, setMaximized } = useChat()\n const chatInterfaceRef = useRef<ChatInterfaceRef>(null)\n\n const handleNewConversation = () => {\n chatInterfaceRef.current?.handleNewConversation()\n }\n\n return (\n <>\n {/* Backdrop cuando esta maximizado - solo en desktop */}\n <AnimatePresence>\n {isMaximized && isOpen && (\n <motion.div\n className=\"fixed inset-0 bg-black/20 backdrop-blur-sm z-40 hidden lg:block\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n onClick={() => setMaximized(false)}\n />\n )}\n </AnimatePresence>\n\n <AnimatePresence>\n {isOpen && (\n <motion.div\n className=\"fixed bg-background shadow-2xl flex flex-col z-50 lg:border lg:border-border overflow-hidden\"\n style={{ borderWidth: '0.5px' }}\n initial={{\n x: '-100vw',\n left: 0,\n top: 0,\n bottom: 0,\n width: '100vw',\n borderRadius: '0px'\n }}\n animate={\n isMaximized\n ? {\n x: 0,\n left: 0,\n top: 0,\n right: 0,\n bottom: 0,\n width: '100vw',\n height: '100vh',\n borderRadius: '0px'\n }\n : typeof window !== 'undefined' && window.innerWidth < 1024\n ? {\n x: 0,\n left: 0,\n top: 0,\n right: 'auto',\n bottom: 0,\n width: '100vw',\n height: '100vh',\n borderRadius: '0px'\n }\n : {\n x: 0,\n left: '1rem',\n top: '5rem',\n right: 'auto',\n bottom: '1rem',\n width: '33.333333%',\n height: 'auto',\n borderRadius: '0.75rem'\n }\n }\n exit={{\n x: '-100vw',\n transition: {\n type: 'spring',\n damping: 20,\n stiffness: 200\n }\n }}\n transition={{\n type: 'spring',\n damping: 25,\n stiffness: 250\n }}\n >\n {/* Header with gradient */}\n <div className=\"flex justify-between items-center p-4 border-b border-border flex-shrink-0 bg-gradient-to-r from-background via-muted/30 to-background\">\n <div className=\"flex items-center gap-3\">\n {/* Avatar with breathing effect and status indicator */}\n <div className=\"relative\">\n <motion.div\n className={cn(\n 'w-10 h-10 rounded-full p-0.5 flex-shrink-0 bg-gradient-to-br from-primary via-primary/80 to-primary/60 animate-pulse-glow',\n !aiIcon && 'flex items-center justify-center'\n )}\n >\n {aiIcon ? (\n <div className=\"w-full h-full rounded-full overflow-hidden\">\n <Image\n src={aiIcon}\n alt={agentName}\n className=\"w-full h-full object-cover\"\n width={40}\n height={40}\n />\n </div>\n ) : (\n <div className=\"w-full h-full rounded-full overflow-hidden bg-primary flex items-center justify-center\">\n <Bot className=\"w-6 h-6 text-primary-foreground\" />\n </div>\n )}\n </motion.div>\n {/* Status indicator - AI available */}\n <motion.div\n className=\"absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-chat-status-online border-2 border-background\"\n animate={{\n scale: [1, 1.2, 1],\n opacity: [1, 0.7, 1]\n }}\n transition={{\n duration: 2,\n repeat: Infinity,\n ease: 'easeInOut'\n }}\n />\n </div>\n <div className=\"flex flex-col\">\n <ChatMenuDropdown title={agentName} onNewConversation={handleNewConversation} />\n <span className=\"text-xs text-muted-foreground\">Disponible para ayudarte</span>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {/* Boton maximizar/minimizar - solo visible en desktop (>=1024px) */}\n <motion.button\n onClick={() => setMaximized(!isMaximized)}\n className=\"hidden lg:flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors\"\n aria-label={isMaximized ? 'Minimizar' : 'Maximizar'}\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n {isMaximized ? <Minimize2 className=\"w-4 h-4\" /> : <Maximize2 className=\"w-4 h-4\" />}\n </motion.button>\n <motion.button\n onClick={onClose}\n className=\"h-9 w-9 flex items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors\"\n aria-label=\"Cerrar chat\"\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n <X className=\"w-5 h-5\" />\n </motion.button>\n </div>\n </div>\n\n {/* Chat Content */}\n <div className=\"flex-1 overflow-hidden\">\n <ChatInterface ref={chatInterfaceRef} generateHref={generateHref} LinkComponent={Link} />\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </>\n )\n}\n\nexport default FloatingChatPanel\n","'use client'\n\nimport type { ImageComponent, LinkComponent } from '../types/components'\nimport FloatingChatButton from './buttons/FloatingChatButton'\nimport { useChat } from './chat-context'\nimport FloatingChatPanel from './FloatingChatPanel'\n\n/**\n * Minimal user type - consumer provides their own user type\n */\ninterface User {\n id: string | number\n [key: string]: unknown\n}\n\ninterface FloatingChatManagerProps {\n aiIcon?: string\n useUser: () => { user: User | null }\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n ImageComponent?: ImageComponent\n}\n\nconst FloatingChatManager = ({\n aiIcon,\n useUser,\n generateHref,\n LinkComponent,\n ImageComponent\n}: FloatingChatManagerProps) => {\n const { user } = useUser()\n const { isPanelOpen, openPanel, closePanel, agents, selectedAgent } = useChat()\n\n if (!user) return null\n\n const currentAgent = agents.find(agent => agent.slug === selectedAgent)\n const currentAvatar =\n currentAgent?.avatar && currentAgent.avatar.trim() !== '' ? currentAgent.avatar : aiIcon || undefined\n const currentAgentName = currentAgent?.name || 'Asistente'\n\n return (\n <>\n <FloatingChatButton onOpen={openPanel} aiIcon={currentAvatar} ImageComponent={ImageComponent} />\n {/* Siempre renderizar para que AnimatePresence funcione */}\n <FloatingChatPanel\n isOpen={isPanelOpen}\n onClose={closePanel}\n aiIcon={currentAvatar}\n agentName={currentAgentName}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n ImageComponent={ImageComponent}\n />\n </>\n )\n}\n\nexport default FloatingChatManager\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"react.mjs","names":["requestBody: Record<string, unknown>","MarkdownText: FC<MarkdownTextProps>","defaultCollectionResolver: Required<CollectionTypeResolver>","data: ChunkData","DefaultLink: LinkComponent","DefaultImage: ImageComponent","itemVariants","metadata: { section?: string; path?: string }","RelevanceBar: React.FC<{ score: number }>","BreadcrumbPath: React.FC<{ path: string }>","SectionPill: React.FC<{ section: string }>","MetadataPills: React.FC<{ metadata: { section?: string; path?: string } | null }>","ExpandedContentBody: React.FC<{\n isLoading: boolean\n error: string | null\n displayContent: string\n cleanContent: string\n metadata: { section?: string; path?: string } | null\n expandedSource: Source\n getContentType: (type: string) => string\n renderViewMore?: SourcesListProps['renderViewMore']\n handleViewMore: () => void\n generateHref: SourcesListProps['generateHref']\n LinkComponent?: LinkComponent\n}>","ExpandedSourceCard: React.FC<{\n expandedSource: Source\n expandedSourceId: string\n loadedContent: string\n getChunkState: ReturnType<typeof useChunkLoader>['getChunkState']\n getIcon: (type: string) => React.ReactNode\n getLabel: (type: string) => string\n getContentType: (type: string) => string\n onClose: () => void\n handleViewMore: () => void\n renderViewMore?: SourcesListProps['renderViewMore']\n generateHref: SourcesListProps['generateHref']\n LinkComponent?: LinkComponent\n}>","SourceListItem: React.FC<{\n source: Source\n idx: number\n getIcon: (type: string) => React.ReactNode\n getLabel: (type: string) => string\n onClick: (id: string) => void\n}>","SourcesList: React.FC<SourcesListProps>","Thread: FC<ThreadProps>","ThreadWelcome: FC<ThreadWelcomeProps>","SuggestionCard: FC<SuggestionCardProps>","LimitAlert: FC","TokenUsageBar: FC","TypingIndicator: FC","Composer: FC","UserMessage: FC","AssistantMessage: FC","userMessage: Message","receivedSources: Source[]","err: unknown","document","DocumentSelector","ChatMenuDropdown","ChatInterface","FloatingChatButton","FloatingChatPanel"],"sources":["../src/adapters/MockAdapter.ts","../src/adapters/NexoPayloadChatAdapter.ts","../src/components/assistant-ui/markdown-text.tsx","../src/lib/utils.ts","../src/hooks/useChatSession.ts","../src/components/chat-context.tsx","../src/hooks/useChunkLoader.ts","../src/types/components.tsx","../src/components/buttons/ViewMoreLink.tsx","../src/components/SourcesList.tsx","../src/components/assistant-ui/thread.tsx","../src/components/buttons/FloatingChatButton.tsx","../src/hooks/useAssistantRuntime.ts","../src/components/useDocumentSelector.ts","../src/components/DocumentSelector.tsx","../src/components/ChatInterface.tsx","../src/components/ChatHistoryList.tsx","../src/components/ChatMenuDropdown.tsx","../src/components/FloatingChatPanel.tsx","../src/components/FloatingChatManager.tsx"],"sourcesContent":["import type {\n ChatAdapter,\n Message,\n PublicAgentInfo,\n SendMessageContext,\n SessionSummary,\n StreamCallbacks\n} from './ChatAdapter'\n\nexport class MockAdapter implements ChatAdapter {\n private sessions: Map<string, { id: string; title: string; messages: Message[] }> = new Map()\n\n constructor() {\n // Initial mock data\n this.sessions.set('mock-session-1', {\n id: 'mock-session-1',\n title: 'Mock Conversation',\n messages: [\n {\n role: 'assistant',\n content: 'Hello! I am a mock assistant. How can I help you?',\n timestamp: new Date(Date.now() - 1000000)\n }\n ]\n })\n }\n\n async sendMessage(\n message: string,\n context: SendMessageContext,\n callbacks: StreamCallbacks,\n signal?: AbortSignal\n ): Promise<void> {\n const convoId = context.conversationId || `mock-session-${Date.now()}`\n\n // Simulate initial delay\n await new Promise(resolve => setTimeout(resolve, 500))\n if (context.conversationId !== convoId) {\n callbacks.onConversationId?.(convoId)\n }\n\n if (!context.conversationId) {\n // Create new session if null\n this.sessions.set(convoId, {\n id: convoId,\n title: 'New Mock Chat',\n messages: []\n })\n }\n\n // Simulate streaming\n const responseText = `This is a mock response to: \"${message}\". \\n\\nI am simulating a stream of tokens.`\n const tokens = responseText.split(/(?=[\\s\\S])/) // Split by chars for smooth effect\n\n for (const token of tokens) {\n if (signal?.aborted) return\n await new Promise(resolve => setTimeout(resolve, 30))\n callbacks.onToken?.(token)\n }\n\n // Simulate sources\n callbacks.onSources?.([\n {\n id: 'mock-source-1',\n title: 'Mock Source Article',\n slug: 'mock-source-article',\n type: 'posts',\n chunkIndex: 0,\n relevanceScore: 0.95,\n content: 'Mock content for the source.'\n }\n ])\n\n // Mock usage\n callbacks.onUsage?.({\n daily_limit: 1000,\n daily_used: 150,\n daily_remaining: 850,\n reset_at: new Date(Date.now() + 86400000).toISOString()\n })\n\n callbacks.onDone?.()\n }\n\n async getActiveSession(): Promise<{\n conversationId: string\n messages: Message[]\n } | null> {\n // Return last session or null\n if (this.sessions.size > 0) {\n const last = Array.from(this.sessions.values()).pop()\n return last ? { conversationId: last.id, messages: last.messages } : null\n }\n return null\n }\n\n async getHistory(): Promise<SessionSummary[]> {\n return Array.from(this.sessions.values()).map(s => ({\n conversation_id: s.id,\n title: s.title,\n last_activity: new Date().toISOString(), // Mock\n status: 'active'\n }))\n }\n\n async loadSession(id: string): Promise<{ conversationId: string; messages: Message[] } | null> {\n const session = this.sessions.get(id)\n return session ? { conversationId: session.id, messages: session.messages } : null\n }\n\n async renameSession(id: string, newTitle: string): Promise<boolean> {\n const session = this.sessions.get(id)\n if (session) {\n session.title = newTitle\n return true\n }\n return false\n }\n\n async deleteSession(id: string): Promise<boolean> {\n return this.sessions.delete(id)\n }\n\n async getAgents(): Promise<PublicAgentInfo[]> {\n return [\n { slug: 'default-mock-agent', name: 'Mock Agent' },\n { slug: 'advanced-mock-agent', name: 'Advanced Mock Agent' }\n ]\n }\n}\n","import type {\n ChatAdapter,\n Message,\n PublicAgentInfo,\n SendMessageContext,\n SessionSummary,\n Source,\n StreamCallbacks\n} from './ChatAdapter'\n\ntype SSEEvent =\n | { type: 'conversation_id'; data: string }\n | { type: 'token'; data: string }\n | { type: 'sources'; data: Source[] }\n | { type: 'done' }\n | { type: 'usage'; data: { daily_limit: number; daily_used: number; daily_remaining: number; reset_at: string } }\n | { type: 'error'; data?: { error?: string; message?: string; chatId?: string } }\n\nfunction handleSSEEvent(event: SSEEvent, callbacks: StreamCallbacks): void {\n switch (event.type) {\n case 'conversation_id':\n callbacks.onConversationId?.(event.data)\n break\n\n case 'token':\n callbacks.onToken?.(event.data)\n break\n\n case 'sources':\n callbacks.onSources?.(event.data)\n break\n\n case 'done':\n callbacks.onDone?.()\n break\n\n case 'usage':\n callbacks.onUsage?.(event.data)\n break\n\n case 'error':\n handleSSEError(event)\n break\n }\n}\n\nfunction handleSSEError(event: Extract<SSEEvent, { type: 'error' }>): never {\n const errorData = event.data\n if (errorData?.error === 'EXPIRED_CONVERSATION') {\n const error = new Error(errorData?.message || 'Esta conversación ha expirado') as Error & {\n code: string\n chatId: string\n }\n error.code = 'EXPIRED_CONVERSATION'\n error.chatId = errorData?.chatId || ''\n throw error\n }\n throw new Error(errorData?.error || 'Streaming error')\n}\n\nfunction parseSSELine(line: string): { done: boolean; event: SSEEvent | null } {\n if (!line.startsWith('data: ')) {\n return { done: false, event: null }\n }\n\n const data = line.slice(6)\n if (data === '[DONE]') {\n return { done: true, event: null }\n }\n\n try {\n const event = JSON.parse(data) as SSEEvent\n return { done: false, event }\n } catch (e) {\n if (!(e instanceof SyntaxError)) throw e\n console.warn('Failed to parse SSE event:', data)\n return { done: false, event: null }\n }\n}\n\nexport class NexoPayloadChatAdapter implements ChatAdapter {\n async sendMessage(\n message: string,\n context: SendMessageContext,\n callbacks: StreamCallbacks,\n signal?: AbortSignal\n ): Promise<void> {\n const requestBody: Record<string, unknown> = {\n message: message,\n agentSlug: context.agentSlug || undefined\n }\n\n if (context.selectedDocuments.length > 0) {\n requestBody.selectedDocuments = context.selectedDocuments\n }\n\n if (context.conversationId) {\n requestBody.chatId = context.conversationId\n }\n\n try {\n const response = await fetch('/api/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestBody),\n signal\n })\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Error al procesar' }))\n\n // Handle 429\n if (response.status === 429 && errorData.limit_info && callbacks.onUsage) {\n callbacks.onUsage({\n daily_limit: errorData.limit_info.limit,\n daily_used: errorData.limit_info.used,\n daily_remaining: errorData.limit_info.remaining,\n reset_at: errorData.limit_info.reset_at\n })\n throw new Error(errorData.error || 'Has alcanzado tu límite diario de tokens.')\n }\n\n throw new Error(errorData.error || 'Error al procesar')\n }\n\n await this.processStream(response, callbacks)\n } catch (err) {\n if (callbacks.onError) {\n callbacks.onError(err instanceof Error ? err : new Error('Unknown error'))\n } else {\n throw err\n }\n }\n }\n\n private async processStream(response: Response, callbacks: StreamCallbacks) {\n const reader = response.body?.getReader()\n const decoder = new TextDecoder()\n\n if (!reader) throw new Error('No stream reader')\n\n let buffer = ''\n let streamDone = false\n\n while (!streamDone) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() || ''\n\n for (const line of lines) {\n const result = parseSSELine(line)\n if (result.done) {\n streamDone = true\n break\n }\n if (result.event) {\n handleSSEEvent(result.event, callbacks)\n }\n }\n }\n }\n\n async getActiveSession(): Promise<{\n conversationId: string\n messages: Message[]\n } | null> {\n try {\n const response = await fetch('/api/chat/session?active=true')\n if (response.ok) {\n const sessionData = await response.json()\n // Don't load if session is closed/expired\n if (sessionData.status === 'closed') {\n console.warn('[NexoPayloadChatAdapter] Active session is closed/expired, clearing')\n return null\n }\n return {\n conversationId: sessionData.conversation_id,\n messages: this.parseBackendMessages(sessionData.messages)\n }\n }\n return null\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error loading active session:', error)\n return null\n }\n }\n\n async getHistory(): Promise<SessionSummary[]> {\n try {\n const response = await fetch('/api/chat/sessions')\n if (response.ok) {\n const data = await response.json()\n const sessions = data.sessions || []\n // Filter out closed/expired sessions\n return sessions.filter((session: SessionSummary) => session.status === 'active')\n }\n return []\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error loading history:', error)\n return []\n }\n }\n\n async loadSession(id: string): Promise<{ conversationId: string; messages: Message[] } | null> {\n try {\n const response = await fetch(`/api/chat/session?conversationId=${encodeURIComponent(id)}`)\n if (response.ok) {\n const sessionData = await response.json()\n // Don't load if session is closed/expired\n if (sessionData.status === 'closed') {\n console.warn('[NexoPayloadChatAdapter] Session is closed/expired:', id)\n return null\n }\n return {\n conversationId: sessionData.conversation_id,\n messages: this.parseBackendMessages(sessionData.messages)\n }\n }\n return null\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error loading session:', error)\n return null\n }\n }\n\n async renameSession(id: string, newTitle: string): Promise<boolean> {\n try {\n const response = await fetch(`/api/chat/session?conversationId=${encodeURIComponent(id)}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ title: newTitle })\n })\n return response.ok\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error renaming session:', error)\n return false\n }\n }\n\n async deleteSession(id: string): Promise<boolean> {\n try {\n const response = await fetch(`/api/chat/session?conversationId=${encodeURIComponent(id)}`, {\n method: 'DELETE'\n })\n return response.ok\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error deleting session:', error)\n return false\n }\n }\n\n async getAgents(): Promise<PublicAgentInfo[]> {\n try {\n const response = await fetch('/api/chat/agents')\n if (response.ok) {\n const data = await response.json()\n return data.agents || []\n }\n return []\n } catch (error) {\n console.error('[NexoPayloadChatAdapter] Error loading agents:', error)\n return []\n }\n }\n\n private parseBackendMessages(backendMessages: Record<string, unknown>[]): Message[] {\n if (!backendMessages) return []\n return backendMessages.map((msg: Record<string, unknown>) => ({\n role: msg.role as Message['role'],\n content: msg.content as string,\n timestamp: new Date(msg.timestamp as string),\n sources: (msg.sources as Record<string, unknown>[])?.map((s: Record<string, unknown>) => ({\n id: s.id as string,\n title: s.title as string,\n slug: s.slug as string,\n type: (s.type as string) || 'document',\n chunkIndex: (s.chunk_index as number) || 0,\n relevanceScore: 0,\n content: ''\n }))\n }))\n }\n}\n","'use client'\n\nimport type { FC } from 'react'\nimport ReactMarkdown from 'react-markdown'\n\nexport interface MarkdownTextProps {\n text: string\n}\n\nexport const MarkdownText: FC<MarkdownTextProps> = ({ text }) => {\n return (\n <div className=\"prose prose-sm max-w-none dark:prose-invert text-current leading-relaxed\">\n <ReactMarkdown>{text}</ReactMarkdown>\n </div>\n )\n}\n","import { type ClassValue, clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { useCallback, useEffect, useState } from 'react'\nimport type { ChatAdapter, Message, SessionSummary } from '../adapters/ChatAdapter'\n\nexport type { Message, SessionSummary }\n\ninterface UseChatSessionReturn {\n conversationId: string | null\n setConversationId: (id: string | null) => void\n messages: Message[]\n setMessages: React.Dispatch<React.SetStateAction<Message[]>>\n isLoadingSession: boolean\n handleNewConversation: () => Promise<void>\n // History management\n sessionsHistory: SessionSummary[]\n isLoadingHistory: boolean\n loadHistory: () => Promise<void>\n loadSession: (conversationId: string) => Promise<void>\n renameSession: (conversationId: string, newTitle: string) => Promise<boolean>\n deleteSession: (conversationId: string) => Promise<boolean>\n}\n\n/**\n * Hook to manage chat session state and persistence\n */\nexport function useChatSession(adapter: ChatAdapter): UseChatSessionReturn {\n const [conversationId, setConversationId] = useState<string | null>(null)\n const [messages, setMessages] = useState<Message[]>([])\n const [isLoadingSession, setIsLoadingSession] = useState(true)\n\n // History state\n const [sessionsHistory, setSessionsHistory] = useState<SessionSummary[]>([])\n const [isLoadingHistory, setIsLoadingHistory] = useState(false)\n\n // Load active session from backend on mount\n useEffect(() => {\n const loadActiveSession = async () => {\n try {\n console.log('[useChatSession] 🔄 Loading active session through adapter...')\n\n const sessionData = await adapter.getActiveSession()\n\n if (sessionData) {\n console.log('[useChatSession] ✅ Active session found:', sessionData.conversationId)\n // Restore conversation state\n setConversationId(sessionData.conversationId)\n // Restore messages\n if (sessionData.messages && sessionData.messages.length > 0) {\n setMessages(sessionData.messages)\n console.log('[useChatSession] ✅ Session restored with', sessionData.messages.length, 'messages')\n }\n } else {\n // No active session found\n console.log('[useChatSession] ℹ️ No active session found (adapter returned null)')\n }\n } catch (error) {\n console.error('[useChatSession] ❌ Error loading session:', error)\n } finally {\n setIsLoadingSession(false)\n }\n }\n\n loadActiveSession()\n }, [adapter])\n\n // Load history\n const loadHistory = useCallback(async () => {\n try {\n setIsLoadingHistory(true)\n const sessions = await adapter.getHistory()\n setSessionsHistory(sessions)\n } catch (error) {\n console.error('[useChatSession] ❌ Error loading history:', error)\n } finally {\n setIsLoadingHistory(false)\n }\n }, [adapter])\n\n // Load a specific session\n const loadSession = useCallback(\n async (id: string) => {\n try {\n setIsLoadingSession(true)\n console.log('[useChatSession] 🔄 Loading session:', id)\n\n const sessionData = await adapter.loadSession(id)\n\n if (sessionData) {\n setConversationId(sessionData.conversationId)\n setMessages(sessionData.messages)\n } else {\n console.error('[useChatSession] ❌ Failed to load session (adapter returned null)')\n }\n } catch (error) {\n console.error('[useChatSession] ❌ Error loading session:', error)\n } finally {\n setIsLoadingSession(false)\n }\n },\n [adapter]\n )\n\n // Rename session\n const renameSession = useCallback(\n async (id: string, newTitle: string) => {\n try {\n const success = await adapter.renameSession(id, newTitle)\n\n if (success) {\n // Update local history\n setSessionsHistory(prev => prev.map(s => (s.conversation_id === id ? { ...s, title: newTitle } : s)))\n return true\n }\n return false\n } catch (error) {\n console.error('[useChatSession] ❌ Error renaming session:', error)\n return false\n }\n },\n [adapter]\n )\n\n // Delete session\n const deleteSession = useCallback(\n async (id: string) => {\n try {\n const success = await adapter.deleteSession(id)\n\n if (success) {\n // Update local history\n setSessionsHistory(prev => prev.filter(s => s.conversation_id !== id))\n // If current session was deleted, clear it\n if (id === conversationId) {\n setConversationId(null)\n setMessages([])\n }\n return true\n }\n return false\n } catch (error) {\n console.error('[useChatSession] ❌ Error deleting session:', error)\n return false\n }\n },\n [conversationId, adapter]\n )\n\n // Clear conversation and start new one\n const handleNewConversation = useCallback(async () => {\n // Just clear local state, don't close the session on backend (backend keeps history)\n setMessages([])\n setConversationId(null)\n console.log('[useChatSession] 🆕 Started new conversation')\n }, [])\n\n return {\n conversationId,\n setConversationId,\n messages,\n setMessages,\n isLoadingSession,\n handleNewConversation,\n sessionsHistory,\n isLoadingHistory,\n loadHistory,\n loadSession,\n renameSession,\n deleteSession\n }\n}\n","'use client'\n\nimport { createContext, type ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react'\nimport type { ChatAdapter, Message, PublicAgentInfo, SessionSummary, TokenUsage } from '../adapters/ChatAdapter'\nimport { NexoPayloadChatAdapter } from '../adapters/NexoPayloadChatAdapter'\nimport { useChatSession } from '../hooks/useChatSession'\n\n/**\n * Resolves collection type metadata for UI rendering.\n * All fields are optional — sensible defaults are applied.\n */\nexport interface CollectionTypeResolver {\n /** Human-readable label (e.g. 'posts' → 'Artículo') */\n label?: (type: string) => string\n /** Icon component (e.g. 'books' → <BookOpen />) */\n icon?: (type: string) => React.ReactNode\n /** URL route segment for link generation (e.g. 'posts' → 'articulos') */\n contentType?: (type: string) => string\n /** Typesense chunk collection name (e.g. 'posts' → 'posts_chunk') */\n chunkCollection?: (type: string) => string\n}\n\nconst defaultCollectionResolver: Required<CollectionTypeResolver> = {\n label: (type: string) => type.charAt(0).toUpperCase() + type.slice(1),\n icon: () => null,\n contentType: (type: string) => type,\n chunkCollection: (type: string) => `${type}_chunk`\n}\n\ninterface ChatContextType {\n adapter: ChatAdapter\n /** Typesense collection names available for document search */\n searchCollections: string[]\n /** Resolved collection type config (with defaults applied) */\n collectionResolver: Required<CollectionTypeResolver>\n isPanelOpen: boolean\n isMaximized: boolean\n openPanel: () => void\n closePanel: () => void\n setMaximized: (value: boolean) => void\n tokenUsage: TokenUsage | null\n isLoadingTokens: boolean\n updateTokenUsage: (newUsage: Partial<TokenUsage>) => void\n // Limit error (when 429 is received)\n limitError: string | null\n setLimitError: (error: string | null) => void\n // Agent management\n agents: PublicAgentInfo[]\n selectedAgent: string | null\n setSelectedAgent: (slug: string) => void\n isLoadingAgents: boolean\n // Session & History (from useChatSession)\n conversationId: string | null\n setConversationId: (id: string | null) => void\n messages: Message[]\n setMessages: React.Dispatch<React.SetStateAction<Message[]>>\n isLoadingSession: boolean\n handleNewConversation: () => Promise<void>\n sessionsHistory: SessionSummary[]\n isLoadingHistory: boolean\n loadHistory: () => Promise<void>\n loadSession: (conversationId: string) => Promise<void>\n renameSession: (conversationId: string, newTitle: string) => Promise<boolean>\n deleteSession: (conversationId: string) => Promise<boolean>\n}\n\nconst ChatContext = createContext<ChatContextType | undefined>(undefined)\n\ninterface ChatProviderProps {\n children: ReactNode\n adapter?: ChatAdapter\n /** Typesense collection names available for document search */\n searchCollections?: string[]\n /** Collection type resolver for UI labels, icons, and URL mapping */\n collectionResolver?: CollectionTypeResolver\n}\n\nexport const ChatProvider = ({\n children,\n adapter: customAdapter,\n searchCollections = [],\n collectionResolver: customResolver\n}: ChatProviderProps) => {\n const [isPanelOpen, setIsPanelOpen] = useState(false)\n const [isMaximized, setIsMaximized] = useState(false)\n\n // Initialize adapter (memoize default to avoid re-creation)\n const adapter = useMemo(() => customAdapter || new NexoPayloadChatAdapter(), [customAdapter])\n\n // Merge custom resolver with defaults\n const collectionResolver = useMemo<Required<CollectionTypeResolver>>(\n () => ({ ...defaultCollectionResolver, ...customResolver }),\n [customResolver]\n )\n\n // Use session hook with adapter\n const chatSession = useChatSession(adapter)\n\n // Token usage management - lazy loaded from SSE events\n const [tokenUsage, setTokenUsage] = useState<TokenUsage | null>(null)\n const [limitError, setLimitError] = useState<string | null>(null)\n const isLoadingTokens = false // No initial fetch needed\n\n // Agent management\n const [agents, setAgents] = useState<PublicAgentInfo[]>([])\n const [selectedAgent, setSelectedAgent] = useState<string | null>(null)\n const [isLoadingAgents, setIsLoadingAgents] = useState(false)\n\n // Load agents on mount\n useEffect(() => {\n const loadAgents = async () => {\n try {\n setIsLoadingAgents(true)\n const loadedAgents = await adapter.getAgents()\n setAgents(loadedAgents)\n if (loadedAgents.length > 0 && !selectedAgent) {\n setSelectedAgent(loadedAgents[0]?.slug || null)\n }\n } catch (error) {\n console.error('[ChatContext] Error loading agents:', error)\n } finally {\n setIsLoadingAgents(false)\n }\n }\n\n loadAgents()\n }, [adapter, selectedAgent])\n\n // Check if device is mobile or tablet (not desktop)\n const isMobileOrTablet = () => {\n if (typeof window === 'undefined') return false\n return window.innerWidth < 1024 // Tailwind lg breakpoint\n }\n\n const openPanel = () => {\n setIsPanelOpen(true)\n // Auto-maximize on mobile and tablet\n if (isMobileOrTablet()) {\n setIsMaximized(true)\n }\n }\n\n const closePanel = () => {\n setIsPanelOpen(false)\n setIsMaximized(false)\n }\n\n const setMaximized = (value: boolean) => setIsMaximized(value)\n\n // Update token usage (called from SSE events)\n // Memoized to prevent infinite loops in useEffect dependencies\n const updateTokenUsage = useCallback((newUsage: Partial<TokenUsage>) => {\n setTokenUsage(prev => {\n if (!prev) {\n // First time: create full object from partial\n return newUsage as TokenUsage\n }\n // Subsequent updates: merge\n return {\n ...prev,\n ...newUsage\n }\n })\n }, [])\n\n // Block body scroll when chat is maximized and open\n useEffect(() => {\n // Restore scroll when not maximized OR not open\n if (!isMaximized || !isPanelOpen) {\n document.body.style.overflow = ''\n document.body.style.position = ''\n document.body.style.top = ''\n document.body.style.width = ''\n return\n }\n\n // Save current scroll position\n const scrollY = window.scrollY\n\n // Prevent body scroll\n document.body.style.overflow = 'hidden'\n document.body.style.position = 'fixed'\n document.body.style.top = `-${scrollY}px`\n document.body.style.width = '100%'\n\n return () => {\n // Restore body scroll\n document.body.style.overflow = ''\n document.body.style.position = ''\n document.body.style.top = ''\n document.body.style.width = ''\n\n // Restore scroll position\n window.scrollTo(0, scrollY)\n }\n }, [isMaximized, isPanelOpen])\n\n return (\n <ChatContext.Provider\n value={{\n adapter,\n searchCollections,\n collectionResolver,\n isPanelOpen,\n isMaximized,\n openPanel,\n closePanel,\n setMaximized,\n tokenUsage,\n isLoadingTokens,\n updateTokenUsage,\n limitError,\n setLimitError,\n agents,\n selectedAgent,\n setSelectedAgent,\n isLoadingAgents,\n ...chatSession // Spread useChatSession return values\n }}\n >\n {children}\n </ChatContext.Provider>\n )\n}\n\nexport const useChat = () => {\n const context = useContext(ChatContext)\n if (!context) {\n throw new Error('useChat must be used within a ChatProvider')\n }\n return context\n}\n","import { useCallback, useState } from 'react'\nimport { useChat } from '../components/chat-context'\n\ninterface ChunkData {\n chunk_text: string\n title: string\n slug: string\n chunk_index: number\n collection: string\n}\n\ninterface ChunkCache {\n [key: string]: {\n content: string\n isLoading: boolean\n error: string | null\n }\n}\n\n/**\n * Hook to lazy-load chunk content from the API\n */\nexport function useChunkLoader() {\n const [chunkCache, setChunkCache] = useState<ChunkCache>({})\n const { collectionResolver } = useChat()\n\n /**\n * Load chunk content from the API\n */\n const loadChunkContent = useCallback(\n async (chunkId: string, type: string): Promise<string> => {\n const cacheKey = `${type}_${chunkId}`\n\n // Return cached content if available\n if (chunkCache[cacheKey]?.content) {\n return chunkCache[cacheKey].content\n }\n\n // Return empty if already loading\n if (chunkCache[cacheKey]?.isLoading) {\n return ''\n }\n\n // Mark as loading\n setChunkCache(prev => ({\n ...prev,\n [cacheKey]: {\n content: '',\n isLoading: true,\n error: null\n }\n }))\n\n try {\n const collectionName = collectionResolver.chunkCollection(type)\n const url = `/api/chat/chunks/${encodeURIComponent(chunkId)}?collection=${encodeURIComponent(collectionName)}`\n\n const response = await fetch(url)\n\n if (!response.ok) {\n const errorData = await response.json()\n throw new Error(errorData.error || 'Error al cargar el chunk')\n }\n\n const data: ChunkData = await response.json()\n\n // Update cache with loaded content\n setChunkCache(prev => ({\n ...prev,\n [cacheKey]: {\n content: data.chunk_text,\n isLoading: false,\n error: null\n }\n }))\n\n return data.chunk_text\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Error desconocido al cargar el chunk'\n\n // Update cache with error\n setChunkCache(prev => ({\n ...prev,\n [cacheKey]: {\n content: '',\n isLoading: false,\n error: errorMessage\n }\n }))\n\n console.error('[useChunkLoader] Error loading chunk:', error)\n return ''\n }\n },\n [chunkCache, collectionResolver]\n )\n\n /**\n * Get the current state of a chunk\n */\n const getChunkState = useCallback(\n (chunkId: string, type: string) => {\n const cacheKey = `${type}_${chunkId}`\n return (\n chunkCache[cacheKey] || {\n content: '',\n isLoading: false,\n error: null\n }\n )\n },\n [chunkCache]\n )\n\n return {\n loadChunkContent,\n getChunkState\n }\n}\n","import type React from 'react'\n\n/**\n * Props for a Link component that can be injected\n * Compatible with next/link and regular <a> tags\n */\nexport interface LinkComponentProps {\n href: string\n children: React.ReactNode\n onClick?: () => void\n className?: string\n target?: string\n 'aria-label'?: string\n title?: string\n}\n\n/**\n * Type for a Link component that can be injected from outside\n * Default fallback is a regular <a> tag\n */\nexport type LinkComponent = React.ComponentType<LinkComponentProps>\n\n/**\n * Props for an Image component that can be injected\n * Compatible with next/image and regular <img> tags\n */\nexport interface ImageComponentProps {\n src: string\n alt: string\n width?: number\n height?: number\n className?: string\n}\n\n/**\n * Type for an Image component that can be injected from outside\n * Default fallback is a regular <img> tag\n */\nexport type ImageComponent = React.ComponentType<ImageComponentProps>\n\n/**\n * Default Link component fallback (regular <a> tag)\n */\nexport const DefaultLink: LinkComponent = ({\n href,\n children,\n onClick,\n className,\n target,\n 'aria-label': ariaLabel,\n title\n}) => (\n <a href={href} onClick={onClick} className={className} target={target} aria-label={ariaLabel} title={title}>\n {children}\n </a>\n)\n\n/**\n * Default Image component fallback (regular <img> tag)\n */\nexport const DefaultImage: ImageComponent = ({ src, alt, width, height, className }) => (\n <img src={src} alt={alt} width={width} height={height} className={className} />\n)\n","'use client'\n\nimport { ArrowRight } from 'lucide-react'\nimport { DefaultLink, type LinkComponent } from '../../types/components'\n\ninterface ViewMoreLinkProps {\n contentType: string\n slug: string\n title: string\n onClick?: () => void\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n}\n\nexport const ViewMoreLink = ({\n contentType,\n slug,\n title,\n onClick,\n generateHref,\n LinkComponent: Link = DefaultLink\n}: ViewMoreLinkProps) => {\n const href = generateHref({\n type: contentType,\n value: { id: parseInt(slug.split('-')?.[0] || '0', 10), slug }\n })\n if (!href) return null\n\n return (\n <Link\n href={href}\n onClick={onClick}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:text-primary/80 transition-colors mt-2\"\n title={`Ver más sobre ${title}`}\n >\n Ver documento completo\n <ArrowRight className=\"w-4 h-4\" />\n </Link>\n )\n}\n","'use client'\n\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { ChevronDown, FileText, List, Loader2, X } from 'lucide-react'\nimport React, { useState } from 'react'\nimport type { Source } from '../adapters/ChatAdapter'\nimport { useChunkLoader } from '../hooks/useChunkLoader'\nimport { cn } from '../lib/utils'\nimport type { LinkComponent } from '../types/components'\nimport { MarkdownText } from './assistant-ui/markdown-text'\nimport { ViewMoreLink } from './buttons/ViewMoreLink'\nimport { useChat } from './chat-context'\n\ninterface SourcesListProps {\n sources: Source[]\n isMaximized?: boolean\n onMinimize?: () => void\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n renderSourceIcon?: (type: string) => React.ReactNode\n renderViewMore?: (props: { type: string; slug: string; title: string; onClick?: () => void }) => React.ReactNode\n}\n\n// Animation variants\nconst listVariants = {\n hidden: { opacity: 0, height: 0, transition: { duration: 0.2, ease: 'easeInOut' as const } },\n visible: { opacity: 1, height: 'auto', transition: { duration: 0.3, ease: 'easeOut' as const } }\n}\n\nconst expandedCardVariants = {\n hidden: { opacity: 0, scale: 0.95, y: -10, transition: { duration: 0.2, ease: 'easeInOut' as const } },\n visible: { opacity: 1, scale: 1, y: 0, transition: { duration: 0.3, ease: [0.4, 0, 0.2, 1] as const } },\n exit: { opacity: 0, scale: 0.95, y: -10, transition: { duration: 0.2, ease: 'easeInOut' as const } }\n}\n\nconst contentVariants = {\n hidden: { opacity: 0, y: 10 },\n visible: { opacity: 1, y: 0, transition: { delay: 0.1, duration: 0.3, ease: 'easeOut' as const } }\n}\n\nconst itemVariants = {\n hidden: { opacity: 0, x: -10 },\n visible: (i: number) => ({\n opacity: 1,\n x: 0,\n transition: {\n delay: i * 0.05,\n duration: 0.2,\n ease: 'easeOut' as const\n }\n })\n}\n\n// Helper to parse chunk content\nconst parseChunkContent = (content: string) => {\n const separator = '.________________________________________.'\n if (!content || !content.includes(separator)) {\n return { text: content, metadata: null }\n }\n\n const [text = '', metadataRaw] = content.split(separator)\n const metadata: { section?: string; path?: string } = {}\n\n if (metadataRaw) {\n const parts = metadataRaw.split('|')\n parts.forEach(part => {\n const trimmed = part.trim()\n if (trimmed.toLowerCase().startsWith('section:')) {\n metadata.section = trimmed.substring('section:'.length).trim()\n } else if (trimmed.toLowerCase().startsWith('path:')) {\n metadata.path = trimmed.substring('path:'.length).trim()\n }\n })\n }\n\n return { text: text.trim(), metadata }\n}\n\n// Relevance bar component\nconst RelevanceBar: React.FC<{ score: number }> = ({ score }) => {\n const percentage = Math.min(Math.max(score * 100, 0), 100)\n const getColor = () => {\n if (percentage >= 80) return 'bg-green-500'\n if (percentage >= 60) return 'bg-primary'\n if (percentage >= 40) return 'bg-yellow-500'\n return 'bg-muted-foreground'\n }\n\n return (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-1 w-12 rounded-full bg-secondary overflow-hidden\">\n <motion.div\n className={cn('h-full rounded-full', getColor())}\n initial={{ width: 0 }}\n animate={{ width: `${percentage}%` }}\n transition={{ duration: 0.5, ease: 'easeOut' }}\n />\n </div>\n <span className=\"text-[10px] text-muted-foreground\">{Math.round(percentage)}%</span>\n </div>\n )\n}\n\n// Fallback icon when collectionResolver.icon returns null\nconst fallbackIcon = () => <FileText className=\"w-full h-full\" />\n\n// Breadcrumb path segments component\nconst BreadcrumbPath: React.FC<{ path: string }> = ({ path }) => (\n <>\n {path.split('>').map((segment, index, arr) => {\n const text = segment.trim()\n const truncated = text.length > 25 ? `${text.slice(0, 25)}...` : text\n return (\n // biome-ignore lint/suspicious/noArrayIndexKey: breadcrumb segments can repeat\n <React.Fragment key={`${text}-${index}`}>\n <motion.span\n className=\"inline-flex items-center rounded-full bg-primary/10 border border-primary/20 px-2.5 py-0.5 text-xs font-medium text-foreground max-w-[150px]\"\n initial={{ opacity: 0, scale: 0.9 }}\n animate={{ opacity: 1, scale: 1 }}\n transition={{ delay: index * 0.05 }}\n title={text}\n >\n <span className=\"truncate\">{truncated}</span>\n </motion.span>\n {index < arr.length - 1 && <span className=\"text-muted-foreground text-xs\">/</span>}\n </React.Fragment>\n )\n })}\n </>\n)\n\n// Section pill component\nconst SectionPill: React.FC<{ section: string }> = ({ section }) => (\n <span\n className=\"inline-flex items-center rounded-full bg-primary/10 border border-primary/20 px-2.5 py-0.5 text-xs font-medium text-foreground max-w-[150px]\"\n title={section}\n >\n <span className=\"truncate\">{section.length > 25 ? `${section.slice(0, 25)}...` : section}</span>\n </span>\n)\n\n// Metadata pills component\nconst MetadataPills: React.FC<{ metadata: { section?: string; path?: string } | null }> = ({ metadata }) => {\n if (!metadata || (!metadata.path && !metadata.section)) return null\n return (\n <div className=\"mt-4 flex flex-wrap items-center gap-2 mb-3\">\n <span className=\"text-xs text-muted-foreground\">Ubicacion:</span>\n {metadata.path ? (\n <BreadcrumbPath path={metadata.path} />\n ) : metadata.section ? (\n <SectionPill section={metadata.section} />\n ) : null}\n </div>\n )\n}\n\n// Expanded content body component\nconst ExpandedContentBody: React.FC<{\n isLoading: boolean\n error: string | null\n displayContent: string\n cleanContent: string\n metadata: { section?: string; path?: string } | null\n expandedSource: Source\n getContentType: (type: string) => string\n renderViewMore?: SourcesListProps['renderViewMore']\n handleViewMore: () => void\n generateHref: SourcesListProps['generateHref']\n LinkComponent?: LinkComponent\n}> = ({\n isLoading,\n error,\n displayContent,\n cleanContent,\n metadata,\n expandedSource,\n getContentType,\n renderViewMore,\n handleViewMore,\n generateHref,\n LinkComponent\n}) => {\n if (isLoading) {\n return (\n <div className=\"flex items-center justify-center gap-2 py-4 text-sm text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n Cargando contenido...\n </div>\n )\n }\n if (error) {\n return <div className=\"text-sm text-destructive py-2\">Error: {error}</div>\n }\n if (!displayContent) {\n return <div className=\"text-sm text-muted-foreground py-2\">No hay contenido disponible para este fragmento</div>\n }\n return (\n <>\n <MarkdownText text={cleanContent} />\n <MetadataPills metadata={metadata} />\n {renderViewMore ? (\n renderViewMore({\n type: expandedSource.type,\n slug: expandedSource.slug,\n title: expandedSource.title,\n onClick: handleViewMore\n })\n ) : (\n <ViewMoreLink\n contentType={getContentType(expandedSource.type)}\n slug={expandedSource.slug}\n title={expandedSource.title}\n onClick={handleViewMore}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n />\n )}\n </>\n )\n}\n\n// Expanded source card component\nconst ExpandedSourceCard: React.FC<{\n expandedSource: Source\n expandedSourceId: string\n loadedContent: string\n getChunkState: ReturnType<typeof useChunkLoader>['getChunkState']\n getIcon: (type: string) => React.ReactNode\n getLabel: (type: string) => string\n getContentType: (type: string) => string\n onClose: () => void\n handleViewMore: () => void\n renderViewMore?: SourcesListProps['renderViewMore']\n generateHref: SourcesListProps['generateHref']\n LinkComponent?: LinkComponent\n}> = ({\n expandedSource,\n expandedSourceId,\n loadedContent,\n getChunkState,\n getIcon,\n getLabel,\n getContentType,\n onClose,\n handleViewMore,\n renderViewMore,\n generateHref,\n LinkComponent\n}) => {\n const chunkState = getChunkState(expandedSource.id, expandedSource.type)\n const displayContent = loadedContent || expandedSource.content\n const { text: cleanContent, metadata } = parseChunkContent(displayContent)\n\n return (\n <div className=\"mt-3 pt-3 border-t border-border\">\n <AnimatePresence mode=\"wait\">\n <motion.div\n key={expandedSourceId}\n variants={expandedCardVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n layoutId={`source-${expandedSourceId}`}\n >\n <div className=\"p-4 bg-muted rounded-lg border border-border shadow-sm\">\n <div className=\"flex items-start justify-between mb-3\">\n <div className=\"flex items-start gap-2 flex-1\">\n <motion.div\n className=\"flex-shrink-0 w-5 h-5 text-foreground mt-0.5\"\n initial={{ rotate: -10 }}\n animate={{ rotate: 0 }}\n transition={{ type: 'spring', stiffness: 300 }}\n >\n {getIcon(expandedSource.type)}\n </motion.div>\n <div>\n <div className=\"text-foreground font-semibold text-sm\">{expandedSource.title}</div>\n <div className=\"text-muted-foreground text-xs mt-1 flex items-center gap-2\">\n <span>\n {getLabel(expandedSource.type)}\n {expandedSource.chunkIndex !== undefined && <> - Parte {expandedSource.chunkIndex + 1}</>}\n </span>\n {expandedSource.relevanceScore && <RelevanceBar score={expandedSource.relevanceScore} />}\n </div>\n </div>\n </div>\n <motion.button\n onClick={onClose}\n className=\"flex-shrink-0 ml-2 h-8 w-8 inline-flex items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors\"\n aria-label=\"Cerrar\"\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n <X className=\"w-4 h-4\" />\n </motion.button>\n </div>\n\n <motion.div variants={contentVariants} initial=\"hidden\" animate=\"visible\">\n <ExpandedContentBody\n isLoading={chunkState.isLoading}\n error={chunkState.error}\n displayContent={displayContent}\n cleanContent={cleanContent}\n metadata={metadata}\n expandedSource={expandedSource}\n getContentType={getContentType}\n renderViewMore={renderViewMore}\n handleViewMore={handleViewMore}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n />\n </motion.div>\n </div>\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\n// Source list item component\nconst SourceListItem: React.FC<{\n source: Source\n idx: number\n getIcon: (type: string) => React.ReactNode\n getLabel: (type: string) => string\n onClick: (id: string) => void\n}> = ({ source, idx, getIcon, getLabel, onClick }) => (\n <motion.button\n key={source.id || idx}\n custom={idx}\n variants={itemVariants}\n initial=\"hidden\"\n animate=\"visible\"\n onClick={() => onClick(source.id)}\n className=\"w-full text-left text-xs rounded-lg p-3 transition-all border border-transparent hover:border-primary/20 hover:bg-muted/50 group\"\n whileHover={{ x: 4 }}\n layoutId={`source-${source.id}`}\n >\n <div className=\"flex items-start gap-2\">\n <motion.div className=\"flex-shrink-0 w-4 h-4 text-foreground mt-0.5\" whileHover={{ scale: 1.1, rotate: 5 }}>\n {getIcon(source.type)}\n </motion.div>\n\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-foreground font-medium truncate group-hover:text-primary transition-colors\">\n {source.title}\n </div>\n\n <div className=\"text-muted-foreground mt-0.5 flex items-center gap-2 flex-wrap\">\n <span className=\"text-xs\">{getLabel(source.type)}</span>\n {source.chunkIndex !== undefined && (\n <>\n <span>-</span>\n <span className=\"text-xs\">Parte {source.chunkIndex + 1}</span>\n </>\n )}\n {source.relevanceScore && <RelevanceBar score={source.relevanceScore} />}\n </div>\n\n {source.excerpt && (\n <div className=\"text-muted-foreground mt-1 text-xs line-clamp-2 italic\">\n <MarkdownText text={`\"${source.excerpt}\"`} />\n </div>\n )}\n </div>\n\n <motion.span\n className=\"text-primary flex-shrink-0 text-xs opacity-0 group-hover:opacity-100 transition-opacity\"\n initial={{ x: -5 }}\n whileHover={{ x: 0 }}\n >\n Ver mas\n </motion.span>\n </div>\n </motion.button>\n)\n\nexport const SourcesList: React.FC<SourcesListProps> = ({\n sources,\n isMaximized = false,\n onMinimize,\n generateHref,\n LinkComponent,\n renderSourceIcon,\n renderViewMore\n}) => {\n const [isExpanded, setIsExpanded] = useState(false)\n const [expandedSourceId, setExpandedSourceId] = useState<string | null>(null)\n const [loadedContent, setLoadedContent] = useState<string>('')\n\n const { loadChunkContent, getChunkState } = useChunkLoader()\n const { collectionResolver } = useChat()\n\n const handleViewMore = () => {\n if (isMaximized && onMinimize) {\n onMinimize()\n }\n }\n\n if (!sources || sources.length === 0) {\n return null\n }\n\n const getIcon = (type: string) => {\n if (renderSourceIcon) return renderSourceIcon(type)\n return collectionResolver.icon(type) || fallbackIcon()\n }\n\n const getLabel = collectionResolver.label\n const getContentType = collectionResolver.contentType\n\n const handleSourceClick = async (sourceId: string) => {\n setExpandedSourceId(sourceId)\n setLoadedContent('')\n\n const source = sources.find(s => s.id === sourceId)\n if (!source) return\n\n if (source.content) {\n setLoadedContent(source.content)\n return\n }\n\n const content = await loadChunkContent(sourceId, source.type)\n setLoadedContent(content)\n }\n\n const handleCloseExpanded = () => {\n setExpandedSourceId(null)\n setLoadedContent('')\n }\n\n // If a source is expanded, show only that one\n if (expandedSourceId) {\n const expandedSource = sources.find(s => s.id === expandedSourceId)\n if (!expandedSource) return null\n\n return (\n <ExpandedSourceCard\n expandedSource={expandedSource}\n expandedSourceId={expandedSourceId}\n loadedContent={loadedContent}\n getChunkState={getChunkState}\n getIcon={getIcon}\n getLabel={getLabel}\n getContentType={getContentType}\n onClose={handleCloseExpanded}\n handleViewMore={handleViewMore}\n renderViewMore={renderViewMore}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n />\n )\n }\n\n // Show collapsed list\n return (\n <div className=\"mt-3 pt-3 border-t border-border\">\n {/* Header - clickable to expand/collapse */}\n <motion.button\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"w-full text-left flex items-center justify-between hover:opacity-75 transition-opacity group\"\n whileTap={{ scale: 0.98 }}\n >\n <div className=\"flex items-center gap-2\">\n <List className=\"w-4 h-4 text-foreground\" />\n <p className=\"text-xs font-semibold text-foreground\">Fuentes consultadas</p>\n <motion.span\n className=\"inline-flex items-center justify-center h-5 w-5 rounded-full bg-primary/10 text-primary text-xs font-medium\"\n initial={{ scale: 1 }}\n animate={{ scale: [1, 1.1, 1] }}\n transition={{ duration: 0.3 }}\n key={sources.length}\n >\n {sources.length}\n </motion.span>\n </div>\n <motion.div animate={{ rotate: isExpanded ? 0 : -90 }} transition={{ duration: 0.2 }}>\n <ChevronDown className=\"w-4 h-4 text-muted-foreground\" />\n </motion.div>\n </motion.button>\n\n {/* Sources list - shown when expanded */}\n <AnimatePresence>\n {isExpanded && (\n <motion.div\n variants={listVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"hidden\"\n className=\"mt-2 space-y-2 overflow-hidden\"\n >\n {sources.map((source, idx) => (\n <SourceListItem\n key={source.id || idx}\n source={source}\n idx={idx}\n getIcon={getIcon}\n getLabel={getLabel}\n onClick={handleSourceClick}\n />\n ))}\n </motion.div>\n )}\n </AnimatePresence>\n </div>\n )\n}\n","import {\n AssistantRuntimeProvider,\n ComposerPrimitive,\n MessagePrimitive,\n ThreadPrimitive,\n useMessageRuntime,\n useThreadRuntime\n} from '@assistant-ui/react'\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { AlertTriangle, ArrowRight, ArrowUpIcon, Sparkles, SquareIcon, X } from 'lucide-react'\nimport type { FC } from 'react'\nimport { createContext, useContext, useEffect, useState } from 'react'\nimport type { Source } from '../../adapters/ChatAdapter'\nimport { cn } from '../../lib/utils'\nimport type { LinkComponent } from '../../types/components'\nimport { useChat } from '../chat-context'\nimport { SourcesList } from '../SourcesList'\nimport { MarkdownText } from './markdown-text'\n\ninterface ThreadContextValue {\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n agentName?: string\n}\n\nconst ThreadContext = createContext<ThreadContextValue | null>(null)\n\ninterface ThreadProps {\n runtime: ReturnType<typeof import('@assistant-ui/react').useExternalStoreRuntime>\n welcomeTitle?: string\n welcomeSubtitle?: string\n suggestedQuestions?: Array<{\n prompt: string\n title: string\n description: string\n }>\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n agentName?: string\n}\n\n/**\n * Main Thread component styled for Oraculo de Escohotado\n */\nexport const Thread: FC<ThreadProps> = ({\n runtime,\n welcomeTitle,\n welcomeSubtitle,\n suggestedQuestions,\n generateHref,\n LinkComponent,\n agentName\n}) => {\n return (\n <AssistantRuntimeProvider runtime={runtime}>\n <ThreadContext.Provider value={{ generateHref, LinkComponent, agentName }}>\n <ThreadPrimitive.Root className=\"flex h-full flex-col bg-background\">\n {/* Limit Error Alert */}\n <LimitAlert />\n\n <ThreadPrimitive.Viewport className=\"flex-1 overflow-y-auto px-4 pt-4\">\n <ThreadPrimitive.Empty>\n {welcomeTitle && welcomeSubtitle && (\n <ThreadWelcome\n title={welcomeTitle}\n subtitle={welcomeSubtitle}\n suggestedQuestions={suggestedQuestions}\n />\n )}\n </ThreadPrimitive.Empty>\n\n <ThreadPrimitive.Messages\n components={{\n UserMessage,\n AssistantMessage\n }}\n />\n\n <TypingIndicator />\n </ThreadPrimitive.Viewport>\n\n <div className=\"sticky bottom-0 border-t border-border bg-gradient-to-t from-background via-background to-background/80 backdrop-blur-sm\">\n {/* Token Usage Bar */}\n <TokenUsageBar />\n <div className=\"p-4 pt-2\">\n <Composer />\n </div>\n </div>\n </ThreadPrimitive.Root>\n </ThreadContext.Provider>\n </AssistantRuntimeProvider>\n )\n}\n\ninterface ThreadWelcomeProps {\n title: string\n subtitle: string\n suggestedQuestions?: Array<{\n prompt: string\n title: string\n description: string\n }>\n}\n\nconst containerVariants = {\n hidden: { opacity: 0 },\n visible: {\n opacity: 1,\n transition: {\n staggerChildren: 0.1,\n delayChildren: 0.1\n }\n }\n} as const\n\nconst itemVariants = {\n hidden: { opacity: 0, y: 20 },\n visible: {\n opacity: 1,\n y: 0,\n transition: {\n type: 'spring' as const,\n stiffness: 300,\n damping: 24\n }\n }\n}\n\nconst ThreadWelcome: FC<ThreadWelcomeProps> = ({ title, subtitle, suggestedQuestions }) => {\n if (!title) return null\n\n return (\n <motion.div\n className=\"flex h-full flex-col items-center justify-center p-4 max-w-2xl mx-auto\"\n variants={containerVariants}\n initial=\"hidden\"\n animate=\"visible\"\n >\n <motion.div className=\"text-center mb-8 space-y-3\" variants={itemVariants}>\n <motion.div\n className=\"inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 mb-4\"\n animate={{\n rotate: [0, 5, -5, 0],\n scale: [1, 1.05, 1]\n }}\n transition={{\n duration: 4,\n repeat: Infinity,\n ease: 'easeInOut'\n }}\n >\n <Sparkles className=\"w-8 h-8 text-primary\" />\n </motion.div>\n <h1 className=\"text-3xl font-semibold tracking-tight text-foreground\">{title}</h1>\n <p className=\"text-lg text-muted-foreground\">{subtitle}</p>\n </motion.div>\n\n {suggestedQuestions && suggestedQuestions.length > 0 && (\n <motion.div className=\"grid grid-cols-1 md:grid-cols-2 gap-3 w-full\" variants={containerVariants}>\n {suggestedQuestions.map(question => (\n <SuggestionCard\n key={question.prompt}\n prompt={question.prompt}\n title={question.title}\n description={question.description}\n />\n ))}\n </motion.div>\n )}\n </motion.div>\n )\n}\n\ninterface SuggestionCardProps {\n prompt: string\n title: string\n description: string\n}\n\nconst SuggestionCard: FC<SuggestionCardProps> = ({ prompt, title, description }) => {\n return (\n <motion.div variants={itemVariants}>\n <ThreadPrimitive.Suggestion\n prompt={prompt}\n className=\"group relative flex flex-col items-start gap-1 rounded-xl border border-border bg-card p-4 text-start cursor-pointer overflow-hidden transition-colors duration-200 hover:border-primary/30\"\n >\n <motion.div\n className=\"absolute inset-0 bg-gradient-to-r from-primary/5 to-transparent\"\n initial={{ x: '-100%' }}\n whileHover={{ x: 0 }}\n transition={{ duration: 0.3 }}\n />\n <div className=\"relative flex items-center justify-between w-full\">\n <span className=\"font-medium text-sm text-foreground\">{title}</span>\n <motion.div initial={{ opacity: 0, x: -10 }} whileHover={{ opacity: 1, x: 0 }} className=\"text-primary\">\n <ArrowRight className=\"w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity\" />\n </motion.div>\n </div>\n <span className=\"relative text-xs text-muted-foreground\">{description}</span>\n </ThreadPrimitive.Suggestion>\n </motion.div>\n )\n}\n\n/**\n * Alert shown when token limit is exceeded\n */\nconst LimitAlert: FC = () => {\n const { limitError, setLimitError, tokenUsage } = useChat()\n\n if (!limitError) return null\n\n const resetTime = tokenUsage?.reset_at\n ? new Date(tokenUsage.reset_at).toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' })\n : null\n\n return (\n <motion.div\n className=\"mx-4 mt-4 rounded-lg border border-destructive/30 bg-destructive/10 p-4\"\n initial={{ opacity: 0, y: -10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10 }}\n >\n <div className=\"flex items-start gap-3\">\n <AlertTriangle className=\"h-5 w-5 text-destructive shrink-0 mt-0.5\" />\n <div className=\"flex-1 space-y-1\">\n <p className=\"text-sm font-medium text-destructive\">{limitError}</p>\n {resetTime && <p className=\"text-xs text-muted-foreground\">Tu límite se restablecerá a las {resetTime}</p>}\n </div>\n <button\n type=\"button\"\n onClick={() => setLimitError(null)}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n </motion.div>\n )\n}\n\n/**\n * Token usage progress bar\n */\nconst TokenUsageBar: FC = () => {\n const { tokenUsage } = useChat()\n\n if (!tokenUsage) return null\n\n const percentage = Math.min(tokenUsage.percentage, 100)\n const isWarning = percentage > 50\n const isCritical = percentage > 80\n\n const getGradientClass = () => {\n if (isCritical) return 'from-red-500 to-red-600'\n if (isWarning) return 'from-yellow-500 to-orange-500'\n return 'from-primary to-primary/80'\n }\n\n return (\n <div className=\"px-4 py-2 border-b border-border/50\">\n <div className=\"flex items-center justify-between text-xs text-muted-foreground mb-1\">\n <span>\n {tokenUsage.used.toLocaleString()} / {tokenUsage.limit.toLocaleString()} tokens\n </span>\n <span>{percentage.toFixed(0)}%</span>\n </div>\n <div className=\"h-1.5 w-full rounded-full bg-secondary overflow-hidden\">\n <motion.div\n className={cn('h-full rounded-full bg-gradient-to-r', getGradientClass())}\n initial={{ width: 0 }}\n animate={{ width: `${percentage}%` }}\n transition={{ duration: 0.5, ease: 'easeOut' }}\n />\n </div>\n </div>\n )\n}\n\nconst TypingIndicator: FC = () => {\n const threadRuntime = useThreadRuntime()\n const [isRunning, setIsRunning] = useState(false)\n const context = useContext(ThreadContext)\n\n useEffect(() => {\n // Subscribe to thread state changes\n const unsubscribe = threadRuntime.subscribe(() => {\n const state = threadRuntime.getState()\n setIsRunning(state.isRunning)\n })\n\n // Check initial state\n setIsRunning(threadRuntime.getState().isRunning)\n\n return unsubscribe\n }, [threadRuntime])\n\n const agentName = context?.agentName || 'El asistente'\n\n return (\n <AnimatePresence>\n {isRunning && (\n <motion.div\n className=\"flex justify-start py-4 w-full\"\n initial={{ opacity: 0, y: 10 }}\n animate={{ opacity: 1, y: 0 }}\n exit={{ opacity: 0, y: -10 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"max-w-[85%] rounded-2xl rounded-bl-md border-l-4 border-l-primary/30 bg-card/80 backdrop-blur-sm px-5 py-4 text-card-foreground shadow-sm\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-1\">\n <span className=\"typing-dot w-2 h-2 rounded-full bg-primary/60\" />\n <span className=\"typing-dot w-2 h-2 rounded-full bg-primary/60\" />\n <span className=\"typing-dot w-2 h-2 rounded-full bg-primary/60\" />\n </div>\n <span className=\"text-sm text-muted-foreground\">{agentName} está pensando...</span>\n </div>\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n )\n}\n\nconst Composer: FC = () => {\n return (\n <ComposerPrimitive.Root className=\"flex w-full items-end max-w-4xl mx-auto\">\n <motion.div\n className=\"relative flex w-full flex-col rounded-3xl border border-input bg-background/50 px-4 py-2 shadow-sm transition-all focus-within:ring-2 focus-within:ring-primary/20 focus-within:border-primary/50\"\n style={\n {\n '--tw-shadow': 'var(--shadow-glow-primary)'\n } as React.CSSProperties\n }\n whileFocus={{ scale: 1.01 }}\n >\n <ComposerPrimitive.Input\n placeholder=\"Escribe tu pregunta...\"\n className=\"w-full resize-none border-none bg-transparent px-2 py-3 text-base text-foreground outline-none placeholder:text-muted-foreground focus:ring-0 max-h-[200px] min-h-[52px] pr-12\"\n rows={1}\n autoFocus\n />\n\n <div className=\"absolute bottom-3 right-3 flex items-center justify-center p-1\">\n <ComposerPrimitive.Send asChild>\n <motion.button\n type=\"submit\"\n className=\"inline-flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-md shadow-primary/25 transition-all disabled:opacity-50 disabled:shadow-none\"\n aria-label=\"Enviar mensaje\"\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n <motion.div\n animate={{ y: [0, -2, 0] }}\n transition={{ duration: 1.5, repeat: Infinity, ease: 'easeInOut' }}\n >\n <ArrowUpIcon className=\"h-5 w-5\" />\n </motion.div>\n </motion.button>\n </ComposerPrimitive.Send>\n\n <ComposerPrimitive.Cancel asChild>\n <motion.button\n type=\"button\"\n className=\"hidden data-[running=true]:inline-flex h-9 w-9 items-center justify-center rounded-full bg-destructive text-destructive-foreground shadow-sm transition-all\"\n aria-label=\"Cancelar\"\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n <SquareIcon className=\"h-3 w-3 fill-current\" />\n </motion.button>\n </ComposerPrimitive.Cancel>\n </div>\n </motion.div>\n </ComposerPrimitive.Root>\n )\n}\n\nconst UserMessage: FC = () => {\n return (\n <MessagePrimitive.Root className=\"flex justify-end py-2 w-full\">\n <motion.div\n className=\"max-w-[80%] rounded-2xl rounded-br-md bg-primary px-4 py-2.5 text-primary-foreground shadow-md shadow-primary/20\"\n initial={{ opacity: 0, scale: 0.95, y: 10 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n transition={{\n type: 'spring',\n stiffness: 400,\n damping: 25\n }}\n >\n <MessagePrimitive.Content\n components={{\n Text: ({ text }) => <span className=\"whitespace-pre-wrap font-medium\">{text}</span>\n }}\n />\n </motion.div>\n </MessagePrimitive.Root>\n )\n}\n\nconst AssistantMessage: FC = () => {\n const messageRuntime = useMessageRuntime()\n const context = useContext(ThreadContext)\n\n // Extract sources from metadata with type-safe access\n const messageState = messageRuntime.getState()\n const metadata = messageState.metadata as { custom?: { sources?: Source[] } } | undefined\n const sources = metadata?.custom?.sources\n\n return (\n <MessagePrimitive.Root className=\"flex justify-start py-4 w-full\">\n <motion.div\n className=\"max-w-[85%] rounded-2xl rounded-bl-md border-l-4 border-l-primary/30 bg-card/80 backdrop-blur-sm px-5 py-4 text-card-foreground shadow-sm\"\n initial={{ opacity: 0, scale: 0.95, y: 10 }}\n animate={{ opacity: 1, scale: 1, y: 0 }}\n transition={{\n type: 'spring',\n stiffness: 400,\n damping: 25\n }}\n >\n <MessagePrimitive.Content\n components={{\n Text: MarkdownText\n }}\n />\n\n {sources && sources.length > 0 && context && (\n <SourcesList sources={sources} generateHref={context.generateHref} LinkComponent={context.LinkComponent} />\n )}\n </motion.div>\n </MessagePrimitive.Root>\n )\n}\n\nexport { AssistantMessage, Composer, ThreadWelcome, TypingIndicator, UserMessage }\n","'use client'\n\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { MessageCircle } from 'lucide-react'\nimport { cn } from '../../lib/utils'\nimport { DefaultImage, type ImageComponent as ImageComponentType } from '../../types/components'\n\ninterface FloatingChatButtonProps {\n onOpen: () => void\n aiIcon?: string\n isOpen?: boolean\n className?: string\n ImageComponent?: ImageComponentType\n}\n\nexport const FloatingChatButton = ({\n onOpen,\n aiIcon,\n isOpen = false,\n className,\n ImageComponent = DefaultImage\n}: FloatingChatButtonProps) => {\n return (\n <AnimatePresence>\n {!isOpen && (\n <motion.button\n onClick={onOpen}\n className={cn(\n 'fixed bottom-6 left-6 z-50 h-[60px] w-[60px] rounded-full shadow-lg focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 overflow-hidden animate-pulse-glow',\n !aiIcon && 'bg-primary text-primary-foreground flex items-center justify-center',\n className\n )}\n initial={{ scale: 0, rotate: -180, opacity: 0 }}\n animate={{\n scale: 1,\n rotate: 0,\n opacity: 1\n }}\n exit={{\n scale: 0,\n rotate: 180,\n opacity: 0,\n transition: { duration: 0.3, ease: 'easeInOut' }\n }}\n transition={{\n type: 'spring' as const,\n stiffness: 260,\n damping: 20\n }}\n whileHover={{\n scale: 1.08,\n boxShadow: 'var(--shadow-glow-lg)'\n }}\n whileTap={{ scale: 0.95 }}\n aria-label=\"Abrir chat\"\n >\n {aiIcon ? (\n <motion.div\n className=\"h-full w-full rounded-full p-[3px]\"\n style={{ background: 'var(--chat-border-color)' }}\n animate={{\n opacity: [0.85, 1, 0.85]\n }}\n transition={{\n duration: 2,\n repeat: Infinity,\n ease: 'easeInOut'\n }}\n >\n <div className=\"h-full w-full rounded-full overflow-hidden bg-background\">\n <ImageComponent\n src={aiIcon}\n alt=\"Chat Avatar\"\n className=\"h-full w-full object-cover\"\n width={60}\n height={60}\n />\n </div>\n </motion.div>\n ) : (\n <motion.div\n animate={{\n scale: [1, 1.1, 1]\n }}\n transition={{\n duration: 2,\n repeat: Infinity,\n ease: 'easeInOut'\n }}\n >\n <MessageCircle className=\"w-8 h-8\" />\n </motion.div>\n )}\n </motion.button>\n )}\n </AnimatePresence>\n )\n}\n\nexport default FloatingChatButton\n","'use client'\n\nimport { type AppendMessage, type ThreadMessage, useExternalStoreRuntime } from '@assistant-ui/react'\nimport { useCallback, useMemo, useState } from 'react'\nimport type { Message, Source } from '../adapters/ChatAdapter'\nimport { useChat } from '../components/chat-context'\nimport type { Document } from '../components/useDocumentSelector'\n\ninterface UseAssistantRuntimeProps {\n messages: Message[]\n setMessages: React.Dispatch<React.SetStateAction<Message[]>>\n conversationId: string | null\n setConversationId: (id: string | null) => void\n selectedDocuments: Document[]\n selectedAgent: string | null\n}\n\n/**\n * Convert internal messages to assistant-ui format\n * Only includes the custom metadata field with sources\n */\nfunction toThreadMessages(messages: Message[]): ThreadMessage[] {\n return messages.map((msg, index) => {\n // Only include custom metadata - other fields are optional and should be undefined\n const metadata = {\n custom: msg.sources ? { sources: msg.sources } : {}\n }\n\n if (msg.role === 'user') {\n return {\n id: `msg-${index}`,\n role: 'user' as const,\n content: [{ type: 'text' as const, text: msg.content }],\n createdAt: msg.timestamp,\n attachments: [],\n metadata\n }\n }\n\n return {\n id: `msg-${index}`,\n role: 'assistant' as const,\n content: [{ type: 'text' as const, text: msg.content }],\n createdAt: msg.timestamp,\n status: { type: 'complete' as const, reason: 'stop' as const },\n attachments: [],\n metadata\n }\n }) as ThreadMessage[]\n}\n\n/**\n * Hook that creates an assistant-ui runtime from existing chat hooks\n */\nexport function useAssistantRuntime({\n messages,\n setMessages,\n conversationId,\n setConversationId,\n selectedDocuments,\n selectedAgent\n}: UseAssistantRuntimeProps) {\n const { updateTokenUsage, setLimitError, adapter, loadHistory } = useChat()\n const [isRunning, setIsRunning] = useState(false)\n const threadMessages = useMemo(() => toThreadMessages(messages), [messages])\n\n const onNew = useCallback(\n async (message: AppendMessage) => {\n // Extract text content from the message\n const textContent = message.content\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map(part => part.text)\n .join('')\n\n if (!textContent.trim()) return\n\n // Set loading state\n setIsRunning(true)\n\n // Add user message and placeholder for assistant response in a single update\n const userMessage: Message = {\n role: 'user',\n content: textContent.trim(),\n timestamp: new Date()\n }\n\n setMessages(prev => [\n ...prev,\n userMessage,\n {\n role: 'assistant',\n content: '',\n timestamp: new Date()\n }\n ])\n\n let accumulatedContent = ''\n let receivedSources: Source[] = []\n\n try {\n // Use adapter to send message\n await adapter.sendMessage(\n textContent.trim(),\n {\n conversationId,\n selectedDocuments: selectedDocuments.map(doc => doc.id),\n agentSlug: selectedAgent\n },\n {\n onConversationId: id => setConversationId(id),\n onToken: token => {\n accumulatedContent += token\n setMessages(prev => {\n const updated = [...prev]\n const lastIdx = updated.length - 1\n if (lastIdx >= 0 && updated[lastIdx]?.role === 'assistant') {\n updated[lastIdx] = {\n ...updated[lastIdx],\n content: accumulatedContent\n }\n }\n return updated\n })\n },\n onSources: sources => {\n receivedSources = sources\n setMessages(prev => {\n const updated = [...prev]\n const lastIdx = updated.length - 1\n if (lastIdx >= 0 && updated[lastIdx]?.role === 'assistant') {\n updated[lastIdx] = { ...updated[lastIdx], sources: sources }\n }\n return updated\n })\n },\n onUsage: usage => {\n if (usage) {\n updateTokenUsage({\n limit: usage.daily_limit,\n used: usage.daily_used,\n remaining: usage.daily_remaining,\n percentage: usage.daily_limit > 0 ? (usage.daily_used / usage.daily_limit) * 100 : 0,\n reset_at: usage.reset_at\n })\n }\n },\n onDone: () => {\n // Final update to ensure consistency\n setMessages(prev => {\n const updated = [...prev]\n const lastIdx = updated.length - 1\n if (lastIdx >= 0 && updated[lastIdx]?.role === 'assistant') {\n updated[lastIdx] = {\n ...updated[lastIdx],\n content: accumulatedContent,\n sources: updated[lastIdx].sources || receivedSources\n }\n }\n return updated\n })\n },\n onError: error => {\n throw error\n }\n }\n )\n\n // Clear any previous limit error\n setLimitError(null)\n } catch (err: unknown) {\n const error = err as Error & { code?: string; chatId?: string; message: string }\n console.error('[useAssistantRuntime] Error:', error)\n\n // Handle expired conversation error\n if (error.code === 'EXPIRED_CONVERSATION') {\n console.warn('[useAssistantRuntime] Conversation expired:', error.chatId)\n\n // Replace placeholder with error message\n setMessages(prev => {\n const updated = [...prev]\n const lastIdx = updated.length - 1\n if (lastIdx >= 0 && updated[lastIdx]?.role === 'assistant') {\n updated[lastIdx] = {\n ...updated[lastIdx],\n content: `⚠️ **Conversación Expirada**\\n\\n${error.message}\\n\\nLas conversaciones expiran después de 24 horas de inactividad por motivos de seguridad y privacidad.`\n }\n }\n return updated\n })\n\n // Clear conversation ID so user can start a new conversation\n setConversationId(null)\n\n // Reload history to remove expired conversation from sidebar\n if (loadHistory) {\n loadHistory().catch(console.error)\n }\n\n return\n }\n\n // Handle 429 - Token limit exceeded (propagated from adapter)\n if (error.message === 'Has alcanzado tu límite diario de tokens.') {\n setLimitError(error.message)\n // Remove placeholder message\n setMessages(prev => prev.slice(0, -1))\n return\n }\n\n // Remove placeholder on other errors\n setMessages(prev => prev.slice(0, -1))\n } finally {\n // Clear loading state\n setIsRunning(false)\n }\n },\n [\n setMessages,\n conversationId,\n setConversationId,\n selectedDocuments,\n selectedAgent,\n setLimitError,\n updateTokenUsage,\n adapter,\n loadHistory\n ]\n )\n\n const runtime = useExternalStoreRuntime({\n messages: threadMessages,\n isRunning,\n onNew\n })\n\n return runtime\n}\n","import { debounce } from 'lodash'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nconst isDev = process.env.NODE_ENV === 'development'\n\nexport interface Document {\n id: string\n title: string\n slug: string\n type: string\n collection: string\n}\n\nfunction buildSearchParams(query: string, collections: string[]): URLSearchParams {\n const params = new URLSearchParams({\n q: query,\n exclude_fields: 'embedding,content,lexical_richtext',\n query_by: 'title',\n simple: 'true',\n mode: 'simple',\n per_page: '15'\n })\n for (const collection of collections) {\n params.append('collection', collection)\n }\n return params\n}\n\nfunction logSearchResponse(data: { documents?: Document[] }): void {\n console.log('[DocumentSelector] API response:', data)\n console.log('[DocumentSelector] Documents found:', data.documents?.length || 0)\n\n if (data.documents && data.documents.length > 0) {\n console.log(\n '[DocumentSelector] Sample document titles:',\n data.documents.slice(0, 3).map((d: Document) => d.title)\n )\n }\n}\n\nasync function fetchSearchResults(query: string, collections: string[]): Promise<Document[]> {\n const params = buildSearchParams(query, collections)\n const url = `/api/search?${params}`\n if (isDev) console.log('[DocumentSelector] Fetching URL:', url)\n\n const response = await fetch(url)\n if (isDev) console.log('[DocumentSelector] Response status:', response.status)\n\n if (!response.ok) {\n const errorText = await response.text()\n if (isDev) console.error('[DocumentSelector] API error:', response.status, errorText)\n throw new Error(`Error ${response.status}: ${errorText}`)\n }\n\n const data = await response.json()\n if (isDev) logSearchResponse(data)\n\n return data.documents || []\n}\n\n/**\n * Hook for managing document search functionality\n * @param collections - Typesense collection names to search in\n */\nexport function useDocumentSearch(collections: string[]) {\n const [searchQuery, setSearchQuery] = useState('')\n const [searchResults, setSearchResults] = useState<Document[]>([])\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n // Search function (not debounced)\n const performSearch = useCallback(\n async (query: string) => {\n if (query.trim().length < 2) {\n setSearchResults([])\n setIsLoading(false)\n return\n }\n\n if (isDev) console.log('[DocumentSelector] Searching for:', query)\n setIsLoading(true)\n setError(null)\n\n try {\n const documents = await fetchSearchResults(query, collections)\n setSearchResults(documents)\n } catch (err) {\n if (isDev) console.error('[DocumentSelector] Error searching documents:', err)\n setError(err instanceof Error ? err.message : 'Error desconocido')\n setSearchResults([])\n } finally {\n setIsLoading(false)\n }\n },\n [collections]\n )\n\n // Create debounced version using useMemo\n const debouncedSearch = useMemo(() => debounce(performSearch, 300), [performSearch])\n\n // Cleanup debounced function on unmount\n useEffect(() => {\n return () => {\n debouncedSearch.cancel()\n }\n }, [debouncedSearch])\n\n // Handle search query changes\n const handleSearchChange = useCallback(\n (query: string) => {\n setSearchQuery(query)\n if (query.trim().length >= 2) {\n setIsLoading(true)\n debouncedSearch(query)\n } else {\n setSearchResults([])\n setIsLoading(false)\n }\n },\n [debouncedSearch]\n )\n\n return {\n searchQuery,\n searchResults,\n isLoading,\n error,\n handleSearchChange\n }\n}\n\n/**\n * Hook for managing document selection\n */\nexport function useDocumentSelection(onSelectionChange?: (selectedDocuments: Document[]) => void) {\n const [selectedDocuments, setSelectedDocuments] = useState<Document[]>([])\n\n // Keep a ref to the latest onSelectionChange to avoid unnecessary effect runs\n const onSelectionChangeRef = useRef(onSelectionChange)\n useEffect(() => {\n onSelectionChangeRef.current = onSelectionChange\n }, [onSelectionChange])\n\n // Notify parent when selection changes\n useEffect(() => {\n onSelectionChangeRef.current?.(selectedDocuments)\n }, [selectedDocuments])\n\n // Toggle document selection\n const toggleDocument = useCallback((document: Document) => {\n setSelectedDocuments(prev => {\n const isSelected = prev.some(d => d.id === document.id)\n return isSelected ? prev.filter(d => d.id !== document.id) : [...prev, document]\n })\n }, [])\n\n // Remove document from selection\n const removeDocument = useCallback((documentId: string, e: React.MouseEvent) => {\n e.stopPropagation()\n setSelectedDocuments(prev => prev.filter(d => d.id !== documentId))\n }, [])\n\n // Clear all selections\n const clearAllSelections = useCallback(() => {\n setSelectedDocuments([])\n }, [])\n\n return {\n selectedDocuments,\n toggleDocument,\n removeDocument,\n clearAllSelections\n }\n}\n\n/**\n * Hook for combining selected documents with search results\n * Optimized with Set for O(n) complexity instead of O(n*m)\n */\nexport function useCombinedDocuments(selectedDocuments: Document[], searchResults: Document[]) {\n return useMemo(() => {\n const combined = [...selectedDocuments]\n const selectedIds = new Set(selectedDocuments.map(doc => doc.id))\n\n // Add search results that aren't already selected\n searchResults.forEach(doc => {\n if (!selectedIds.has(doc.id)) {\n combined.push(doc)\n }\n })\n\n return combined\n }, [selectedDocuments, searchResults])\n}\n","'use client'\n\nimport { BookOpen, ChevronDown, FileText, Loader2, Search, X } from 'lucide-react'\nimport { useCallback, useState } from 'react'\nimport { cn } from '../lib/utils'\nimport { useChat } from './chat-context'\nimport { type Document, useCombinedDocuments, useDocumentSearch, useDocumentSelection } from './useDocumentSelector'\n\ninterface DocumentSelectorProps {\n onSelectionChange?: (selectedDocuments: Document[]) => void\n isMaximized?: boolean\n isSidePanel?: boolean\n}\n\nconst DocumentSelector = ({ onSelectionChange, isSidePanel = false }: DocumentSelectorProps) => {\n const [isExpanded, setIsExpanded] = useState(isSidePanel)\n const { searchCollections } = useChat()\n\n const {\n searchQuery,\n searchResults,\n isLoading,\n error,\n handleSearchChange: baseHandleSearchChange\n } = useDocumentSearch(searchCollections)\n const { selectedDocuments, toggleDocument, clearAllSelections } = useDocumentSelection(onSelectionChange)\n const allDocuments = useCombinedDocuments(selectedDocuments, searchResults)\n\n const handleSearchChange = useCallback(\n (query: string) => {\n baseHandleSearchChange(query)\n if (query.trim().length >= 2) {\n setIsExpanded(true)\n }\n },\n [baseHandleSearchChange]\n )\n\n const getDocumentIcon = (type: Document['type']) => {\n return type === 'book' ? <BookOpen className=\"w-4 h-4\" /> : <FileText className=\"w-4 h-4\" />\n }\n\n if (isSidePanel) {\n return (\n <div className=\"h-full flex flex-col bg-background border-r border-border\">\n <div className=\"p-4 border-b border-border\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Buscar contenido...\"\n value={searchQuery}\n onChange={e => handleSearchChange(e.target.value)}\n className=\"flex h-9 w-full rounded-md border border-input bg-background pl-10 pr-3 py-1 text-sm text-foreground shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n />\n </div>\n </div>\n\n {selectedDocuments.length > 0 && (\n <div className=\"flex items-center justify-between p-4 border-b border-border bg-muted/50\">\n <span className=\"text-sm font-medium text-foreground\">\n {selectedDocuments.length} documento{selectedDocuments.length !== 1 ? 's' : ''}\n </span>\n <button\n type=\"button\"\n onClick={clearAllSelections}\n className=\"text-sm text-destructive hover:text-destructive/80\"\n >\n Limpiar\n </button>\n </div>\n )}\n\n <div className=\"flex-1 overflow-y-auto\">\n {isLoading && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin mx-auto mb-2\" />\n Buscando...\n </div>\n )}\n\n {error && <div className=\"p-4 text-center text-sm text-destructive\">{error}</div>}\n\n {!isLoading && !error && searchQuery.length >= 2 && searchResults.length === 0 && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">Sin resultados para \"{searchQuery}\"</div>\n )}\n\n {!isLoading && !error && searchQuery.length < 2 && selectedDocuments.length === 0 && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">Busca libros o artículos para filtrar</div>\n )}\n\n {!isLoading && !error && allDocuments.length > 0 && (\n <div>\n {allDocuments.map(doc => {\n const isSelected = selectedDocuments.some(d => d.id === doc.id)\n return (\n <button\n type=\"button\"\n key={doc.id}\n onClick={() => toggleDocument(doc)}\n className={cn(\n 'w-full flex items-center justify-between p-4 text-left border-b border-border/50 hover:bg-muted/50 transition-colors',\n isSelected && 'bg-primary/10'\n )}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span className=\"text-muted-foreground\">{getDocumentIcon(doc.type)}</span>\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium text-foreground truncate\">{doc.title}</div>\n <div className=\"text-xs text-muted-foreground\">\n {doc.type === 'book' ? 'Libro' : 'Artículo'}\n </div>\n </div>\n </div>\n {isSelected && <X className=\"w-4 h-4 text-destructive flex-shrink-0\" />}\n </button>\n )\n })}\n </div>\n )}\n </div>\n </div>\n )\n }\n\n // Dropdown mode\n return (\n <div className=\"space-y-3\">\n <div className=\"relative\">\n <div className=\"flex\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Buscar libros o artículos...\"\n value={searchQuery}\n onChange={e => handleSearchChange(e.target.value)}\n className=\"flex h-9 w-full rounded-l-md border border-input bg-background pl-10 pr-3 py-1 text-sm text-foreground shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n />\n </div>\n <button\n type=\"button\"\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"inline-flex items-center gap-1 h-9 rounded-r-md border border-l-0 border-input bg-background px-3 text-sm text-foreground hover:bg-accent transition-colors\"\n >\n {selectedDocuments.length > 0 ? `${selectedDocuments.length} filtros` : 'Filtros'}\n <ChevronDown className={cn('w-4 h-4 transition-transform', isExpanded && 'rotate-180')} />\n </button>\n </div>\n\n {isExpanded && (\n <>\n <button\n type=\"button\"\n className=\"fixed inset-0 z-40 cursor-default bg-transparent border-none\"\n onClick={() => setIsExpanded(false)}\n aria-label=\"Cerrar selector\"\n />\n <div className=\"absolute top-full left-0 right-0 mt-2 bg-popover border border-border rounded-lg shadow-lg max-h-80 overflow-hidden z-50\">\n {selectedDocuments.length > 0 && (\n <div className=\"flex items-center justify-between p-3 border-b border-border bg-muted/50\">\n <span className=\"text-sm font-medium text-foreground\">\n {selectedDocuments.length} seleccionado{selectedDocuments.length !== 1 ? 's' : ''}\n </span>\n <button\n type=\"button\"\n onClick={clearAllSelections}\n className=\"text-sm text-destructive hover:text-destructive/80\"\n >\n Limpiar\n </button>\n </div>\n )}\n\n <div className=\"max-h-64 overflow-y-auto\">\n {isLoading && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">\n <Loader2 className=\"w-4 h-4 animate-spin mx-auto mb-2\" />\n Buscando...\n </div>\n )}\n\n {!isLoading && !error && allDocuments.length > 0 && (\n <div>\n {allDocuments.map(doc => {\n const isSelected = selectedDocuments.some(d => d.id === doc.id)\n return (\n <button\n type=\"button\"\n key={doc.id}\n onClick={() => toggleDocument(doc)}\n className={cn(\n 'w-full flex items-center justify-between p-3 text-left border-b border-border/50 hover:bg-muted/50 transition-colors',\n isSelected && 'bg-primary/10'\n )}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span className=\"text-muted-foreground\">{getDocumentIcon(doc.type)}</span>\n <div className=\"min-w-0\">\n <div className=\"text-sm font-medium text-foreground truncate\">{doc.title}</div>\n <div className=\"text-xs text-muted-foreground\">\n {doc.type === 'book' ? 'Libro' : 'Artículo'}\n </div>\n </div>\n </div>\n {isSelected && <X className=\"w-4 h-4 text-destructive flex-shrink-0\" />}\n </button>\n )\n })}\n </div>\n )}\n\n {!isLoading && !error && searchQuery.length < 2 && selectedDocuments.length === 0 && (\n <div className=\"p-4 text-center text-sm text-muted-foreground\">Busca libros o artículos</div>\n )}\n </div>\n </div>\n </>\n )}\n </div>\n </div>\n )\n}\n\nexport default DocumentSelector\n","'use client'\n\nimport { forwardRef, useEffect, useImperativeHandle, useState } from 'react'\nimport { useAssistantRuntime } from '../hooks/useAssistantRuntime'\nimport type { LinkComponent } from '../types/components'\nimport { Thread } from './assistant-ui/thread'\nimport { useChat } from './chat-context'\nimport DocumentSelector from './DocumentSelector'\nimport type { Document } from './useDocumentSelector'\n\nexport interface ChatInterfaceRef {\n handleNewConversation: () => void\n}\n\ninterface ChatInterfaceProps {\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n}\n\nconst ChatInterface = forwardRef<ChatInterfaceRef, ChatInterfaceProps>(({ generateHref, LinkComponent }, ref) => {\n const {\n isMaximized,\n selectedAgent,\n agents,\n // Session props from context\n conversationId,\n setConversationId,\n messages,\n setMessages,\n isLoadingSession,\n handleNewConversation,\n isLoadingAgents\n } = useChat()\n const [selectedDocuments, setSelectedDocuments] = useState<Document[]>([])\n const [isDesktop, setIsDesktop] = useState(false)\n\n // Find the full agent configuration\n const currentAgent = agents.find(agent => agent.slug === selectedAgent)\n\n // Create assistant-ui runtime\n const runtime = useAssistantRuntime({\n messages,\n setMessages,\n conversationId,\n setConversationId,\n selectedDocuments,\n selectedAgent\n })\n\n // Detect if device is desktop (window width >= 1024px)\n useEffect(() => {\n const checkIsDesktop = () => {\n setIsDesktop(window.innerWidth >= 1024)\n }\n\n checkIsDesktop()\n window.addEventListener('resize', checkIsDesktop)\n\n return () => window.removeEventListener('resize', checkIsDesktop)\n }, [])\n\n // Determine if we should use side panel layout\n const shouldUseSidePanel = isMaximized && isDesktop\n\n // Expose handleNewConversation to parent via ref\n useImperativeHandle(ref, () => ({\n handleNewConversation\n }))\n\n // Show loading state while restoring session or loading agents\n if (isLoadingSession || isLoadingAgents || (agents.length > 0 && !selectedAgent)) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center\">\n <div className=\"flex space-x-2 justify-center mb-4\">\n <div className=\"w-3 h-3 rounded-full bg-primary animate-bounce\" />\n <div className=\"w-3 h-3 rounded-full bg-primary animate-bounce delay-75\" />\n <div className=\"w-3 h-3 rounded-full bg-primary animate-bounce delay-150\" />\n </div>\n <p className=\"text-muted-foreground\">\n {isLoadingSession ? 'Cargando conversación...' : 'Cargando asistente...'}\n </p>\n </div>\n </div>\n )\n }\n\n // Show error/empty state if no agents loaded\n if (!isLoadingAgents && agents.length === 0) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <div className=\"text-center max-w-md p-6\">\n <p className=\"text-lg font-medium text-muted-foreground mb-2\">No hay asistentes disponibles</p>\n <p className=\"text-sm text-muted-foreground\">Por favor, contacta con el administrador del sistema.</p>\n </div>\n </div>\n )\n }\n\n if (shouldUseSidePanel) {\n // Desktop maximized mode: Side panel layout (1/4 + 3/4)\n return (\n <div className=\"flex h-full\">\n {/* Document Selector Side Panel (1/4 width) */}\n <div className=\"w-1/4 flex-shrink-0\">\n <DocumentSelector onSelectionChange={setSelectedDocuments} isMaximized={isMaximized} isSidePanel={true} />\n </div>\n\n {/* Chat Area (3/4 width) */}\n <div className=\"flex-1 flex flex-col\">\n <Thread\n runtime={runtime}\n welcomeTitle={currentAgent?.welcomeTitle || undefined}\n welcomeSubtitle={currentAgent?.welcomeSubtitle || undefined}\n suggestedQuestions={currentAgent?.suggestedQuestions}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n agentName={currentAgent?.name}\n />\n </div>\n </div>\n )\n }\n\n // Default layout: Mobile/tablet or desktop non-maximized (dropdown mode)\n return (\n <div className=\"flex flex-col h-full\">\n {/* Document Selector */}\n <div className=\"border-b border-border p-4 bg-background\">\n <DocumentSelector onSelectionChange={setSelectedDocuments} isMaximized={isMaximized} isSidePanel={false} />\n </div>\n\n {/* Chat Thread */}\n <div className=\"flex-1 min-h-0\">\n <Thread\n runtime={runtime}\n welcomeTitle={currentAgent?.welcomeTitle || undefined}\n welcomeSubtitle={currentAgent?.welcomeSubtitle || undefined}\n suggestedQuestions={currentAgent?.suggestedQuestions}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n agentName={currentAgent?.name}\n />\n </div>\n </div>\n )\n})\n\nChatInterface.displayName = 'ChatInterface'\n\nexport default ChatInterface\n","'use client'\nimport { formatDistanceToNow } from 'date-fns'\nimport { es } from 'date-fns/locale'\nimport { Check, Edit2, Loader2, MessageSquare, MoreHorizontal, Trash2, X } from 'lucide-react'\nimport type React from 'react'\nimport { useEffect, useState } from 'react'\nimport type { PublicAgentInfo } from '../adapters/ChatAdapter'\nimport type { SessionSummary } from '../hooks/useChatSession'\nimport { cn } from '../lib/utils'\n\ninterface ChatHistoryListProps {\n sessions: SessionSummary[]\n activeSessionId: string | null\n isLoading: boolean\n onSelectSession: (id: string) => void\n onRenameSession: (id: string, newTitle: string) => Promise<boolean>\n onDeleteSession: (id: string) => Promise<boolean>\n onLoadHistory: () => Promise<void>\n agents?: PublicAgentInfo[]\n}\n\nexport const ChatHistoryList = ({\n sessions,\n activeSessionId,\n isLoading,\n onSelectSession,\n onRenameSession,\n onDeleteSession,\n onLoadHistory,\n agents = []\n}: ChatHistoryListProps) => {\n const [editingId, setEditingId] = useState<string | null>(null)\n const [editTitle, setEditTitle] = useState('')\n const [deletingId, setDeletingId] = useState<string | null>(null)\n const [menuOpenId, setMenuOpenId] = useState<string | null>(null)\n\n // Helper to get agent name from slug\n const getAgentName = (agentSlug?: string) => {\n if (!agentSlug) return null\n const agent = agents.find(a => a.slug === agentSlug)\n return agent?.name || null\n }\n\n useEffect(() => {\n onLoadHistory()\n }, [onLoadHistory])\n\n const handleStartEdit = (session: SessionSummary, e: React.MouseEvent) => {\n e.stopPropagation()\n setEditingId(session.conversation_id)\n setEditTitle(session.title || 'Nueva conversación')\n setMenuOpenId(null)\n }\n\n const handleSaveEdit = async (e: React.MouseEvent | React.FormEvent) => {\n e.stopPropagation()\n if (!editingId) return\n\n await onRenameSession(editingId, editTitle)\n setEditingId(null)\n }\n\n const handleCancelEdit = () => {\n setEditingId(null)\n }\n\n const handleDeleteClick = (id: string, e: React.MouseEvent) => {\n e.stopPropagation()\n setDeletingId(id)\n setMenuOpenId(null)\n }\n\n const handleConfirmDelete = async (e: React.MouseEvent) => {\n e.stopPropagation()\n if (deletingId) {\n await onDeleteSession(deletingId)\n setDeletingId(null)\n }\n }\n\n const handleCancelDelete = (e: React.MouseEvent) => {\n e.stopPropagation()\n setDeletingId(null)\n }\n\n const toggleMenu = (id: string, e: React.MouseEvent) => {\n e.stopPropagation()\n setMenuOpenId(menuOpenId === id ? null : id)\n }\n\n if (isLoading && sessions.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-muted-foreground\">\n <Loader2 className=\"h-6 w-6 animate-spin mb-2\" />\n <span className=\"text-sm\">Cargando historial...</span>\n </div>\n )\n }\n\n if (sessions.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center py-8 text-muted-foreground\">\n <MessageSquare className=\"h-8 w-8 mb-2 opacity-20\" />\n <span className=\"text-sm\">No hay conversaciones anteriores</span>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-1\">\n {sessions.map((session, _index) => (\n <button\n type=\"button\"\n key={session.conversation_id}\n className={cn(\n 'group relative flex items-center rounded-md px-2 py-2 text-sm transition-colors hover:bg-accent/50 cursor-pointer w-full text-left',\n activeSessionId === session.conversation_id ? 'bg-accent text-accent-foreground' : 'text-foreground'\n )}\n onClick={() => onSelectSession(session.conversation_id)}\n >\n <MessageSquare className=\"mr-2 h-4 w-4 opacity-70 flex-shrink-0\" />\n\n {editingId === session.conversation_id ? (\n <div className=\"flex items-center flex-1 gap-1\">\n <input\n type=\"text\"\n value={editTitle}\n onChange={e => setEditTitle(e.target.value)}\n onClick={e => e.stopPropagation()}\n className=\"h-6 flex-1 rounded-sm border border-input bg-transparent px-1 text-xs outline-none focus:ring-1 focus:ring-ring\"\n onKeyDown={e => {\n e.stopPropagation()\n if (e.key === 'Enter') handleSaveEdit(e)\n if (e.key === 'Escape') {\n setEditingId(null)\n setEditTitle('')\n }\n }}\n />\n <button\n type=\"button\"\n onClick={e => {\n e.stopPropagation()\n handleSaveEdit(e)\n }}\n className=\"p-1 hover:text-green-500\"\n >\n <Check className=\"h-3 w-3\" />\n </button>\n <button\n type=\"button\"\n onClick={e => {\n e.stopPropagation()\n handleCancelEdit()\n }}\n className=\"p-1 hover:text-red-500\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </div>\n ) : deletingId === session.conversation_id ? (\n <div className=\"flex items-center flex-1 justify-between bg-destructive/10 -mx-2 px-2 py-1 rounded\">\n <span className=\"text-xs text-destructive font-medium\">¿Borrar?</span>\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={handleConfirmDelete}\n className=\"p-1 text-destructive hover:bg-destructive/20 rounded\"\n >\n <Check className=\"h-3 w-3\" />\n </button>\n <button\n type=\"button\"\n onClick={handleCancelDelete}\n className=\"p-1 text-muted-foreground hover:bg-black/5 rounded\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n </div>\n </div>\n ) : (\n <>\n <div className=\"flex-1 overflow-hidden\">\n <div className=\"truncate font-medium\">{session.title || 'Conversación sin título'}</div>\n {(() => {\n const agentName = getAgentName(session.agentSlug)\n return agentName && <div className=\"truncate text-xs text-primary/80 font-medium\">{agentName}</div>\n })()}\n <div className=\"truncate text-xs text-muted-foreground opacity-70\">\n {formatDistanceToNow(new Date(session.last_activity), { addSuffix: true, locale: es })}\n </div>\n </div>\n\n <div className=\"relative flex items-center\">\n {menuOpenId === session.conversation_id ? (\n <div className=\"flex items-center gap-1 animate-in fade-in slide-in-from-right-4 duration-200 bg-background/80 backdrop-blur-sm rounded-md p-1 pl-2 ml-[-8px]\">\n <button\n type=\"button\"\n onClick={e => handleStartEdit(session, e)}\n className=\"p-1.5 text-foreground/70 hover:text-foreground hover:bg-accent rounded-sm transition-colors\"\n title=\"Renombrar\"\n >\n <Edit2 className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={e => handleDeleteClick(session.conversation_id, e)}\n className=\"p-1.5 text-destructive/70 hover:text-destructive hover:bg-destructive/10 rounded-sm transition-colors\"\n title=\"Eliminar\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n </button>\n <button\n type=\"button\"\n onClick={e => {\n e.stopPropagation()\n setMenuOpenId(null)\n }}\n className=\"p-1.5 text-muted-foreground hover:text-foreground hover:bg-accent rounded-sm transition-colors\"\n title=\"Cerrar menú\"\n >\n <X className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={e => toggleMenu(session.conversation_id, e)}\n className={cn(\n 'ml-1 rounded p-1 text-muted-foreground hover:bg-accent transition-all',\n 'opacity-70 group-hover:opacity-100'\n )}\n title=\"Opciones\"\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n </>\n )}\n </button>\n ))}\n </div>\n )\n}\n","'use client'\n\nimport { Check, ChevronDown, ChevronLeft, History, MessageSquarePlus } from 'lucide-react'\nimport { useState } from 'react'\nimport { cn } from '../lib/utils'\nimport { ChatHistoryList } from './ChatHistoryList'\nimport { useChat } from './chat-context'\n\ninterface ChatMenuDropdownProps {\n title: string\n onNewConversation: () => void\n}\n\nconst ChatMenuDropdown = ({ title, onNewConversation }: ChatMenuDropdownProps) => {\n const {\n agents,\n selectedAgent,\n setSelectedAgent,\n // History props\n sessionsHistory,\n isLoadingHistory,\n loadHistory,\n loadSession,\n renameSession,\n deleteSession,\n conversationId\n } = useChat()\n const [isOpen, setIsOpen] = useState(false)\n const [pendingAgentSlug, setPendingAgentSlug] = useState<string | null>(null)\n const [menuView, setMenuView] = useState<'main' | 'history'>('main')\n\n // Import History List (using dynamic import might be cleaner but inline here is fine if imported at top)\n // Assuming ChatHistoryList is imported at top. If not, I need to add import.\n\n const handleNewConversationClick = () => {\n onNewConversation()\n setIsOpen(false)\n }\n\n const handleAgentSelect = (agentSlug: string) => {\n if (agentSlug === selectedAgent) {\n setIsOpen(false)\n return\n }\n setPendingAgentSlug(agentSlug)\n }\n\n const confirmAgentChange = () => {\n if (pendingAgentSlug) {\n setSelectedAgent(pendingAgentSlug)\n onNewConversation()\n setPendingAgentSlug(null)\n setIsOpen(false)\n }\n }\n\n const cancelAgentChange = () => {\n setPendingAgentSlug(null)\n }\n\n const handleOpenHistory = () => {\n setMenuView('history')\n }\n\n const handleBackToMenu = () => {\n setMenuView('main')\n }\n\n const handleSelectHistorySession = async (id: string) => {\n await loadSession(id)\n setIsOpen(false)\n }\n\n // Reset view when closing\n const toggleOpen = () => {\n if (isOpen) {\n setIsOpen(false)\n // Small delay to reset view after animation could be nice, but instant is fine\n setTimeout(() => setMenuView('main'), 200)\n } else {\n setIsOpen(true)\n }\n }\n\n // Find current agent name for title\n const currentAgent = agents.find(a => a.slug === selectedAgent)\n const displayTitle = currentAgent?.name || currentAgent?.slug || title\n const pendingAgent = agents.find(a => a.slug === pendingAgentSlug)\n\n return (\n <>\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={toggleOpen}\n className=\"flex items-center gap-2 text-xl font-bold text-foreground hover:text-foreground/80 transition-colors\"\n aria-label=\"Menú de chat\"\n >\n <span>{displayTitle}</span>\n <ChevronDown className={cn('w-4 h-4 transition-transform', isOpen && 'rotate-180')} />\n </button>\n\n {isOpen && (\n <>\n <button\n type=\"button\"\n className=\"fixed inset-0 z-40 cursor-default bg-transparent border-none\"\n onClick={toggleOpen}\n aria-label=\"Cerrar menú\"\n />\n\n {/* Dropdown */}\n <div className=\"absolute top-full left-0 mt-2 z-50 min-w-[16rem] max-w-[20rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md\">\n {menuView === 'main' ? (\n <>\n <button\n type=\"button\"\n onClick={handleNewConversationClick}\n className=\"relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground\"\n >\n <MessageSquarePlus className=\"w-4 h-4\" />\n Nueva conversación\n </button>\n\n <button\n type=\"button\"\n onClick={handleOpenHistory}\n className=\"relative flex w-full cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground\"\n >\n <History className=\"w-4 h-4\" />\n Historial de chats\n </button>\n\n {agents.length > 1 && (\n <>\n <div className=\"-mx-1 my-1 h-px bg-border\" />\n <div className=\"px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider\">\n Cambiar Agente\n </div>\n {agents.map(agent => (\n <button\n type=\"button\"\n key={agent.slug}\n onClick={() => handleAgentSelect(agent.slug)}\n className={cn(\n 'relative flex w-full cursor-pointer select-none items-center justify-between gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors hover:bg-accent hover:text-accent-foreground',\n selectedAgent === agent.slug && 'text-primary font-medium bg-primary/10'\n )}\n >\n <span className=\"truncate\">{agent.name || agent.slug}</span>\n {selectedAgent === agent.slug && <Check className=\"w-4 h-4 flex-shrink-0\" />}\n </button>\n ))}\n </>\n )}\n </>\n ) : (\n <div className=\"flex flex-col max-h-[60vh]\">\n <div className=\"flex items-center gap-2 px-2 py-1.5 border-b border-border mb-1\">\n <button type=\"button\" onClick={handleBackToMenu} className=\"p-1 hover:bg-accent rounded-sm\">\n <ChevronLeft className=\"w-4 h-4\" />\n </button>\n <span className=\"text-sm font-semibold\">Historial</span>\n </div>\n\n <div className=\"overflow-y-auto custom-scrollbar p-1\">\n <ChatHistoryList\n sessions={sessionsHistory}\n activeSessionId={conversationId}\n isLoading={isLoadingHistory}\n onSelectSession={handleSelectHistorySession}\n onRenameSession={renameSession}\n onDeleteSession={deleteSession}\n onLoadHistory={loadHistory}\n agents={agents}\n />\n </div>\n </div>\n )}\n </div>\n </>\n )}\n </div>\n\n {/* Confirmation Dialog */}\n {pendingAgentSlug && (\n <>\n <button\n type=\"button\"\n className=\"fixed inset-0 z-50 bg-black/50 backdrop-blur-sm cursor-default border-none\"\n onClick={cancelAgentChange}\n aria-label=\"Cerrar diálogo\"\n />\n <div className=\"fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 w-full max-w-md border border-border bg-background p-6 shadow-lg rounded-xl\">\n <h3 className=\"text-lg font-semibold\">¿Cambiar a {pendingAgent?.name || pendingAgent?.slug}?</h3>\n <p className=\"mt-2 text-sm text-muted-foreground\">\n Se iniciará una nueva conversación con este agente. La conversación actual se guardará en el historial.\n </p>\n <div className=\"mt-4 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={cancelAgentChange}\n className=\"inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground\"\n >\n Cancelar\n </button>\n <button\n type=\"button\"\n onClick={confirmAgentChange}\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow hover:bg-primary/90\"\n >\n Sí, cambiar\n </button>\n </div>\n </div>\n </>\n )}\n </>\n )\n}\n\nexport default ChatMenuDropdown\n","'use client'\n\nimport { AnimatePresence, motion } from 'framer-motion'\nimport { Bot, Maximize2, Minimize2, X } from 'lucide-react'\nimport { useRef } from 'react'\nimport { cn } from '../lib/utils'\nimport { DefaultImage, DefaultLink, type ImageComponent, type LinkComponent } from '../types/components'\nimport ChatInterface, { type ChatInterfaceRef } from './ChatInterface'\nimport ChatMenuDropdown from './ChatMenuDropdown'\nimport { useChat } from './chat-context'\n\ninterface FloatingChatPanelProps {\n isOpen: boolean\n onClose: () => void\n aiIcon?: string\n agentName?: string\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n ImageComponent?: ImageComponent\n LinkComponent?: LinkComponent\n}\n\nconst FloatingChatPanel = ({\n isOpen,\n onClose,\n aiIcon,\n agentName = 'Asistente',\n generateHref,\n ImageComponent: Image = DefaultImage,\n LinkComponent: Link = DefaultLink\n}: FloatingChatPanelProps) => {\n const { isMaximized, setMaximized } = useChat()\n const chatInterfaceRef = useRef<ChatInterfaceRef>(null)\n\n const handleNewConversation = () => {\n chatInterfaceRef.current?.handleNewConversation()\n }\n\n return (\n <>\n {/* Backdrop cuando esta maximizado - solo en desktop */}\n <AnimatePresence>\n {isMaximized && isOpen && (\n <motion.div\n className=\"fixed inset-0 bg-black/20 backdrop-blur-sm z-40 hidden lg:block\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n onClick={() => setMaximized(false)}\n />\n )}\n </AnimatePresence>\n\n <AnimatePresence>\n {isOpen && (\n <motion.div\n className=\"fixed bg-background shadow-2xl flex flex-col z-50 lg:border lg:border-border overflow-hidden\"\n style={{ borderWidth: '0.5px' }}\n initial={{\n x: '-100vw',\n left: 0,\n top: 0,\n bottom: 0,\n width: '100vw',\n borderRadius: '0px'\n }}\n animate={\n isMaximized\n ? {\n x: 0,\n left: 0,\n top: 0,\n right: 0,\n bottom: 0,\n width: '100vw',\n height: '100vh',\n borderRadius: '0px'\n }\n : typeof window !== 'undefined' && window.innerWidth < 1024\n ? {\n x: 0,\n left: 0,\n top: 0,\n right: 'auto',\n bottom: 0,\n width: '100vw',\n height: '100vh',\n borderRadius: '0px'\n }\n : {\n x: 0,\n left: '1rem',\n top: '5rem',\n right: 'auto',\n bottom: '1rem',\n width: '33.333333%',\n height: 'auto',\n borderRadius: '0.75rem'\n }\n }\n exit={{\n x: '-100vw',\n transition: {\n type: 'spring',\n damping: 20,\n stiffness: 200\n }\n }}\n transition={{\n type: 'spring',\n damping: 25,\n stiffness: 250\n }}\n >\n {/* Header with gradient */}\n <div className=\"flex justify-between items-center p-4 border-b border-border flex-shrink-0 bg-gradient-to-r from-background via-muted/30 to-background\">\n <div className=\"flex items-center gap-3\">\n {/* Avatar with breathing effect and status indicator */}\n <div className=\"relative\">\n <motion.div\n className={cn(\n 'w-10 h-10 rounded-full p-0.5 flex-shrink-0 bg-gradient-to-br from-primary via-primary/80 to-primary/60 animate-pulse-glow',\n !aiIcon && 'flex items-center justify-center'\n )}\n >\n {aiIcon ? (\n <div className=\"w-full h-full rounded-full overflow-hidden\">\n <Image\n src={aiIcon}\n alt={agentName}\n className=\"w-full h-full object-cover\"\n width={40}\n height={40}\n />\n </div>\n ) : (\n <div className=\"w-full h-full rounded-full overflow-hidden bg-primary flex items-center justify-center\">\n <Bot className=\"w-6 h-6 text-primary-foreground\" />\n </div>\n )}\n </motion.div>\n {/* Status indicator - AI available */}\n <motion.div\n className=\"absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 rounded-full bg-chat-status-online border-2 border-background\"\n animate={{\n scale: [1, 1.2, 1],\n opacity: [1, 0.7, 1]\n }}\n transition={{\n duration: 2,\n repeat: Infinity,\n ease: 'easeInOut'\n }}\n />\n </div>\n <div className=\"flex flex-col\">\n <ChatMenuDropdown title={agentName} onNewConversation={handleNewConversation} />\n <span className=\"text-xs text-muted-foreground\">Disponible para ayudarte</span>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n {/* Boton maximizar/minimizar - solo visible en desktop (>=1024px) */}\n <motion.button\n onClick={() => setMaximized(!isMaximized)}\n className=\"hidden lg:flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors\"\n aria-label={isMaximized ? 'Minimizar' : 'Maximizar'}\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n {isMaximized ? <Minimize2 className=\"w-4 h-4\" /> : <Maximize2 className=\"w-4 h-4\" />}\n </motion.button>\n <motion.button\n onClick={onClose}\n className=\"h-9 w-9 flex items-center justify-center rounded-md text-muted-foreground hover:text-foreground hover:bg-accent transition-colors\"\n aria-label=\"Cerrar chat\"\n whileHover={{ scale: 1.1 }}\n whileTap={{ scale: 0.95 }}\n >\n <X className=\"w-5 h-5\" />\n </motion.button>\n </div>\n </div>\n\n {/* Chat Content */}\n <div className=\"flex-1 overflow-hidden\">\n <ChatInterface ref={chatInterfaceRef} generateHref={generateHref} LinkComponent={Link} />\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </>\n )\n}\n\nexport default FloatingChatPanel\n","'use client'\n\nimport type { ImageComponent, LinkComponent } from '../types/components'\nimport FloatingChatButton from './buttons/FloatingChatButton'\nimport { useChat } from './chat-context'\nimport FloatingChatPanel from './FloatingChatPanel'\n\n/**\n * Minimal user type - consumer provides their own user type\n */\ninterface User {\n id: string | number\n [key: string]: unknown\n}\n\ninterface FloatingChatManagerProps {\n aiIcon?: string\n useUser: () => { user: User | null }\n generateHref: (props: { type: string; value: { id: number; slug?: string | null } }) => string\n LinkComponent?: LinkComponent\n ImageComponent?: ImageComponent\n}\n\nconst FloatingChatManager = ({\n aiIcon,\n useUser,\n generateHref,\n LinkComponent,\n ImageComponent\n}: FloatingChatManagerProps) => {\n const { user } = useUser()\n const { isPanelOpen, openPanel, closePanel, agents, selectedAgent } = useChat()\n\n if (!user) return null\n\n const currentAgent = agents.find(agent => agent.slug === selectedAgent)\n const currentAvatar =\n currentAgent?.avatar && currentAgent.avatar.trim() !== '' ? currentAgent.avatar : aiIcon || undefined\n const currentAgentName = currentAgent?.name || 'Asistente'\n\n return (\n <>\n <FloatingChatButton onOpen={openPanel} aiIcon={currentAvatar} ImageComponent={ImageComponent} />\n {/* Siempre renderizar para que AnimatePresence funcione */}\n <FloatingChatPanel\n isOpen={isPanelOpen}\n onClose={closePanel}\n aiIcon={currentAvatar}\n agentName={currentAgentName}\n generateHref={generateHref}\n LinkComponent={LinkComponent}\n ImageComponent={ImageComponent}\n />\n </>\n )\n}\n\nexport default FloatingChatManager\n"],"mappings":"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"}
|