@filigran/chatbot 3.2.2 → 3.3.1
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/README.md +84 -44
- package/dist/index.d.ts +45 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/utils/index.ts","../../src/hooks/protocols/parseLegacyEvent.ts","../../src/hooks/protocols/parseRestEvent.ts","../../src/hooks/protocols/parseAgUiEvent.ts","../../src/hooks/useChat.ts","../../src/hooks/useAgents.ts","../../src/hooks/useSidebarResize.ts","../../src/components/icons/AttachFileIcon.tsx","../../src/components/icons/BrainIcon.tsx","../../src/components/icons/CheckIcon.tsx","../../src/components/icons/ChevronDownIcon.tsx","../../src/components/icons/CloseIcon.tsx","../../src/components/icons/CopyIcon.tsx","../../src/components/icons/DatabaseIcon.tsx","../../src/components/icons/DefaultLogoIcon.tsx","../../src/components/icons/EditIcon.tsx","../../src/components/icons/ExternalLinkIcon.tsx","../../src/components/icons/FileIcon.tsx","../../src/components/icons/FloatingIcon.tsx","../../src/components/icons/FullscreenExitIcon.tsx","../../src/components/icons/FullscreenIcon.tsx","../../src/components/icons/GlobeIcon.tsx","../../src/components/icons/InfoIcon.tsx","../../src/components/icons/MailIcon.tsx","../../src/components/icons/SearchIcon.tsx","../../src/components/icons/SendIcon.tsx","../../src/components/icons/SidebarIcon.tsx","../../src/components/icons/SparklesIcon.tsx","../../src/components/icons/StopCircleIcon.tsx","../../src/components/icons/TerminalIcon.tsx","../../src/components/icons/UserPlusIcon.tsx","../../src/components/icons/WrenchIcon.tsx","../../src/components/Dropdown.tsx","../../src/hooks/useClickOutside.ts","../../src/components/Spinner.tsx","../../src/components/Tooltip.tsx","../../src/components/ChatHeader.tsx","../../src/components/ChatInput.tsx","../../src/components/ChatThinking.tsx","../../src/components/MarkdownMessage.tsx","../../src/components/ChatMessages.tsx","../../src/components/ChatWelcome.tsx","../../src/components/ChatPanel.tsx","../../src/components/ChatToggleButton.tsx"],"sourcesContent":["export function hexAlpha(hex: string, alpha: number): string {\n const a = Math.round(alpha * 255)\n .toString(16)\n .padStart(2, '0');\n return `${hex}${a}`;\n}\n\nexport const identity = (key: string) => key;\n","import type { ParsedAction, ProtocolContext } from './types';\n\n/**\n * Parse a Flowise-style SSE event into a normalized action.\n */\nexport function parseLegacyEvent(evt: Record<string, unknown>, ctx: ProtocolContext): ParsedAction {\n const eventType = evt.event as string | undefined;\n\n if (eventType === 'nextAgentFlow') {\n const data = evt.data as Record<string, unknown> | undefined;\n const nodeId = data?.nodeId as string | undefined;\n if (data?.status === 'INPROGRESS' && nodeId) {\n ctx.activeNodeId = nodeId;\n }\n return { action: 'noop' };\n }\n\n if (eventType === 'start') {\n return { action: 'noop' };\n }\n\n if (eventType === 'token') {\n const tokenData = ((evt.data as string) ?? '').replace(/<br\\s*\\/?>/g, '\\n');\n return { action: 'stream', content: tokenData };\n }\n\n if (eventType === 'agentReasoning') {\n const reasoning = evt.data as Record<string, unknown> | undefined;\n const usedTools = reasoning?.usedTools as Array<{ tool: string }> | undefined;\n if (usedTools?.length) {\n ctx.hasUsedTools = true;\n return { action: 'status', status: 'tool_start', tools: usedTools.map((t) => t.tool) };\n }\n if (ctx.hasUsedTools) {\n return { action: 'status', status: 'analyzing' };\n }\n return { action: 'status', status: 'thinking' };\n }\n\n if (eventType === 'usedTools') {\n ctx.hasUsedTools = true;\n const data = evt.data as Array<{ tool: string }> | undefined;\n const toolNames = Array.isArray(data) ? data.map((t) => t.tool) : [];\n return { action: 'status', status: 'tool_start', tools: toolNames };\n }\n\n if (eventType === 'metadata') {\n const data = evt.data as Record<string, unknown> | undefined;\n const chatId = data?.chatId as string | undefined;\n if (chatId) {\n return { action: 'set_chat_id', chatId };\n }\n return { action: 'noop' };\n }\n\n if (eventType === 'error') {\n return { action: 'error', content: (evt.data as string) || '' };\n }\n\n if (eventType === 'end') {\n return { action: 'done', content: '' };\n }\n\n return { action: 'noop' };\n}\n","import type { ParsedAction, ProtocolContext } from './types';\n\n/**\n * Parse an XTM One (REST) SSE event into a normalized action.\n */\nexport function parseRestEvent(evt: Record<string, unknown>, ctx: ProtocolContext): ParsedAction {\n const type = evt.type as string | undefined;\n\n if (type === 'error') {\n return { action: 'error', content: (evt.content as string) || '' };\n }\n\n if (type === 'status') {\n const st = evt.status as string;\n if (st === 'tool_done' || st === 'wind_down') {\n return { action: 'noop' };\n }\n if (st === 'streaming') {\n return { action: 'status', status: 'streaming' };\n }\n if (st === 'thinking_text') {\n return { action: 'status', status: 'thinking_text', thinkingContent: evt.content as string };\n }\n if (st === 'tool_start') {\n ctx.hasUsedTools = true;\n return { action: 'status', status: 'tool_start', tools: evt.tools as string[] | undefined };\n }\n if (st === 'thinking' && ctx.hasUsedTools) {\n return { action: 'status', status: 'analyzing' };\n }\n return { action: 'status', status: st, tools: evt.tools as string[] | undefined };\n }\n\n if (type === 'stream') {\n return { action: 'stream', content: evt.content as string };\n }\n\n if (type === 'done') {\n return {\n action: 'done',\n content: evt.content as string,\n conversationId: evt.conversation_id as string | undefined,\n toolNames: evt.tool_names as string[] | undefined,\n toolCallCount: evt.tool_call_count as number | undefined,\n iterations: evt.iterations as number | undefined,\n transferAgentId: evt.transfer_agent_id as string | undefined,\n transferAgentName: evt.transfer_agent_name as string | undefined,\n };\n }\n\n return { action: 'noop' };\n}\n","import type { ParsedAction, ProtocolContext } from './types';\n\n/**\n * AG-UI protocol event types.\n * @see https://github.com/ag-ui-protocol/ag-ui\n */\n\n/**\n * Parse an AG-UI protocol SSE event into a normalized action.\n *\n * AG-UI uses a Start/Content/End lifecycle for messages and tool calls.\n * We map these to the same internal actions used by the other protocols.\n */\nexport function parseAgUiEvent(evt: Record<string, unknown>, ctx: ProtocolContext): ParsedAction {\n const type = evt.type as string | undefined;\n\n // --- Run lifecycle ---\n\n if (type === 'RUN_STARTED') {\n return { action: 'status', status: 'thinking' };\n }\n\n if (type === 'RUN_FINISHED') {\n return { action: 'done', content: '' };\n }\n\n if (type === 'RUN_ERROR') {\n return { action: 'error', content: (evt.message as string) || 'Unknown error' };\n }\n\n // --- Step lifecycle ---\n\n if (type === 'STEP_STARTED') {\n const stepName = evt.stepName as string | undefined;\n return { action: 'status', status: stepName || 'thinking' };\n }\n\n if (type === 'STEP_FINISHED') {\n return { action: 'noop' };\n }\n\n // --- Text message streaming ---\n\n if (type === 'TEXT_MESSAGE_START') {\n return { action: 'status', status: 'streaming' };\n }\n\n if (type === 'TEXT_MESSAGE_CONTENT') {\n const delta = evt.delta as string;\n if (delta) {\n return { action: 'stream', content: delta };\n }\n return { action: 'noop' };\n }\n\n if (type === 'TEXT_MESSAGE_END') {\n return { action: 'noop' };\n }\n\n // TEXT_MESSAGE_CHUNK is a convenience event that combines Start+Content+End\n if (type === 'TEXT_MESSAGE_CHUNK') {\n const delta = evt.delta as string | undefined;\n if (delta) {\n return { action: 'stream', content: delta };\n }\n return { action: 'noop' };\n }\n\n // --- Tool call lifecycle ---\n\n if (type === 'TOOL_CALL_START') {\n ctx.hasUsedTools = true;\n const toolName = evt.toolCallName as string | undefined;\n return { action: 'status', status: 'tool_start', tools: toolName ? [toolName] : [] };\n }\n\n if (type === 'TOOL_CALL_ARGS') {\n // Tool arguments streaming — no UI equivalent, skip\n return { action: 'noop' };\n }\n\n if (type === 'TOOL_CALL_END') {\n return { action: 'status', status: 'analyzing' };\n }\n\n if (type === 'TOOL_CALL_RESULT') {\n // Tool result — no direct UI mapping, skip\n return { action: 'noop' };\n }\n\n if (type === 'TOOL_CALL_CHUNK') {\n // Convenience form — treat like TOOL_CALL_START if it has a name\n const toolName = evt.toolCallName as string | undefined;\n if (toolName) {\n ctx.hasUsedTools = true;\n return { action: 'status', status: 'tool_start', tools: [toolName] };\n }\n return { action: 'noop' };\n }\n\n // --- Reasoning / thinking ---\n\n if (type === 'REASONING_START' || type === 'REASONING_MESSAGE_START') {\n return { action: 'status', status: 'thinking' };\n }\n\n if (type === 'REASONING_MESSAGE_CONTENT' || type === 'REASONING_MESSAGE_CHUNK') {\n // Reasoning text — show as thinking status (content not surfaced to chat)\n return { action: 'status', status: 'thinking' };\n }\n\n if (type === 'REASONING_MESSAGE_END' || type === 'REASONING_END' || type === 'REASONING_ENCRYPTED_VALUE') {\n return { action: 'noop' };\n }\n\n // --- State management ---\n\n if (type === 'STATE_SNAPSHOT' || type === 'STATE_DELTA' || type === 'MESSAGES_SNAPSHOT') {\n // State sync — not mapped to chat UI currently\n return { action: 'noop' };\n }\n\n // --- Activity events ---\n\n if (type === 'ACTIVITY_SNAPSHOT' || type === 'ACTIVITY_DELTA') {\n return { action: 'noop' };\n }\n\n // --- Pass-through / custom ---\n\n if (type === 'RAW' || type === 'CUSTOM') {\n return { action: 'noop' };\n }\n\n return { action: 'noop' };\n}\n","import { useRef, useState } from 'react';\nimport type { AgentStatusState, ApiEndpoints, BackendType, ChatFile, ChatMessage } from '../types';\nimport type { ParsedAction, ProtocolContext } from './protocols';\nimport { parseAgUiEvent, parseLegacyEvent, parseRestEvent } from './protocols';\n\nconst STORAGE_KEY = 'filigranChatConversationId';\nconst LEGACY_CHAT_ID_KEY = 'filigranChatLegacyChatId';\n\n/** Maximum number of files that can be attached to a single message. */\nconst DEFAULT_MAX_FILE_COUNT = 10;\n/** Maximum total size of all attached files (50 MB). */\nconst DEFAULT_MAX_TOTAL_SIZE = 50 * 1024 * 1024;\n\ninterface UseChatOptions {\n apiBaseUrl: string;\n apiEndpoints?: ApiEndpoints;\n backendType?: BackendType;\n agentSlug: string | null | undefined;\n requestHeaders?: Record<string, string>;\n t: (key: string) => string;\n maxFileCount?: number;\n maxTotalSize?: number;\n}\n\nexport interface TransferredAgent {\n id: string;\n name: string;\n}\n\ninterface UseChatReturn {\n messages: ChatMessage[];\n inputValue: string;\n setInputValue: (value: string) => void;\n isLoading: boolean;\n agentStatus: AgentStatusState | null;\n attachedFiles: ChatFile[];\n conversationId: string | null;\n transferredAgent: TransferredAgent | null;\n historyLoadedRef: React.MutableRefObject<boolean>;\n handleFileAdd: (fileList: FileList | null) => void;\n handlePaste: (e: React.ClipboardEvent) => void;\n handleSendMessage: () => Promise<void>;\n handleNewChat: () => void;\n handleStopGenerating: () => void;\n setAttachedFiles: React.Dispatch<React.SetStateAction<ChatFile[]>>;\n setMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>;\n setConversationId: React.Dispatch<React.SetStateAction<string | null>>;\n}\n\nfunction getParser(backendType: BackendType): (evt: Record<string, unknown>, ctx: ProtocolContext) => ParsedAction {\n switch (backendType) {\n case 'legacy':\n return parseLegacyEvent;\n case 'ag-ui':\n return parseAgUiEvent;\n default:\n return parseRestEvent;\n }\n}\n\nfunction buildRequestBody(\n backendType: BackendType,\n content: string,\n opts: { legacyChatId: string | null; conversationId: string | null; agentSlug: string | null | undefined },\n): Record<string, unknown> {\n switch (backendType) {\n case 'legacy':\n return { question: content, chatId: opts.legacyChatId ?? undefined, streaming: true };\n case 'ag-ui':\n return {\n threadId: opts.conversationId ?? crypto.randomUUID(),\n runId: crypto.randomUUID(),\n messages: [{ id: crypto.randomUUID(), role: 'user', content }],\n tools: [],\n context: [],\n state: {},\n forwardedProps: opts.agentSlug ? { agentSlug: opts.agentSlug } : {},\n };\n default:\n return { content, conversation_id: opts.conversationId, agent_slug: opts.agentSlug };\n }\n}\n\nexport function useChat({\n apiBaseUrl,\n apiEndpoints,\n backendType = 'rest',\n agentSlug,\n requestHeaders,\n t,\n maxFileCount = DEFAULT_MAX_FILE_COUNT,\n maxTotalSize = DEFAULT_MAX_TOTAL_SIZE,\n}: UseChatOptions): UseChatReturn {\n const isLegacy = backendType === 'legacy';\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [inputValue, setInputValue] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [agentStatus, setAgentStatus] = useState<AgentStatusState | null>(null);\n const [conversationId, setConversationId] = useState<string | null>(() => {\n if (typeof window === 'undefined') return null;\n return localStorage.getItem(STORAGE_KEY);\n });\n const [attachedFiles, setAttachedFiles] = useState<ChatFile[]>([]);\n const [transferredAgent, setTransferredAgent] = useState<TransferredAgent | null>(null);\n const [legacyChatId, setLegacyChatId] = useState<string | null>(() => {\n if (typeof window === 'undefined') return null;\n return localStorage.getItem(LEGACY_CHAT_ID_KEY);\n });\n\n const historyLoadedRef = useRef(false);\n const abortControllerRef = useRef<AbortController | null>(null);\n const hasUsedToolsRef = useRef(false);\n // Ref mirror of conversationId — always current across async boundaries\n const conversationIdRef = useRef(conversationId);\n // Mutex to prevent concurrent session creation\n const creatingSessionRef = useRef<Promise<string | null> | null>(null);\n // Abort controller for in-flight file uploads (cancelled on new chat)\n const uploadAbortRef = useRef<AbortController>(new AbortController());\n\n // Guard invalid consumer values and keep deterministic limits.\n const effectiveMaxFileCount = Number.isFinite(maxFileCount) && maxFileCount > 0 ? Math.floor(maxFileCount) : DEFAULT_MAX_FILE_COUNT;\n const effectiveMaxTotalSize = Number.isFinite(maxTotalSize) && maxTotalSize > 0 ? maxTotalSize : DEFAULT_MAX_TOTAL_SIZE;\n\n // Determine message endpoint URL\n const getMessagesUrl = () => {\n if (isLegacy || apiEndpoints?.singleEndpoint) {\n return apiBaseUrl; // POST directly to base URL\n }\n return `${apiBaseUrl}${apiEndpoints?.messages ?? '/chat/messages'}`;\n };\n\n // Determine upload endpoint URL (null disables file upload proxying)\n const getUploadUrl = (): string | null => {\n if (isLegacy || apiEndpoints?.singleEndpoint || apiEndpoints?.upload === null) {\n return null;\n }\n return `${apiBaseUrl}${apiEndpoints?.upload ?? '/chat/upload'}`;\n };\n\n // Determine sessions endpoint URL\n const getSessionsUrl = (): string | null => {\n if (isLegacy || apiEndpoints?.singleEndpoint || apiEndpoints?.sessions === null) {\n return null;\n }\n return `${apiBaseUrl}${apiEndpoints?.sessions ?? '/chat/sessions'}`;\n };\n\n /**\n * Update conversationId in both React state and the ref mirror.\n */\n const updateConversationId = (id: string | null) => {\n conversationIdRef.current = id;\n setConversationId(id);\n if (id) {\n localStorage.setItem(STORAGE_KEY, id);\n } else {\n localStorage.removeItem(STORAGE_KEY);\n }\n };\n\n /**\n * Ensure a conversation exists. Uses a mutex so concurrent callers\n * (e.g. multiple files selected at once) share a single session creation.\n */\n const ensureConversation = async (slug: string | null | undefined): Promise<string | null> => {\n // Fast path: already have one\n if (conversationIdRef.current) return conversationIdRef.current;\n\n // If another call is already creating, wait for it\n if (creatingSessionRef.current) return creatingSessionRef.current;\n\n const sessionsUrl = getSessionsUrl();\n if (!sessionsUrl) return null;\n\n const promise = (async () => {\n try {\n const res = await fetch(sessionsUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(requestHeaders ?? {}) },\n body: JSON.stringify({ agent_slug: slug }),\n });\n if (!res.ok) return null;\n const data = await res.json();\n const convId = (data?.conversation_id as string) ?? null;\n if (convId) {\n updateConversationId(convId);\n }\n return convId;\n } catch {\n return null;\n } finally {\n creatingSessionRef.current = null;\n }\n })();\n\n creatingSessionRef.current = promise;\n return promise;\n };\n\n /**\n * Upload a single file to the backend and return its file_id.\n */\n const uploadSingleFile = async (file: File, convId: string, signal: AbortSignal): Promise<string> => {\n const uploadUrl = getUploadUrl()!;\n const formData = new FormData();\n formData.append('conversation_id', convId);\n formData.append('file', file, file.name);\n\n const uploadHeaders = requestHeaders\n ? Object.fromEntries(\n Object.entries(requestHeaders).filter(([k]) => {\n const key = k.toLowerCase();\n return key !== 'content-type';\n }),\n )\n : undefined;\n\n const res = await fetch(uploadUrl, {\n method: 'POST',\n headers: uploadHeaders,\n body: formData,\n signal,\n });\n if (!res.ok) {\n throw new Error(`File upload failed: ${res.status}`);\n }\n const data = await res.json();\n const ids: string[] = data.file_ids ?? [];\n if (ids.length === 0) throw new Error('No file_id returned');\n return ids[0];\n };\n\n /**\n * Handle file selection: validate limits, add files to state immediately,\n * then upload them in the background. Each file chip shows its upload status.\n */\n const handleFileAdd = (fileList: FileList | null) => {\n if (!fileList || fileList.length === 0 || !getUploadUrl()) return;\n\n // Build the list of accepted files outside the state updater (pure logic)\n const incoming = Array.from(fileList);\n\n // We need current state to check limits — use a ref-like approach:\n // read attachedFiles via a one-shot updater that returns prev unchanged,\n // then compute outside. Simpler: just compute optimistically and let the\n // updater do the final gating.\n\n // Pre-generate stable IDs and entries so side effects use the same IDs\n const candidates: { file: File; tempId: string }[] = incoming.map((file) => ({\n file,\n tempId: crypto.randomUUID(),\n }));\n\n // Update state (pure — no side effects)\n let accepted: { file: File; tempId: string }[] = [];\n setAttachedFiles((prev) => {\n const currentCount = prev.length;\n const currentSize = prev.reduce((sum, f) => sum + f.size, 0);\n\n const slotsAvailable = effectiveMaxFileCount - currentCount;\n if (slotsAvailable <= 0) return prev;\n\n let sizeLeft = effectiveMaxTotalSize - currentSize;\n const filtered: { file: File; tempId: string }[] = [];\n for (const c of candidates.slice(0, slotsAvailable)) {\n if (c.file.size <= sizeLeft) {\n filtered.push(c);\n sizeLeft -= c.file.size;\n }\n }\n if (filtered.length === 0) return prev;\n\n accepted = filtered;\n\n const newEntries: ChatFile[] = filtered.map(({ file, tempId }) => ({\n name: file.name,\n type: file.type,\n size: file.size,\n rawFile: file,\n uploadStatus: 'pending' as const,\n fileId: tempId,\n }));\n\n return [...prev, ...newEntries];\n });\n\n // Launch uploads OUTSIDE the state updater (side effects)\n // Use setTimeout(0) to ensure state has settled after the updater\n setTimeout(() => {\n const signal = uploadAbortRef.current.signal;\n for (const { file, tempId } of accepted) {\n (async () => {\n try {\n const convId = await ensureConversation(agentSlug);\n if (!convId) {\n setAttachedFiles((p) => p.map((f) => (f.fileId === tempId ? { ...f, uploadStatus: 'error' } : f)));\n return;\n }\n const fileId = await uploadSingleFile(file, convId, signal);\n setAttachedFiles((p) => p.map((f) => (f.fileId === tempId ? { ...f, fileId, uploadStatus: 'done' } : f)));\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setAttachedFiles((p) => p.map((f) => (f.fileId === tempId ? { ...f, uploadStatus: 'error' } : f)));\n }\n })();\n }\n }, 0);\n };\n\n const handlePaste = (e: React.ClipboardEvent) => {\n const { files } = e.clipboardData;\n if (files.length > 0) {\n e.preventDefault();\n handleFileAdd(files);\n }\n };\n\n const handleSendMessage = async () => {\n if ((!inputValue.trim() && attachedFiles.length === 0) || isLoading) return;\n const content = inputValue.trim();\n\n const userMsg: ChatMessage = {\n id: crypto.randomUUID(),\n role: 'user',\n content,\n timestamp: new Date(),\n files: attachedFiles.length > 0 ? [...attachedFiles] : undefined,\n };\n setMessages((prev) => [...prev, userMsg]);\n setInputValue('');\n // Clear attachment chips after sending so the input returns to a clean state.\n setAttachedFiles([]);\n setIsLoading(true);\n setAgentStatus({ status: 'thinking' });\n hasUsedToolsRef.current = false;\n\n const assistantId = crypto.randomUUID();\n setMessages((prev) => [...prev, { id: assistantId, role: 'assistant', content: '', timestamp: new Date() }]);\n\n try {\n const controller = new AbortController();\n abortControllerRef.current = controller;\n\n // Collect file_ids from already-uploaded files (uploaded eagerly on selection)\n const fileIds = (userMsg.files ?? [])\n .filter((f) => f.uploadStatus === 'done' && f.fileId)\n .map((f) => f.fileId!);\n\n // Step 1: Send the message (with file_ids if files were uploaded)\n // Use conversationIdRef to get the latest value (may have been set by eager upload)\n const requestBody = buildRequestBody(backendType, content, {\n legacyChatId,\n conversationId: conversationIdRef.current,\n agentSlug,\n });\n if (fileIds.length > 0) {\n (requestBody as Record<string, unknown>).file_ids = fileIds;\n }\n\n setAgentStatus({ status: 'thinking' });\n\n const res = await fetch(getMessagesUrl(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(requestHeaders ?? {}) },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n if (!res.ok || !res.body) {\n setMessages((prev) =>\n prev.map((m) => (m.id === assistantId ? { ...m, content: t('Unable to connect. Please check the configuration.') } : m)),\n );\n return;\n }\n\n const parseEvent = getParser(backendType);\n const ctx: ProtocolContext = { hasUsedTools: false, activeNodeId: '' };\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let accumulated = '';\n let doneReceived = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n for (const rawLine of lines) {\n const line = rawLine.replace(/\\r$/, '');\n if (!line.startsWith('data:')) continue;\n const jsonStr = line.startsWith('data: ') ? line.slice(6) : line.slice(5);\n try {\n const evt = JSON.parse(jsonStr) as Record<string, unknown>;\n const parsed: ParsedAction = parseEvent(evt, ctx);\n\n // Sync ref → context for cross-event tracking\n ctx.hasUsedTools = ctx.hasUsedTools || hasUsedToolsRef.current;\n\n switch (parsed.action) {\n case 'status':\n if (parsed.status === 'tool_start') hasUsedToolsRef.current = true;\n if (parsed.status === 'thinking_text') {\n setAgentStatus((prev) => ({\n ...prev,\n status: prev?.status ?? 'thinking',\n thinkingContent: (prev?.thinkingContent ?? '') + (parsed.thinkingContent ?? ''),\n }));\n } else {\n setAgentStatus((prev) => ({\n status: parsed.status,\n tools: parsed.tools,\n thinkingContent: prev?.thinkingContent,\n }));\n }\n break;\n\n case 'stream':\n accumulated += parsed.content;\n setAgentStatus((prev) => ({ status: 'streaming', thinkingContent: prev?.thinkingContent }));\n setMessages((prev) => prev.map((m) => (m.id === assistantId ? { ...m, content: accumulated } : m)));\n break;\n\n case 'done':\n doneReceived = true;\n if (parsed.conversationId) {\n updateConversationId(parsed.conversationId);\n }\n if (parsed.transferAgentId && parsed.transferAgentName) {\n setTransferredAgent({ id: parsed.transferAgentId, name: parsed.transferAgentName });\n }\n setMessages((prev) =>\n prev.map((m) =>\n m.id === assistantId\n ? {\n ...m,\n content: parsed.content || accumulated,\n toolNames: parsed.toolNames,\n toolCallCount: parsed.toolCallCount,\n iterations: parsed.iterations,\n }\n : m,\n ),\n );\n break;\n\n case 'error':\n setMessages((prev) =>\n prev.map((m) =>\n m.id === assistantId ? { ...m, content: parsed.content || t('Unable to connect. Please check the configuration.') } : m,\n ),\n );\n return;\n\n case 'set_chat_id':\n setLegacyChatId(parsed.chatId);\n localStorage.setItem(LEGACY_CHAT_ID_KEY, parsed.chatId);\n break;\n\n case 'noop':\n break;\n }\n\n // Keep ref in sync with context\n hasUsedToolsRef.current = ctx.hasUsedTools;\n } catch {\n /* skip malformed SSE */\n }\n }\n }\n if (accumulated && !doneReceived) {\n setMessages((prev) => prev.map((m) => (m.id === assistantId ? { ...m, content: accumulated || 'No response.' } : m)));\n }\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setMessages((prev) =>\n prev.map((m) => (m.id === assistantId ? { ...m, content: t('Sorry, an error occurred. Please try again.') } : m)),\n );\n } finally {\n abortControllerRef.current = null;\n setIsLoading(false);\n setAgentStatus(null);\n hasUsedToolsRef.current = false;\n }\n };\n\n const handleNewChat = () => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n // Cancel any in-flight file uploads\n uploadAbortRef.current.abort();\n uploadAbortRef.current = new AbortController();\n creatingSessionRef.current = null;\n setMessages([]);\n setInputValue('');\n setAttachedFiles([]);\n setIsLoading(false);\n setAgentStatus(null);\n setTransferredAgent(null);\n hasUsedToolsRef.current = false;\n historyLoadedRef.current = false;\n if (isLegacy) {\n setLegacyChatId(null);\n localStorage.removeItem(LEGACY_CHAT_ID_KEY);\n } else {\n updateConversationId(null);\n }\n };\n\n const handleStopGenerating = () => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n setIsLoading(false);\n setAgentStatus(null);\n hasUsedToolsRef.current = false;\n setMessages((prev) => prev.filter((m) => !(m.role === 'assistant' && !m.content)));\n };\n\n return {\n messages,\n inputValue,\n setInputValue,\n isLoading,\n agentStatus,\n attachedFiles,\n conversationId,\n transferredAgent,\n historyLoadedRef,\n handleFileAdd,\n handlePaste,\n handleSendMessage,\n handleNewChat,\n handleStopGenerating,\n setAttachedFiles,\n setMessages,\n setConversationId,\n };\n}\n","import { useEffect, useState } from 'react';\nimport type { ApiEndpoints, BackendType, XtmAgent } from '../types';\n\nconst STORAGE_AGENT_KEY = 'filigranChatAgentSlug';\n\ninterface UseAgentsOptions {\n apiBaseUrl: string;\n apiEndpoints?: ApiEndpoints;\n backendType?: BackendType;\n requestHeaders?: Record<string, string>;\n}\n\ninterface UseAgentsReturn {\n agents: XtmAgent[];\n selectedAgent: XtmAgent | null;\n setSelectedAgent: React.Dispatch<React.SetStateAction<XtmAgent | null>>;\n agentMenuOpen: boolean;\n setAgentMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;\n handleSwitchAgent: (agent: XtmAgent, onSwitch?: () => void) => void;\n}\n\nexport function useAgents({ apiBaseUrl, apiEndpoints, backendType = 'rest', requestHeaders }: UseAgentsOptions): UseAgentsReturn {\n const [agents, setAgents] = useState<XtmAgent[]>([]);\n const [selectedAgent, setSelectedAgent] = useState<XtmAgent | null>(null);\n const [agentMenuOpen, setAgentMenuOpen] = useState(false);\n\n useEffect(() => {\n // Skip agents fetch if disabled, using single endpoint mode, or legacy backend\n if (apiEndpoints?.agents === null || apiEndpoints?.singleEndpoint || backendType === 'legacy') {\n return;\n }\n const agentsUrl = `${apiBaseUrl}${apiEndpoints?.agents ?? '/chat/agents'}`;\n fetch(agentsUrl, { headers: requestHeaders })\n .then((res) => (res.ok ? res.json() : []))\n .then((data: XtmAgent[]) => {\n setAgents(data);\n if (data.length > 0 && !selectedAgent) {\n const savedSlug = localStorage.getItem(STORAGE_AGENT_KEY);\n const match = savedSlug ? data.find((a) => a.slug === savedSlug) : null;\n setSelectedAgent(match || data[0]);\n }\n })\n .catch(() => {});\n }, [apiBaseUrl, apiEndpoints, backendType, requestHeaders]);\n\n const handleSwitchAgent = (agent: XtmAgent, onSwitch?: () => void) => {\n if (agent.id === selectedAgent?.id) {\n setAgentMenuOpen(false);\n return;\n }\n setSelectedAgent(agent);\n if (agent.slug) localStorage.setItem(STORAGE_AGENT_KEY, agent.slug);\n setAgentMenuOpen(false);\n onSwitch?.();\n };\n\n return {\n agents,\n selectedAgent,\n setSelectedAgent,\n agentMenuOpen,\n setAgentMenuOpen,\n handleSwitchAgent,\n };\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { ChatMode } from '../types';\n\nconst SIDEBAR_WIDTH = 400;\nconst SIDEBAR_WIDTH_STORAGE_KEY = 'filigranChatSidebarWidth';\nconst MAX_SIDEBAR_RATIO = 0.4;\n\ninterface UseSidebarResizeOptions {\n mode: ChatMode;\n resizable: boolean;\n onWidthChange?: (width: number) => void;\n onResizeStart?: () => void;\n onResizeEnd?: () => void;\n}\n\ninterface UseSidebarResizeReturn {\n sidebarWidth: number;\n handleResizeStart: (e: React.MouseEvent) => void;\n defaultWidth: number;\n isResizing: boolean;\n}\n\nexport function useSidebarResize({\n mode,\n resizable,\n onWidthChange,\n onResizeStart,\n onResizeEnd,\n}: UseSidebarResizeOptions): UseSidebarResizeReturn {\n const [sidebarWidth, setSidebarWidth] = useState<number>(() => {\n if (typeof window === 'undefined') return SIDEBAR_WIDTH;\n const stored = localStorage.getItem(SIDEBAR_WIDTH_STORAGE_KEY);\n if (stored) {\n const parsed = parseInt(stored, 10);\n if (!Number.isNaN(parsed) && parsed >= SIDEBAR_WIDTH) return parsed;\n }\n return SIDEBAR_WIDTH;\n });\n const [isResizing, setIsResizing] = useState(false);\n\n const isResizingRef = useRef(false);\n const sidebarWidthRef = useRef(sidebarWidth);\n sidebarWidthRef.current = sidebarWidth;\n const onWidthChangeRef = useRef(onWidthChange);\n onWidthChangeRef.current = onWidthChange;\n const onResizeEndRef = useRef(onResizeEnd);\n onResizeEndRef.current = onResizeEnd;\n\n // Notify parent of sidebar width when entering sidebar mode\n useEffect(() => {\n if (mode === 'sidebar' && resizable) {\n onWidthChangeRef.current?.(sidebarWidthRef.current);\n }\n }, [mode, resizable]);\n\n // Resize event handlers\n useEffect(() => {\n if (mode !== 'sidebar' || !resizable) return undefined;\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!isResizingRef.current) return;\n e.preventDefault();\n const newWidth = window.innerWidth - e.clientX;\n const maxWidth = window.innerWidth * MAX_SIDEBAR_RATIO;\n const clamped = Math.min(Math.max(newWidth, SIDEBAR_WIDTH), maxWidth);\n setSidebarWidth(clamped);\n sidebarWidthRef.current = clamped;\n onWidthChangeRef.current?.(clamped);\n };\n\n const handleMouseUp = () => {\n if (!isResizingRef.current) return;\n isResizingRef.current = false;\n setIsResizing(false);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n localStorage.setItem(SIDEBAR_WIDTH_STORAGE_KEY, String(sidebarWidthRef.current));\n onResizeEndRef.current?.();\n };\n\n const handleWindowResize = () => {\n const maxWidth = window.innerWidth * MAX_SIDEBAR_RATIO;\n if (sidebarWidthRef.current > maxWidth) {\n const clamped = Math.max(maxWidth, SIDEBAR_WIDTH);\n setSidebarWidth(clamped);\n sidebarWidthRef.current = clamped;\n onWidthChangeRef.current?.(clamped);\n }\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n window.addEventListener('resize', handleWindowResize);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n window.removeEventListener('resize', handleWindowResize);\n };\n }, [mode, resizable]);\n\n const handleResizeStart = (e: React.MouseEvent) => {\n e.preventDefault();\n isResizingRef.current = true;\n setIsResizing(true);\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n onResizeStart?.();\n };\n\n return {\n sidebarWidth,\n handleResizeStart,\n defaultWidth: SIDEBAR_WIDTH,\n isResizing,\n };\n}\n","import type { IconProps } from '../../types';\n\nexport const AttachFileIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const BrainIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z\" />\n <path d=\"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z\" />\n <path d=\"M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4\" />\n <path d=\"M17.599 6.5a3 3 0 0 0 .399-1.375\" />\n <path d=\"M6.003 5.125A3 3 0 0 0 6.401 6.5\" />\n <path d=\"M3.477 10.896a4 4 0 0 1 .585-.396\" />\n <path d=\"M19.938 10.5a4 4 0 0 1 .585.396\" />\n <path d=\"M6 18a4 4 0 0 1-1.967-.516\" />\n <path d=\"M19.967 17.484A4 4 0 0 1 18 18\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const CheckIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const ChevronDownIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const CloseIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const CopyIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const DatabaseIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" />\n <path d=\"M3 5V19A9 3 0 0 0 21 19V5\" />\n <path d=\"M3 12A9 3 0 0 0 21 12\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const DefaultLogoIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" className={className}>\n <path d=\"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const EditIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M12 20h9\" />\n <path d=\"M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const ExternalLinkIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M15 3h6v6\" />\n <path d=\"M10 14 21 3\" />\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const FileIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const FloatingIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M11 13H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h7\" />\n <rect width=\"12\" height=\"12\" x=\"10\" y=\"10\" rx=\"2\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const FullscreenExitIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3\" />\n <path d=\"M21 8h-3a2 2 0 0 1-2-2V3\" />\n <path d=\"M3 16h3a2 2 0 0 0 2 2v3\" />\n <path d=\"M16 21v-3a2 2 0 0 1 2-2h3\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const FullscreenIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3\" />\n <path d=\"M21 8V5a2 2 0 0 0-2-2h-3\" />\n <path d=\"M3 16v3a2 2 0 0 0 2 2h3\" />\n <path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const GlobeIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\" />\n <path d=\"M2 12h20\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const InfoIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const MailIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n <path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const SearchIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const SendIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"m22 2-7 20-4-9-9-4Z\" />\n <path d=\"m22 2-11 11\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const SidebarIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\n <path d=\"M15 3v18\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const SparklesIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const StopCircleIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <rect width=\"6\" height=\"6\" x=\"9\" y=\"9\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const TerminalIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <polyline points=\"4 17 10 11 4 5\" />\n <line x1=\"12\" x2=\"20\" y1=\"19\" y2=\"19\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const UserPlusIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2\" />\n <circle cx=\"9\" cy=\"7\" r=\"4\" />\n <line x1=\"19\" x2=\"19\" y1=\"8\" y2=\"14\" />\n <line x1=\"22\" x2=\"16\" y1=\"11\" y2=\"11\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const WrenchIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\n <path d=\"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z\" />\n </svg>\n);\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useClickOutside } from '../hooks/useClickOutside';\n\ninterface DropdownProps {\n open: boolean;\n onClose: () => void;\n anchorRef: React.RefObject<HTMLElement | null>;\n placement?: 'bottom-start' | 'bottom-end';\n width?: number;\n children: React.ReactNode;\n}\n\nfunction findChatbotRoot(el: HTMLElement | null): HTMLElement {\n let node = el;\n while (node) {\n if (node.classList.contains('filigran-chatbot')) return node;\n node = node.parentElement;\n }\n return document.body;\n}\n\nexport const Dropdown = ({ open, onClose, anchorRef, placement = 'bottom-start', width = 280, children }: DropdownProps) => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [pos, setPos] = useState({ top: 0, left: 0 });\n\n const stableOnClose = useCallback(() => onClose(), [onClose]);\n useClickOutside(panelRef, stableOnClose, open);\n\n useEffect(() => {\n if (!open || !anchorRef.current) return;\n const rect = anchorRef.current.getBoundingClientRect();\n const left = placement === 'bottom-end' ? rect.right - width : rect.left;\n setPos({ top: rect.bottom + 4, left });\n }, [open, anchorRef, placement, width]);\n\n if (!open) return null;\n\n const portalTarget = findChatbotRoot(anchorRef.current);\n\n return createPortal(\n <div\n ref={panelRef}\n className=\"fixed z-[10000] rounded-[10px] overflow-hidden border border-gray-200 dark:border-white/10 bg-white dark:bg-[#2a2a3e] shadow-xl\"\n style={{ top: pos.top, left: pos.left, width }}\n >\n {children}\n </div>,\n portalTarget,\n );\n};\n","import { useEffect, type RefObject } from 'react';\n\nexport function useClickOutside(ref: RefObject<HTMLElement | null>, handler: () => void, active = true) {\n useEffect(() => {\n if (!active) return undefined;\n const listener = (e: MouseEvent | TouchEvent) => {\n if (!ref.current || ref.current.contains(e.target as Node)) return;\n handler();\n };\n document.addEventListener('mousedown', listener);\n document.addEventListener('touchstart', listener);\n return () => {\n document.removeEventListener('mousedown', listener);\n document.removeEventListener('touchstart', listener);\n };\n }, [ref, handler, active]);\n}\n","interface SpinnerProps {\n size?: number;\n className?: string;\n}\n\nexport const Spinner = ({ size = 16, className = '' }: SpinnerProps) => (\n <div\n className={`animate-spin rounded-full border-2 border-current/20 border-t-[var(--chat-accent)] ${className}`}\n style={{ width: size, height: size }}\n />\n);\n","import { useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\ninterface TooltipProps {\n title: string;\n children: React.ReactElement;\n}\n\nfunction findChatbotRoot(el: HTMLElement | null): HTMLElement {\n let node = el;\n while (node) {\n if (node.classList.contains('filigran-chatbot')) return node;\n node = node.parentElement;\n }\n return document.body;\n}\n\nexport const Tooltip = ({ title, children }: TooltipProps) => {\n const ref = useRef<HTMLSpanElement>(null);\n const [show, setShow] = useState(false);\n const [pos, setPos] = useState({ top: 0, left: 0 });\n\n if (!title) return children;\n\n const handleEnter = () => {\n if (!ref.current) return;\n const rect = ref.current.getBoundingClientRect();\n setPos({\n top: rect.top - 4,\n left: rect.left + rect.width / 2,\n });\n setShow(true);\n };\n\n return (\n <span\n ref={ref}\n className=\"inline-flex\"\n onMouseEnter={handleEnter}\n onMouseLeave={() => setShow(false)}\n >\n {children}\n {show &&\n createPortal(\n <span\n className=\"pointer-events-none fixed z-[10001] -translate-x-1/2 -translate-y-full whitespace-nowrap rounded-md bg-gray-900 dark:bg-gray-100 px-2 py-1 text-xs text-white dark:text-gray-900 shadow-lg\"\n style={{ top: pos.top, left: pos.left }}\n role=\"tooltip\"\n >\n {title}\n </span>,\n findChatbotRoot(ref.current),\n )}\n </span>\n );\n};\n","import { useRef } from 'react';\nimport type { ChatMode, XtmAgent } from '../types';\nimport {\n ChevronDownIcon,\n CloseIcon,\n EditIcon,\n ExternalLinkIcon,\n FloatingIcon,\n FullscreenExitIcon,\n FullscreenIcon,\n SidebarIcon,\n UserPlusIcon,\n} from './icons';\nimport { Dropdown } from './Dropdown';\nimport { Spinner } from './Spinner';\nimport { Tooltip } from './Tooltip';\n\ninterface ChatHeaderProps {\n mode: ChatMode;\n agentName: string;\n agents: XtmAgent[];\n selectedAgent: XtmAgent | null;\n transferredFrom?: string;\n agentMenuOpen: boolean;\n onAgentMenuToggle: () => void;\n onAgentMenuClose: () => void;\n onSwitchAgent: (agent: XtmAgent) => void;\n modeMenuOpen: boolean;\n onModeMenuToggle: () => void;\n onModeMenuClose: () => void;\n onModeChange: (mode: ChatMode) => void;\n onNewChat: () => void;\n onClose: () => void;\n logoIcon: React.ReactNode;\n agentDashboardUrl?: string;\n t: (key: string) => string;\n}\n\nconst modeOptions: { mode: ChatMode; label: string; getIcon: (p: { size: number; className: string }) => React.ReactNode }[] = [\n { mode: 'floating', label: 'Floating', getIcon: (p) => <FloatingIcon {...p} /> },\n { mode: 'sidebar', label: 'Sidebar', getIcon: (p) => <SidebarIcon {...p} /> },\n { mode: 'fullscreen', label: 'Full screen', getIcon: (p) => <FullscreenIcon {...p} /> },\n];\n\nexport const ChatHeader = ({\n mode,\n agentName,\n agents,\n selectedAgent,\n transferredFrom,\n agentMenuOpen,\n onAgentMenuToggle,\n onAgentMenuClose,\n onSwitchAgent,\n modeMenuOpen,\n onModeMenuToggle,\n onModeMenuClose,\n onModeChange,\n onNewChat,\n onClose,\n logoIcon,\n agentDashboardUrl,\n t,\n}: ChatHeaderProps) => {\n const agentAnchorRef = useRef<HTMLButtonElement>(null);\n const modeAnchorRef = useRef<HTMLButtonElement>(null);\n\n const CurrentModeIcon = mode === 'sidebar' ? SidebarIcon : mode === 'fullscreen' ? FullscreenExitIcon : FloatingIcon;\n\n return (\n <div className={`flex items-center px-3 py-2 min-h-[48px] border-b border-gray-200 dark:border-white/10 bg-gradient-to-br from-[var(--chat-accent-dark)]/[0.13] to-[var(--chat-accent)]/[0.07] ${mode === 'floating' ? 'rounded-t-xl' : ''}`}>\n <div className=\"min-w-0\">\n <button\n ref={agentAnchorRef}\n type=\"button\"\n onClick={onAgentMenuToggle}\n className=\"flex items-center gap-1.5 text-sm font-semibold text-gray-900 dark:text-white px-2 py-1 rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 transition-colors\"\n >\n <span className=\"flex items-center text-[var(--chat-accent)] [&>svg]:w-[18px] [&>svg]:h-[18px]\">{logoIcon}</span>\n <span>{agentName}</span>\n <ChevronDownIcon size={16} className=\"text-gray-400 dark:text-white/30\" />\n </button>\n {transferredFrom && (\n <div className=\"pl-10 pr-2 text-[0.6rem] font-normal text-gray-400 dark:text-white/30\">\n {t('Transferred from')} {transferredFrom}\n </div>\n )}\n </div>\n\n <Dropdown open={agentMenuOpen} onClose={onAgentMenuClose} anchorRef={agentAnchorRef} width={280}>\n <span className=\"block px-4 pt-3 pb-1 text-[0.68rem] tracking-[1px] uppercase text-gray-400 dark:text-white/40\">\n {t('Switch to another agent')}\n </span>\n {agents.length === 0 && (\n <div className=\"px-4 py-2\">\n <Spinner size={16} />\n </div>\n )}\n <div>\n {agents.map((agent) => (\n <button\n key={agent.id}\n type=\"button\"\n onClick={() => onSwitchAgent(agent)}\n className={`w-full flex items-center gap-2 px-4 py-1.5 text-left hover:bg-gray-100 dark:hover:bg-white/10 transition-colors ${\n agent.id === selectedAgent?.id ? 'bg-[var(--chat-accent)]/10' : ''\n }`}\n >\n <div className=\"w-7 h-7 rounded-full flex items-center justify-center shrink-0 bg-gradient-to-br from-[var(--chat-accent)]/20 to-[var(--chat-accent)]/5\">\n <span className=\"text-[var(--chat-accent)] [&>svg]:w-4 [&>svg]:h-4\">{logoIcon}</span>\n </div>\n <div className=\"min-w-0\">\n <div className=\"text-[0.8125rem] font-medium text-gray-900 dark:text-white truncate\">{agent.name}</div>\n {agent.description && <div className=\"text-[0.7rem] text-gray-500 dark:text-white/40 truncate\">{agent.description}</div>}\n </div>\n </button>\n ))}\n </div>\n <div className=\"h-px bg-gray-200 dark:bg-white/10 mx-2\" />\n <div>\n {agentDashboardUrl && (\n <button\n type=\"button\"\n onClick={() => {\n onAgentMenuClose();\n window.open(`${agentDashboardUrl}/agents`, '_blank');\n }}\n className=\"w-full flex items-center gap-2 px-4 py-1.5 text-left hover:bg-gray-100 dark:hover:bg-white/10 transition-colors\"\n >\n <ExternalLinkIcon size={18} className=\"text-gray-400 dark:text-white/40 shrink-0\" />\n <span className=\"text-[0.8125rem] text-gray-700 dark:text-white/70\">{t('Browse agents')}</span>\n </button>\n )}\n {agentDashboardUrl && (\n <button\n type=\"button\"\n onClick={() => {\n onAgentMenuClose();\n window.open(`${agentDashboardUrl}/agents/new`, '_blank');\n }}\n className=\"w-full flex items-center gap-2 px-4 py-1.5 text-left hover:bg-gray-100 dark:hover:bg-white/10 transition-colors\"\n >\n <UserPlusIcon size={18} className=\"text-gray-400 dark:text-white/40 shrink-0\" />\n <span className=\"text-[0.8125rem] text-gray-700 dark:text-white/70\">{t('Create agent')}</span>\n </button>\n )}\n </div>\n </Dropdown>\n\n <div className=\"flex-1\" />\n\n <Tooltip title={t('New chat')}>\n <button\n type=\"button\"\n onClick={onNewChat}\n className=\"w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 text-gray-500 dark:text-white/40 hover:text-gray-700 dark:hover:text-white/70 transition-colors\"\n >\n <EditIcon size={18} />\n </button>\n </Tooltip>\n\n <Tooltip title={t('Switch view')}>\n <button\n ref={modeAnchorRef}\n type=\"button\"\n onClick={onModeMenuToggle}\n className=\"w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 text-gray-500 dark:text-white/40 hover:text-gray-700 dark:hover:text-white/70 transition-colors\"\n >\n <CurrentModeIcon size={18} />\n </button>\n </Tooltip>\n\n <Dropdown open={modeMenuOpen} onClose={onModeMenuClose} anchorRef={modeAnchorRef} placement=\"bottom-end\" width={180}>\n <span className=\"block px-4 pt-3 pb-1 text-[0.68rem] tracking-[1px] uppercase text-gray-400 dark:text-white/40\">{t('Switch to')}</span>\n <div className=\"pb-1\">\n {modeOptions.map((opt) => (\n <button\n key={opt.mode}\n type=\"button\"\n onClick={() => {\n onModeChange(opt.mode);\n onModeMenuClose();\n }}\n className={`w-full flex items-center gap-3 px-4 py-1 text-left hover:bg-gray-100 dark:hover:bg-white/10 transition-colors ${\n mode === opt.mode ? 'bg-[var(--chat-accent)]/10' : ''\n }`}\n >\n {opt.getIcon({ size: 18, className: 'text-gray-400 dark:text-white/40' })}\n <span className=\"text-[0.8125rem] text-gray-700 dark:text-white/70\">{t(opt.label)}</span>\n </button>\n ))}\n </div>\n </Dropdown>\n\n <Tooltip title={t('Close')}>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 text-gray-500 dark:text-white/40 hover:text-gray-700 dark:hover:text-white/70 transition-colors\"\n >\n <CloseIcon size={18} />\n </button>\n </Tooltip>\n </div>\n );\n};\n","import { useRef, type KeyboardEvent } from 'react';\nimport type { ChatFile, ChatMode } from '../types';\nimport { AttachFileIcon, FileIcon, SendIcon, StopCircleIcon } from './icons';\nimport { Tooltip } from './Tooltip';\n\ninterface ChatInputProps {\n inputValue: string;\n onInputChange: (value: string) => void;\n onSend: () => void;\n onStop: () => void;\n isLoading: boolean;\n attachedFiles?: ChatFile[];\n onFileAdd?: (files: FileList | null) => void;\n onFileRemove?: (index: number) => void;\n onPaste?: (e: React.ClipboardEvent) => void;\n t: (key: string) => string;\n mode?: ChatMode;\n separatorColor?: string;\n}\n\nexport const ChatInput = ({\n inputValue,\n onInputChange,\n onSend,\n onStop,\n isLoading,\n attachedFiles = [],\n onFileAdd,\n onFileRemove,\n onPaste,\n t,\n mode,\n separatorColor,\n}: ChatInputProps) => {\n const fileInputRef = useRef<HTMLInputElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n onSend();\n }\n };\n\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n onInputChange(e.target.value);\n const el = e.target;\n el.style.height = 'auto';\n el.style.height = `${Math.min(el.scrollHeight, 120)}px`;\n };\n\n const isFileManagementEnabled = Boolean(onFileAdd && onFileRemove && onPaste);\n const hasContent = inputValue.trim() || (isFileManagementEnabled && attachedFiles.length > 0);\n const hasFilesUploading = isFileManagementEnabled && attachedFiles.some((f) => f.uploadStatus === 'pending');\n const canSend = hasContent && !hasFilesUploading;\n\n return (\n <div\n className={`px-4 py-3 border-t border-gray-200 dark:border-white/10 ${mode === 'floating' ? 'rounded-b-xl' : ''}`}\n style={separatorColor ? { borderTopColor: separatorColor, borderTopWidth: 1 } : undefined}\n >\n {isFileManagementEnabled && attachedFiles.length > 0 && (\n <div className=\"flex gap-1.5 flex-wrap mb-2\">\n {attachedFiles.map((f, i) => (\n <span\n key={i}\n className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full border text-[0.7rem] ${\n f.uploadStatus === 'error'\n ? 'border-red-300 dark:border-red-500/30 text-red-500 dark:text-red-400'\n : f.uploadStatus === 'pending'\n ? 'border-gray-200 dark:border-white/10 text-gray-400 dark:text-white/40'\n : 'border-gray-200 dark:border-white/10 text-gray-600 dark:text-white/60'\n }`}\n >\n {f.uploadStatus === 'pending' ? (\n <span className=\"w-3.5 h-3.5 border border-current/30 border-t-current rounded-full animate-spin\" />\n ) : (\n <FileIcon size={14} />\n )}\n {f.name}\n {f.uploadStatus === 'error' && <span className=\"text-red-400 text-[0.6rem]\">✕</span>}\n <button\n type=\"button\"\n onClick={() => onFileRemove?.(i)}\n className=\"ml-0.5 text-gray-400 dark:text-white/30 hover:text-gray-600 dark:hover:text-white/60\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n )}\n\n <div className=\"flex items-center border border-gray-200 dark:border-white/10 rounded-xl px-2 py-1 transition-colors focus-within:border-[var(--chat-accent)]\">\n {isFileManagementEnabled && (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n hidden\n onChange={(e) => {\n onFileAdd?.(e.target.files);\n e.target.value = '';\n }}\n />\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n className=\"w-8 h-8 flex items-center justify-center shrink-0 rounded-lg text-gray-400 dark:text-white/30 hover:bg-gray-100 dark:hover:bg-white/10 mr-0.5 transition-colors\"\n >\n <AttachFileIcon size={18} />\n </button>\n </>\n )}\n <textarea\n ref={textareaRef}\n placeholder={t('Ask a question...')}\n value={inputValue}\n onChange={handleInput}\n onKeyDown={handleKeyDown}\n onPaste={onPaste}\n rows={1}\n className=\"flex-1 bg-transparent border-none outline-hidden resize-none text-[0.8125rem] py-1.5 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-white/30 filigran-chat-scrollable\"\n style={{ maxHeight: 120 }}\n />\n <Tooltip title={isLoading ? t('Stop generating') : hasFilesUploading ? t('Files uploading...') : ''}>\n <button\n type=\"button\"\n onClick={isLoading ? onStop : onSend}\n disabled={!isLoading && !canSend}\n className={`p-1.5 rounded-lg w-8 h-8 flex items-center justify-center transition-all duration-150 ${\n isLoading\n ? 'text-red-500 bg-red-500/10 hover:bg-red-500/20'\n : canSend\n ? 'text-[var(--chat-accent)] bg-[var(--chat-accent)]/10 hover:bg-[var(--chat-accent)]/20'\n : 'text-gray-300 dark:text-white/20 cursor-not-allowed'\n }`}\n >\n {isLoading ? <StopCircleIcon size={18} /> : <SendIcon size={18} />}\n </button>\n </Tooltip>\n </div>\n\n <p className=\"text-center text-[0.65rem] text-gray-400 dark:text-white/30 mt-1.5 opacity-70\">{t('Uses AI. Verify results.')}</p>\n </div>\n );\n};\n","import { useEffect, useRef } from 'react';\nimport type { AgentStatusState, IconProps } from '../types';\nimport { BrainIcon, DatabaseIcon, ExternalLinkIcon, GlobeIcon, MailIcon, SearchIcon, SparklesIcon, TerminalIcon, UserPlusIcon, WrenchIcon } from './icons';\n\ninterface ChatThinkingProps {\n agentStatus: AgentStatusState | null;\n logoIcon?: React.ReactNode;\n t: (key: string) => string;\n}\n\ntype IconComponent = (props: IconProps) => React.JSX.Element;\n\ninterface StatusVisual {\n label: string;\n StatusIcon: IconComponent;\n showDots: boolean;\n}\n\nfunction resolveStatusVisual(agentStatus: AgentStatusState | null, t: (key: string) => string): StatusVisual {\n if (!agentStatus) {\n return { label: t('Thinking...'), StatusIcon: BrainIcon, showDots: false };\n }\n switch (agentStatus.status) {\n case 'tool_start': {\n const rawNames = agentStatus.tools ?? [];\n const lower = rawNames.map((n) => n.toLowerCase());\n\n // Delegation tools have dedicated statuses\n if (lower.some((n) => n === 'spawn_background_task')) {\n const count = rawNames.filter((n) => n === 'spawn_background_task').length;\n const label = count > 1\n ? `${t('Delegating')} ${count} ${t('tasks')}…`\n : `${t('Delegating task')}…`;\n return { label, StatusIcon: UserPlusIcon, showDots: false };\n }\n if (lower.some((n) => n === 'check_task_status')) {\n const count = rawNames.filter((n) => n === 'check_task_status').length;\n const target = count > 1 ? `${count} ${t('background tasks')}` : t('background task');\n return { label: `${t('Waiting for')} ${target}…`, StatusIcon: SparklesIcon, showDots: false };\n }\n if (lower.some((n) => n === 'get_task_result')) {\n const count = rawNames.filter((n) => n === 'get_task_result').length;\n const from = count > 1 ? `${count} ${t('tasks')}` : t('task');\n return { label: `${t('Collecting results from')} ${from}…`, StatusIcon: SparklesIcon, showDots: false };\n }\n\n let StatusIcon: IconComponent = WrenchIcon;\n if (lower.some((n) => n.includes('search') || n.includes('list'))) {\n StatusIcon = SearchIcon;\n } else if (lower.some((n) => n.includes('read') || n.includes('get') || n.includes('query'))) {\n StatusIcon = DatabaseIcon;\n } else if (lower.some((n) => n.includes('send') || n.includes('create') || n.includes('draft') || n.includes('reply') || n.includes('flag'))) {\n StatusIcon = MailIcon;\n } else if (lower.some((n) => n.includes('code') || n.includes('execute'))) {\n StatusIcon = TerminalIcon;\n } else if (lower.some((n) => n.includes('web') || n.includes('browse'))) {\n StatusIcon = GlobeIcon;\n }\n let label: string;\n if (rawNames.length > 0) {\n const display = rawNames.map((n) => n.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()));\n const unique = Array.from(new Set(display));\n label = unique.length === 1 ? `${unique[0]}…` : `${unique[0]} (+${unique.length - 1} more)…`;\n } else {\n label = t('Using tools…');\n }\n return { label, StatusIcon, showDots: false };\n }\n case 'analyzing':\n return { label: t('Analyzing results…'), StatusIcon: SparklesIcon, showDots: false };\n case 'composing':\n return { label: t('Composing answer…'), StatusIcon: BrainIcon, showDots: true };\n case 'consulting': {\n const consultName = agentStatus.tools?.[0] ?? 'agent';\n return { label: `${t('Consulting')} ${consultName}…`, StatusIcon: UserPlusIcon, showDots: false };\n }\n case 'delegating': {\n const count = agentStatus.tools?.filter((n) => n === 'spawn_background_task').length ?? 0;\n return { label: count > 1 ? `${t('Delegating')} ${count} ${t('tasks')}…` : `${t('Delegating task')}…`, StatusIcon: UserPlusIcon, showDots: false };\n }\n case 'polling': {\n const checkCount = agentStatus.tools?.filter((n) => n === 'check_task_status').length ?? 0;\n const target = checkCount > 1 ? `${checkCount} ${t('background tasks')}` : t('background task');\n return { label: `${t('Waiting for')} ${target}…`, StatusIcon: SparklesIcon, showDots: false };\n }\n case 'collecting': {\n const fetchCount = agentStatus.tools?.filter((n) => n === 'get_task_result').length ?? 0;\n const from = fetchCount > 1 ? `${fetchCount} ${t('tasks')}` : t('task');\n return { label: `${t('Collecting results from')} ${from}…`, StatusIcon: SparklesIcon, showDots: false };\n }\n case 'transferring': {\n const targetName = agentStatus.tools?.[0] ?? 'agent';\n return { label: `${t('Transferring to')} ${targetName}…`, StatusIcon: ExternalLinkIcon, showDots: false };\n }\n case 'thinking':\n default:\n return { label: t('Thinking...'), StatusIcon: BrainIcon, showDots: false };\n }\n}\n\nfunction stripMarkdown(text: string): string {\n return text\n .replace(/```[\\s\\S]*?```/g, ' ')\n .replace(/`([^`]+)`/g, '$1')\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n .replace(/__(.+?)__/g, '$1')\n .replace(/\\*(.+?)\\*/g, '$1')\n .replace(/_(.+?)_/g, '$1')\n .replace(/#{1,6}\\s+/g, '')\n .replace(/[*\\->]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nexport function ThinkingTextBubble({ content }: { content: string }) {\n const ref = useRef<HTMLDivElement>(null);\n const cleaned = stripMarkdown(content);\n\n useEffect(() => {\n if (ref.current) ref.current.scrollTop = ref.current.scrollHeight;\n }, [cleaned]);\n\n if (cleaned.length < 3) return null;\n\n return (\n <div\n ref={ref}\n className=\"ml-11 max-w-[70%] max-h-20 overflow-hidden relative rounded-md border-l-2 bg-[var(--chat-accent)]/[0.03] pl-3 pr-3 py-2\"\n style={{ animation: 'reasoningGlow 3s ease-in-out infinite, chat-fade-in 0.5s ease-out' }}\n >\n <p className=\"text-[13px] leading-[1.35rem] text-gray-400 dark:text-white/40 break-words m-0\">\n {cleaned}\n </p>\n <div className=\"absolute inset-x-0 bottom-0 h-5 bg-gradient-to-t from-white/90 dark:from-[#1e1e2e]/90 to-transparent pointer-events-none\" />\n </div>\n );\n}\n\nexport const ChatThinking = ({ agentStatus, logoIcon, t }: ChatThinkingProps) => {\n const { label, StatusIcon, showDots } = resolveStatusVisual(agentStatus, t);\n const thinkingContent = agentStatus?.thinkingContent;\n\n return (\n <>\n <div className=\"flex gap-3 items-center justify-start\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-[var(--chat-accent)]/15 to-[var(--chat-accent)]/5\">\n <span className=\"text-[var(--chat-accent)] [&>svg]:w-4 [&>svg]:h-4\">{logoIcon}</span>\n </div>\n <div className=\"rounded-lg bg-gray-50 dark:bg-white/[0.03] px-4 py-3 relative overflow-hidden\">\n <div className=\"absolute inset-0 bg-gradient-to-r from-[var(--chat-accent)]/[0.03] via-transparent to-[var(--chat-accent)]/[0.03] animate-pulse pointer-events-none\" />\n <div className=\"relative flex items-center gap-2.5\">\n {showDots ? (\n <div className=\"flex gap-[3px] items-center h-3.5 w-3.5 justify-center\">\n {[0, 0.15, 0.3].map((delay, i) => (\n <span\n key={i}\n className=\"h-[5px] w-[5px] rounded-full bg-[var(--chat-accent)]/50\"\n style={{ animation: `chat-dot 1s ease-in-out infinite ${delay}s` }}\n />\n ))}\n </div>\n ) : (\n <StatusIcon size={14} className=\"text-[var(--chat-accent)] animate-pulse transition-all duration-300\" />\n )}\n <span className=\"text-sm text-gray-500 dark:text-white/50 transition-all duration-300\">{label}</span>\n </div>\n </div>\n </div>\n {thinkingContent && <ThinkingTextBubble content={thinkingContent} />}\n </>\n );\n};\n","import { useState } from 'react';\nimport Markdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport { CheckIcon, CopyIcon } from './icons';\n\ninterface MarkdownMessageProps {\n content: string;\n onRelativeLinkClick?: (href: string) => void;\n}\n\nconst isRelativeHref = (href?: string) => {\n if (!href) return false;\n if (href.startsWith('//')) return false;\n const hasAbsoluteScheme = /^[a-zA-Z][a-zA-Z\\d+.-]*:/.test(href);\n return !hasAbsoluteScheme;\n};\n\nexport const MarkdownMessage = ({ content, onRelativeLinkClick }: MarkdownMessageProps) => {\n const [copiedBlock, setCopiedBlock] = useState<string | null>(null);\n\n const handleCopyCode = (code: string) => {\n navigator.clipboard.writeText(code);\n setCopiedBlock(code);\n setTimeout(() => setCopiedBlock(null), 2000);\n };\n\n return (\n <Markdown\n remarkPlugins={[remarkGfm]}\n components={{\n p: ({ children }) => <p className=\"mb-3 last:mb-0 leading-7 break-words text-[0.8125rem] text-gray-900 dark:text-white/90\">{children}</p>,\n code: ({ className, children }) => {\n const match = /language-(\\w+)/.exec(className || '');\n const codeStr = String(children).replace(/\\n$/, '');\n if (match) {\n return (\n <div className=\"my-3 rounded-lg border border-gray-200 dark:border-white/10 overflow-hidden bg-gray-50 dark:bg-white/[0.03]\">\n <div className=\"flex items-center justify-between px-3 py-1.5 border-b border-gray-200 dark:border-white/10 bg-gray-100 dark:bg-white/[0.03]\">\n <span className=\"text-[0.7rem] text-gray-500 dark:text-white/40 font-mono\">{match[1]}</span>\n <button\n type=\"button\"\n onClick={() => handleCopyCode(codeStr)}\n className=\"p-0.5 rounded-sm hover:bg-gray-200 dark:hover:bg-white/10 transition-colors\"\n >\n {copiedBlock === codeStr ? (\n <CheckIcon size={14} className=\"text-green-500\" />\n ) : (\n <CopyIcon size={14} className=\"text-gray-400 dark:text-white/40\" />\n )}\n </button>\n </div>\n <pre className=\"m-0 px-3 py-2 overflow-x-auto\">\n <code className=\"font-mono text-xs leading-[1.7] text-gray-800 dark:text-white/90 whitespace-pre\">{codeStr}</code>\n </pre>\n </div>\n );\n }\n return (\n <code className=\"bg-gray-100 dark:bg-white/[0.08] px-1.5 py-0.5 rounded-sm font-mono text-xs text-[var(--chat-accent)]\">{children}</code>\n );\n },\n ul: ({ children }) => (\n <ul className=\"pl-5 mb-3 text-[0.8125rem] text-gray-900 dark:text-white/90 [&_li]:mb-1 marker:text-[var(--chat-accent)]/50\">{children}</ul>\n ),\n ol: ({ children }) => (\n <ol className=\"pl-5 mb-3 text-[0.8125rem] text-gray-900 dark:text-white/90 [&_li]:mb-1 marker:text-[var(--chat-accent)]/50\">{children}</ol>\n ),\n blockquote: ({ children }) => (\n <blockquote className=\"my-3 border-l-2 border-[var(--chat-accent)]/30 bg-[var(--chat-accent)]/[0.03] pl-4 pr-3 py-2 rounded-r-md italic text-gray-500 dark:text-white/60\">\n {children}\n </blockquote>\n ),\n a: ({ href, children }) => {\n const openInNewTab = !isRelativeHref(href);\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n if (!href || !isRelativeHref(href) || !onRelativeLinkClick) return;\n event.preventDefault();\n onRelativeLinkClick(href);\n };\n\n return (\n <a\n href={href}\n onClick={handleClick}\n target={openInNewTab ? '_blank' : undefined}\n rel={openInNewTab ? 'noopener noreferrer' : undefined}\n className=\"text-[var(--chat-accent)] underline underline-offset-2 hover:brightness-125\"\n >\n {children}\n </a>\n );\n },\n h1: ({ children }) => <h1 className=\"mt-4 first:mt-0 mb-2 font-bold text-base text-gray-900 dark:text-white\">{children}</h1>,\n h2: ({ children }) => <h2 className=\"mt-3 first:mt-0 mb-2 font-bold text-[0.9rem] text-gray-900 dark:text-white\">{children}</h2>,\n h3: ({ children }) => <h3 className=\"mt-3 first:mt-0 mb-1.5 font-semibold text-[0.85rem] text-gray-900 dark:text-white\">{children}</h3>,\n table: ({ children }) => (\n <div className=\"my-3 overflow-x-auto rounded-lg border border-gray-200 dark:border-white/10\">\n <table className=\"w-full border-collapse text-xs\">{children}</table>\n </div>\n ),\n th: ({ children }) => (\n <th className=\"px-3 py-2 text-left font-semibold bg-gray-50 dark:bg-white/[0.04] border-b border-gray-200 dark:border-white/10 text-gray-900 dark:text-white\">\n {children}\n </th>\n ),\n td: ({ children }) => (\n <td className=\"px-3 py-2 border-b border-gray-200 dark:border-white/10 text-gray-700 dark:text-white/80\">{children}</td>\n ),\n }}\n >\n {content}\n </Markdown>\n );\n};\n","import { useEffect, useRef, useState } from 'react';\nimport type { AgentStatusState, ChatMessage } from '../types';\nimport { FileIcon, InfoIcon } from './icons';\nimport { ChatThinking, ThinkingTextBubble } from './ChatThinking';\nimport { MarkdownMessage } from './MarkdownMessage';\n\ninterface ChatMessagesProps {\n messages: ChatMessage[];\n isLoading: boolean;\n agentStatus: AgentStatusState | null;\n agentName: string;\n logoIcon: React.ReactNode;\n onRelativeLinkClick?: (href: string) => void;\n t: (key: string) => string;\n}\n\nexport const ChatMessages = ({ messages, isLoading, agentStatus, agentName, logoIcon, onRelativeLinkClick, t }: ChatMessagesProps) => {\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const [toolDetailMsgId, setToolDetailMsgId] = useState<string | null>(null);\n\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n return (\n <div className=\"flex-1 overflow-y-auto px-4 py-3 flex flex-col gap-4 filigran-chat-scrollable\">\n {messages.map((msg) => {\n const isAssistant = msg.role === 'assistant';\n const isEmpty = !msg.content;\n const isThinking = isAssistant && isEmpty && isLoading;\n\n if (isThinking) {\n return (\n <div key={msg.id}>\n <ChatThinking agentStatus={agentStatus} logoIcon={logoIcon} t={t} />\n </div>\n );\n }\n\n return (\n <div key={msg.id} className={`flex flex-col ${isAssistant ? 'items-start' : 'items-end'}`}>\n {isAssistant && (\n <div className=\"flex items-center gap-1.5 mb-1\">\n <div className=\"w-6 h-6 rounded-lg flex items-center justify-center bg-gradient-to-br from-[var(--chat-accent)]/20 to-[var(--chat-accent)]/5\">\n <span className=\"text-[var(--chat-accent)] [&>svg]:w-3 [&>svg]:h-3\">{logoIcon}</span>\n </div>\n <span className=\"font-semibold text-xs text-gray-900 dark:text-white\">{agentName}</span>\n </div>\n )}\n\n {isAssistant && !isEmpty && isLoading && agentStatus?.thinkingContent && <ThinkingTextBubble content={agentStatus.thinkingContent} />}\n\n {msg.files && msg.files.length > 0 && (\n <div className=\"flex gap-1.5 flex-wrap mb-1.5\">\n {msg.files.map((f, i) => (\n <span\n key={i}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-gray-200 dark:border-white/10 text-[0.7rem] text-gray-600 dark:text-white/60\"\n >\n <FileIcon size={14} />\n {f.name}\n </span>\n ))}\n </div>\n )}\n\n <div\n className={`max-w-[90%] ${\n isAssistant\n ? 'pl-1 py-1 text-[0.8125rem] leading-7'\n : 'px-3.5 py-2 rounded-[14px_14px_4px_14px] bg-[var(--chat-accent-dark)] text-white text-[0.8125rem] leading-6'\n }`}\n >\n {isAssistant ? <MarkdownMessage content={msg.content} onRelativeLinkClick={onRelativeLinkClick} /> : msg.content}\n {isAssistant && isEmpty && !isLoading && <span className=\"text-[0.8125rem] text-gray-400 dark:text-white/40 italic\">...</span>}\n {isAssistant && !isEmpty && isLoading && (\n <span className=\"inline-block w-1.5 h-4 bg-[var(--chat-accent)]/70 rounded-xs ml-0.5 animate-pulse align-text-bottom\" />\n )}\n </div>\n\n {isAssistant && !isEmpty && !isLoading && msg.toolNames && msg.toolNames.length > 0 && (\n <>\n <button\n type=\"button\"\n onClick={() => setToolDetailMsgId(toolDetailMsgId === msg.id ? null : msg.id)}\n className=\"mt-0.5 p-1 rounded-lg opacity-50 hover:opacity-100 hover:text-[var(--chat-accent)] transition-opacity\"\n title={t('Reasoning details')}\n >\n <InfoIcon size={14} />\n </button>\n {toolDetailMsgId === msg.id && (\n <div className=\"mt-1.5 p-3 rounded-lg bg-gray-50 dark:bg-white/[0.04] border border-gray-200 dark:border-white/10\">\n <p className=\"text-[0.7rem] text-gray-500 dark:text-white/40 mb-1.5\">\n {msg.iterations && msg.iterations > 1 ? `${msg.iterations} iterations · ` : ''}\n {msg.toolCallCount ?? msg.toolNames.length}{' '}\n {(msg.toolCallCount ?? msg.toolNames.length) === 1 ? t('tool call') : t('tool calls')}\n </p>\n <div className=\"flex flex-wrap gap-1\">\n {Array.from(new Set(msg.toolNames)).map((tn) => (\n <span\n key={tn}\n className=\"inline-flex items-center px-2 py-0.5 rounded-full border border-gray-200 dark:border-white/10 text-[0.68rem] font-mono text-gray-500 dark:text-white/40\"\n >\n {tn.replace(/_/g, ' ')}\n </span>\n ))}\n </div>\n </div>\n )}\n </>\n )}\n </div>\n );\n })}\n <div ref={messagesEndRef} />\n </div>\n );\n};\n","interface ChatWelcomeProps {\n firstName: string;\n logoIcon: React.ReactNode;\n promptSuggestions: string[];\n onPromptClick: (prompt: string) => void;\n t: (key: string) => string;\n}\n\nexport const ChatWelcome = ({ firstName, logoIcon, promptSuggestions, onPromptClick, t }: ChatWelcomeProps) => (\n <div className=\"flex-1 flex flex-col items-center justify-center px-6 pb-8\">\n <span className=\"text-[var(--chat-accent)] mb-4 [&>svg]:w-12 [&>svg]:h-12 drop-shadow-[0_0_12px_var(--chat-accent-40)]\">{logoIcon}</span>\n <h2 className=\"text-xl font-medium mb-6 text-center text-gray-900 dark:text-white\" style={{ fontFamily: '\"Geologica\", sans-serif' }}>\n {t('How can I help you, ')}\n {firstName}?\n </h2>\n <div className=\"w-full max-w-[320px]\">\n <span className=\"block text-center mb-2 text-[0.65rem] tracking-[1.5px] uppercase text-[var(--chat-accent)] font-semibold\">\n {t('Suggestions')}\n </span>\n {promptSuggestions.map((prompt) => (\n <button\n key={prompt}\n type=\"button\"\n onClick={() => onPromptClick(prompt)}\n className=\"w-full text-left text-[0.8125rem] text-gray-800 dark:text-white py-1.5 px-3 mb-1 rounded-lg border border-gray-200 dark:border-white/10 bg-transparent transition-colors hover:bg-[var(--chat-accent-10)] hover:border-[var(--chat-accent-50)]\"\n >\n {t(prompt)}\n </button>\n ))}\n </div>\n </div>\n);\n","import { type FunctionComponent, useEffect, useState } from 'react';\nimport type { ChatMessage, ChatPanelProps } from '../types';\nimport { hexAlpha, identity } from '../utils';\nimport { useChat } from '../hooks/useChat';\nimport { useAgents } from '../hooks/useAgents';\nimport { useSidebarResize } from '../hooks/useSidebarResize';\nimport { DefaultLogoIcon } from './icons';\nimport { ChatHeader } from './ChatHeader';\nimport { ChatInput } from './ChatInput';\nimport { ChatMessages } from './ChatMessages';\nimport { ChatWelcome } from './ChatWelcome';\n\nconst FLOATING_WIDTH = 380;\nconst FLOATING_HEIGHT = 560;\nconst SIDEBAR_GAP = 6;\n\nconst DEFAULT_SUGGESTIONS = [\n 'Help me create a new simulation scenario',\n 'What are the latest attack patterns?',\n 'How do I configure detection rules?',\n 'Summarize my recent findings',\n];\n\nexport const ChatPanel: FunctionComponent<ChatPanelProps> = ({\n mode,\n onClose,\n onModeChange,\n topOffset = 0,\n apiBaseUrl,\n apiEndpoints,\n agentDashboardUrl,\n user,\n t = identity,\n accentColor = '#7b5cff',\n logoIcon,\n promptSuggestions = DEFAULT_SUGGESTIONS,\n draftBorderColor,\n resizable = false,\n onWidthChange,\n onResizeStart,\n onResizeEnd,\n disableFileManagement = false,\n onRelativeLinkClick,\n maxFileCount,\n maxTotalSize,\n requestHeaders,\n pushContentSelector,\n backendType = 'rest',\n}) => {\n const [modeMenuOpen, setModeMenuOpen] = useState(false);\n\n const { agents, selectedAgent, agentMenuOpen, setAgentMenuOpen, handleSwitchAgent } = useAgents({ apiBaseUrl, apiEndpoints, backendType, requestHeaders });\n\n const {\n messages,\n inputValue,\n setInputValue,\n isLoading,\n agentStatus,\n attachedFiles,\n conversationId,\n transferredAgent,\n historyLoadedRef,\n handleFileAdd,\n handlePaste,\n handleSendMessage,\n handleNewChat,\n handleStopGenerating,\n setAttachedFiles,\n setMessages,\n setConversationId,\n } = useChat({\n apiBaseUrl,\n apiEndpoints,\n backendType,\n agentSlug: selectedAgent?.slug,\n requestHeaders,\n t,\n maxFileCount,\n maxTotalSize,\n });\n\n const { sidebarWidth, handleResizeStart, defaultWidth, isResizing } = useSidebarResize({\n mode,\n resizable,\n onWidthChange,\n onResizeStart,\n onResizeEnd,\n });\n\n // Push content when sidebar mode is active using CSS variable\n useEffect(() => {\n const width = mode === 'sidebar' ? (resizable ? sidebarWidth : defaultWidth) : 0;\n const pushWidth = width > 0 ? width + SIDEBAR_GAP : 0;\n\n // Set CSS variable on :root for any component to use\n document.documentElement.style.setProperty('--chatbot-sidebar-width', `${pushWidth}px`);\n document.documentElement.style.setProperty('--chatbot-transition', isResizing ? 'none' : 'all 225ms cubic-bezier(0.4, 0, 0.2, 1)');\n\n // Also apply to pushContentSelector if provided (for simple cases)\n if (pushContentSelector) {\n const contentElement = document.querySelector<HTMLElement>(pushContentSelector);\n if (contentElement) {\n const originalPaddingRight = contentElement.style.paddingRight;\n const originalTransition = contentElement.style.transition;\n\n contentElement.style.paddingRight = pushWidth > 0 ? `${pushWidth}px` : '';\n contentElement.style.transition = isResizing ? 'none' : 'padding-right 225ms cubic-bezier(0.4, 0, 0.2, 1)';\n\n return () => {\n contentElement.style.paddingRight = originalPaddingRight;\n contentElement.style.transition = originalTransition;\n document.documentElement.style.setProperty('--chatbot-sidebar-width', '0px');\n };\n }\n }\n\n return () => {\n document.documentElement.style.setProperty('--chatbot-sidebar-width', '0px');\n };\n }, [pushContentSelector, mode, sidebarWidth, defaultWidth, resizable, isResizing]);\n\n const resolvedLogo = logoIcon ?? <DefaultLogoIcon size={24} />;\n const firstName = user.firstName;\n const agentName = transferredAgent?.name || selectedAgent?.name || 'Assistant';\n\n const cssVars = {\n '--chat-accent': accentColor,\n '--chat-accent-10': hexAlpha(accentColor, 0.1),\n '--chat-accent-40': hexAlpha(accentColor, 0.25),\n '--chat-accent-50': hexAlpha(accentColor, 0.5),\n '--chat-accent-dark': accentColor,\n } as React.CSSProperties;\n\n // Load conversation history when agent is selected\n useEffect(() => {\n // Skip session history if disabled, using single endpoint mode, or non-REST backend\n if (apiEndpoints?.sessions === null || apiEndpoints?.singleEndpoint || backendType === 'legacy' || backendType === 'ag-ui') return;\n if (!conversationId || historyLoadedRef.current || !selectedAgent) return;\n historyLoadedRef.current = true;\n const sessionsUrl = `${apiBaseUrl}${apiEndpoints?.sessions ?? '/chat/sessions'}`;\n\n const clearStaleConversation = () => {\n setConversationId(null);\n localStorage.removeItem('filigranChatConversationId');\n };\n\n fetch(sessionsUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(requestHeaders ?? {}) },\n body: JSON.stringify({\n conversation_id: conversationId,\n agent_slug: selectedAgent.slug,\n }),\n })\n .then((res) => {\n if (!res.ok) {\n clearStaleConversation();\n return null;\n }\n return res.json();\n })\n .then((data) => {\n if (!data?.messages?.length) return;\n const restored: ChatMessage[] = data.messages.map((m: { role: string; content: string }, i: number) => ({\n id: `restored-${i}`,\n role: m.role as 'user' | 'assistant',\n content: m.content,\n timestamp: new Date(),\n }));\n setMessages(restored);\n })\n .catch(() => {\n clearStaleConversation();\n });\n }, [conversationId, selectedAgent, apiBaseUrl, apiEndpoints, historyLoadedRef, requestHeaders, setMessages, setConversationId]);\n\n const onSwitchAgent = (agent: typeof selectedAgent) => {\n if (!agent) return;\n handleSwitchAgent(agent, () => {\n handleNewChat();\n });\n };\n\n const containerClasses = (() => {\n const base = 'filigran-chatbot';\n switch (mode) {\n case 'sidebar':\n return `${base} fixed right-0 bottom-0 flex flex-col bg-white dark:bg-[#1e1e2e] border-l border-gray-200 dark:border-white/10 z-[1200]`;\n case 'floating':\n return `${base} fixed bottom-5 right-5 flex flex-col bg-white dark:bg-[#1e1e2e] rounded-xl shadow-[0_8px_32px_rgba(0,0,0,0.15)] dark:shadow-[0_8px_32px_rgba(0,0,0,0.4)] z-[1300] border border-gray-200 dark:border-white/10`;\n case 'fullscreen':\n return `${base} fixed right-0 bottom-0 left-0 flex flex-col bg-gray-50 dark:bg-[#161622] z-[1400]`;\n default:\n return base;\n }\n })();\n\n const containerStyle: React.CSSProperties = {\n ...cssVars,\n ...(mode === 'sidebar'\n ? { top: topOffset, width: resizable ? sidebarWidth : defaultWidth }\n : mode === 'floating'\n ? { width: FLOATING_WIDTH, height: FLOATING_HEIGHT }\n : { top: topOffset }),\n };\n\n return (\n <div className={containerClasses} style={containerStyle}>\n {mode === 'sidebar' && resizable && (\n <div onMouseDown={handleResizeStart} className=\"absolute top-0 -left-1 bottom-0 w-2 cursor-col-resize z-10 group\">\n <div className=\"absolute top-0 left-1/2 -translate-x-1/2 bottom-0 w-0.5 rounded-sm bg-[var(--chat-accent)] opacity-0 transition-opacity group-hover:opacity-100 group-active:opacity-100\" />\n </div>\n )}\n <ChatHeader\n mode={mode}\n agentName={agentName}\n agents={agents}\n selectedAgent={selectedAgent}\n transferredFrom={transferredAgent ? selectedAgent?.name : undefined}\n agentMenuOpen={agentMenuOpen}\n onAgentMenuToggle={() => setAgentMenuOpen((p) => !p)}\n onAgentMenuClose={() => setAgentMenuOpen(false)}\n onSwitchAgent={onSwitchAgent}\n modeMenuOpen={modeMenuOpen}\n onModeMenuToggle={() => setModeMenuOpen((p) => !p)}\n onModeMenuClose={() => setModeMenuOpen(false)}\n onModeChange={onModeChange}\n onNewChat={handleNewChat}\n onClose={onClose}\n logoIcon={resolvedLogo}\n agentDashboardUrl={agentDashboardUrl}\n t={t}\n />\n {messages.length === 0 ? (\n <ChatWelcome firstName={firstName} logoIcon={resolvedLogo} promptSuggestions={promptSuggestions} onPromptClick={setInputValue} t={t} />\n ) : (\n <ChatMessages\n messages={messages}\n isLoading={isLoading}\n agentStatus={agentStatus}\n agentName={agentName}\n logoIcon={resolvedLogo}\n onRelativeLinkClick={onRelativeLinkClick}\n t={t}\n />\n )}\n <ChatInput\n inputValue={inputValue}\n onInputChange={setInputValue}\n onSend={handleSendMessage}\n onStop={handleStopGenerating}\n isLoading={isLoading}\n attachedFiles={disableFileManagement ? [] : attachedFiles}\n onFileAdd={disableFileManagement ? undefined : handleFileAdd}\n onFileRemove={disableFileManagement ? undefined : (i) => setAttachedFiles((prev) => prev.filter((_, j) => j !== i))}\n onPaste={disableFileManagement ? undefined : handlePaste}\n t={t}\n mode={mode}\n separatorColor={draftBorderColor}\n />\n </div>\n );\n};\n","import type { FunctionComponent } from 'react';\nimport type { ChatToggleButtonProps } from '../types';\nimport { hexAlpha } from '../utils';\nimport { DefaultLogoIcon } from './icons';\n\nexport const ChatToggleButton: FunctionComponent<ChatToggleButtonProps> = ({ isOpen, onToggle, label = 'Ask Assistant', accentColor = '#7b5cff', icon }) => {\n const resolvedIcon = icon ?? <DefaultLogoIcon size={16} />;\n\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n className=\"filigran-chatbot inline-flex items-center gap-1.5 px-3 py-[3px] text-[0.8125rem] font-medium whitespace-nowrap rounded-md border transition-colors\"\n style={{\n borderColor: isOpen ? accentColor : hexAlpha(accentColor, 0.5),\n color: accentColor,\n backgroundColor: isOpen ? hexAlpha(accentColor, 0.1) : 'transparent',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.borderColor = accentColor;\n e.currentTarget.style.backgroundColor = hexAlpha(accentColor, 0.1);\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.borderColor = isOpen ? accentColor : hexAlpha(accentColor, 0.5);\n e.currentTarget.style.backgroundColor = isOpen ? hexAlpha(accentColor, 0.1) : 'transparent';\n }}\n >\n <span className=\"[&>svg]:w-4 [&>svg]:h-4\">{resolvedIcon}</span>\n {label}\n </button>\n );\n};\n"],"names":["hexAlpha","hex","alpha","Math","round","toString","padStart","identity","key","parseLegacyEvent","evt","ctx","eventType","event","data","nodeId","status","activeNodeId","action","content","replace","reasoning","usedTools","length","hasUsedTools","tools","map","t","tool","Array","isArray","chatId","parseRestEvent","type","st","thinkingContent","conversationId","conversation_id","toolNames","tool_names","toolCallCount","tool_call_count","iterations","transferAgentId","transfer_agent_id","transferAgentName","transfer_agent_name","parseAgUiEvent","message","stepName","delta","toolName","toolCallName","STORAGE_KEY","LEGACY_CHAT_ID_KEY","DEFAULT_MAX_TOTAL_SIZE","useChat","apiBaseUrl","apiEndpoints","backendType","agentSlug","requestHeaders","maxFileCount","maxTotalSize","isLegacy","messages","setMessages","useState","inputValue","setInputValue","isLoading","setIsLoading","agentStatus","setAgentStatus","setConversationId","window","localStorage","getItem","attachedFiles","setAttachedFiles","transferredAgent","setTransferredAgent","legacyChatId","setLegacyChatId","historyLoadedRef","useRef","abortControllerRef","hasUsedToolsRef","conversationIdRef","creatingSessionRef","uploadAbortRef","AbortController","effectiveMaxFileCount","Number","isFinite","floor","effectiveMaxTotalSize","getUploadUrl","singleEndpoint","upload","updateConversationId","id","current","setItem","removeItem","ensureConversation","async","slug","sessionsUrl","sessions","promise","res","fetch","method","headers","body","JSON","stringify","agent_slug","ok","json","convId","uploadSingleFile","file","signal","uploadUrl","formData","FormData","append","name","uploadHeaders","Object","fromEntries","entries","filter","k","toLowerCase","undefined","Error","ids","file_ids","handleFileAdd","fileList","candidates","from","tempId","crypto","randomUUID","accepted","prev","currentCount","currentSize","reduce","sum","f","size","slotsAvailable","sizeLeft","filtered","c","slice","push","newEntries","rawFile","uploadStatus","fileId","setTimeout","p","err","DOMException","handlePaste","e","files","clipboardData","preventDefault","handleSendMessage","trim","userMsg","role","timestamp","Date","assistantId","controller","fileIds","requestBody","opts","question","streaming","threadId","runId","context","state","forwardedProps","buildRequestBody","m","parseEvent","getParser","reader","getReader","decoder","TextDecoder","buffer","accumulated","doneReceived","done","value","read","decode","stream","lines","split","pop","rawLine","line","startsWith","jsonStr","parsed","parse","handleNewChat","abort","handleStopGenerating","STORAGE_AGENT_KEY","SIDEBAR_WIDTH","SIDEBAR_WIDTH_STORAGE_KEY","AttachFileIcon","className","_jsx","xmlns","width","height","viewBox","fill","stroke","strokeWidth","strokeLinecap","strokeLinejoin","children","d","BrainIcon","_jsxs","CheckIcon","ChevronDownIcon","CloseIcon","CopyIcon","x","y","rx","ry","DatabaseIcon","cx","cy","DefaultLogoIcon","EditIcon","ExternalLinkIcon","FileIcon","FloatingIcon","FullscreenExitIcon","FullscreenIcon","GlobeIcon","r","InfoIcon","MailIcon","SearchIcon","SendIcon","SidebarIcon","SparklesIcon","StopCircleIcon","TerminalIcon","points","x1","x2","y1","y2","UserPlusIcon","WrenchIcon","Dropdown","open","onClose","anchorRef","placement","panelRef","pos","setPos","top","left","ref","handler","active","useEffect","listener","contains","target","document","addEventListener","removeEventListener","useClickOutside","useCallback","rect","getBoundingClientRect","right","bottom","portalTarget","el","node","classList","parentElement","findChatbotRoot","createPortal","style","Spinner","Tooltip","title","show","setShow","onMouseEnter","onMouseLeave","modeOptions","mode","label","getIcon","ChatHeader","agentName","agents","selectedAgent","transferredFrom","agentMenuOpen","onAgentMenuToggle","onAgentMenuClose","onSwitchAgent","modeMenuOpen","onModeMenuToggle","onModeMenuClose","onModeChange","onNewChat","logoIcon","agentDashboardUrl","agentAnchorRef","modeAnchorRef","CurrentModeIcon","onClick","agent","description","opt","ChatInput","onInputChange","onSend","onStop","onFileAdd","onFileRemove","onPaste","separatorColor","fileInputRef","textareaRef","isFileManagementEnabled","Boolean","hasContent","hasFilesUploading","some","canSend","borderTopColor","borderTopWidth","i","multiple","hidden","onChange","click","placeholder","min","scrollHeight","onKeyDown","shiftKey","rows","maxHeight","disabled","ThinkingTextBubble","cleaned","scrollTop","animation","ChatThinking","StatusIcon","showDots","rawNames","lower","n","count","includes","display","toUpperCase","unique","Set","consultName","checkCount","fetchCount","targetName","resolveStatusVisual","_Fragment","delay","isRelativeHref","href","test","MarkdownMessage","onRelativeLinkClick","copiedBlock","setCopiedBlock","Markdown","remarkPlugins","remarkGfm","components","code","match","exec","codeStr","String","handleCopyCode","navigator","clipboard","writeText","ul","ol","blockquote","a","openInNewTab","rel","h1","h2","h3","table","th","td","ChatMessages","messagesEndRef","toolDetailMsgId","setToolDetailMsgId","scrollIntoView","behavior","msg","isAssistant","isEmpty","tn","ChatWelcome","firstName","promptSuggestions","onPromptClick","fontFamily","prompt","DEFAULT_SUGGESTIONS","ChatPanel","topOffset","user","accentColor","draftBorderColor","resizable","onWidthChange","onResizeStart","onResizeEnd","disableFileManagement","pushContentSelector","setModeMenuOpen","setAgentMenuOpen","handleSwitchAgent","setAgents","setSelectedAgent","then","savedSlug","find","catch","onSwitch","useAgents","sidebarWidth","handleResizeStart","defaultWidth","isResizing","setSidebarWidth","stored","parseInt","isNaN","setIsResizing","isResizingRef","sidebarWidthRef","onWidthChangeRef","onResizeEndRef","handleMouseMove","newWidth","innerWidth","clientX","maxWidth","clamped","max","handleMouseUp","cursor","userSelect","handleWindowResize","useSidebarResize","pushWidth","documentElement","setProperty","contentElement","querySelector","originalPaddingRight","paddingRight","originalTransition","transition","resolvedLogo","cssVars","clearStaleConversation","restored","containerClasses","base","containerStyle","onMouseDown","_","j","ChatToggleButton","isOpen","onToggle","icon","resolvedIcon","borderColor","color","backgroundColor","currentTarget"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/utils/index.ts","../../src/hooks/protocols/parseRestEvent.ts","../../src/hooks/protocols/parseLegacyEvent.ts","../../src/hooks/protocols/parseAgUiEvent.ts","../../src/hooks/useChat.ts","../../src/hooks/useAgents.ts","../../src/hooks/useSidebarResize.ts","../../src/components/icons/AttachFileIcon.tsx","../../src/components/icons/BrainIcon.tsx","../../src/components/icons/CheckIcon.tsx","../../src/components/icons/ChevronDownIcon.tsx","../../src/components/icons/CloseIcon.tsx","../../src/components/icons/CopyIcon.tsx","../../src/components/icons/DatabaseIcon.tsx","../../src/components/icons/DefaultLogoIcon.tsx","../../src/components/icons/DownloadIcon.tsx","../../src/components/icons/EditIcon.tsx","../../src/components/icons/ExternalLinkIcon.tsx","../../src/components/icons/FileIcon.tsx","../../src/components/icons/FloatingIcon.tsx","../../src/components/icons/FullscreenExitIcon.tsx","../../src/components/icons/FullscreenIcon.tsx","../../src/components/icons/GlobeIcon.tsx","../../src/components/icons/InfoIcon.tsx","../../src/components/icons/MailIcon.tsx","../../src/components/icons/SearchIcon.tsx","../../src/components/icons/SendIcon.tsx","../../src/components/icons/SidebarIcon.tsx","../../src/components/icons/SparklesIcon.tsx","../../src/components/icons/StopCircleIcon.tsx","../../src/components/icons/TerminalIcon.tsx","../../src/components/icons/UserPlusIcon.tsx","../../src/components/icons/WrenchIcon.tsx","../../src/components/Dropdown.tsx","../../src/hooks/useClickOutside.ts","../../src/components/Spinner.tsx","../../src/components/Tooltip.tsx","../../src/components/ChatHeader.tsx","../../src/components/ChatInput.tsx","../../src/components/ChatThinking.tsx","../../src/components/MarkdownMessage.tsx","../../src/components/ChatMessages.tsx","../../src/components/ChatWelcome.tsx","../../src/components/ChatPanel.tsx","../../src/components/ChatToggleButton.tsx"],"sourcesContent":["export function hexAlpha(hex: string, alpha: number): string {\n const a = Math.round(alpha * 255)\n .toString(16)\n .padStart(2, '0');\n return `${hex}${a}`;\n}\n\nexport const identity = (key: string) => key;\n\n/** Matches a complete `[[FILE:<id>]]` deliverable marker agents embed in prose. */\nconst FILE_MARKER_RE = /\\[\\[FILE:[^\\]]+\\]\\]/g;\n\n/**\n * Matches an INCOMPLETE marker at the very end of the string. SSE streams can\n * split a `[[FILE:<id>]]` token before the closing `]]` arrives, so the tail\n * may be `[[FILE`, `[[FILE:`, `[[FILE:abc`, or `[[FILE:abc]` mid-stream. We\n * anchor on the literal `[[FILE` prefix (which is vanishingly unlikely to\n * appear legitimately at the end of prose) so it never clips real content.\n */\nconst PARTIAL_FILE_MARKER_RE = /\\[\\[FILE(?::[^\\]]*)?\\]?$/;\n\n/**\n * Strip the `[[FILE:<id>]]` markers an agent embeds in its reply to point at\n * generated files. The actual files render as separate download chips, so the\n * raw markers must be removed from the prose. Complete markers are removed\n * anywhere; an incomplete marker at the end is also removed so a partially\n * streamed token never flickers as raw `[[FILE:...` text.\n *\n * When no marker is present the content is returned **untouched** — we must\n * not trim or collapse blank lines on ordinary prose, which would clobber\n * intentional leading/trailing whitespace (e.g. indented Markdown / code).\n * Whitespace is only normalized when a marker was actually removed.\n *\n * Applied to assistant content only — user-typed text is never touched, so a\n * user who literally types `[[FILE:x]]` still sees their own text.\n */\nexport function stripFileMarkers(content: string): string {\n if (!content) return content;\n const stripped = content.replace(FILE_MARKER_RE, '').replace(PARTIAL_FILE_MARKER_RE, '');\n if (stripped === content) return content;\n return stripped\n .replace(/[ \\t]+\\n/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n\n/** An ordered piece of assistant content: prose text or a file marker. */\nexport type FileMarkerPart = { type: 'text'; value: string } | { type: 'file'; fileId: string };\n\n/**\n * Split assistant content into ordered text/file parts around complete\n * `[[FILE:<id>]]` markers, so the renderer can place each download card at the\n * marker's source position (preserving interleaved order). A trailing\n * incomplete marker (an SSE token split mid-stream) is removed from the final\n * text part so it never shows as raw `[[FILE:...` text.\n */\nexport function splitFileMarkers(content: string): FileMarkerPart[] {\n if (!content) return [];\n const parts: FileMarkerPart[] = [];\n const re = /\\[\\[FILE:([^\\]]+)\\]\\]/g;\n let lastIndex = 0;\n let match: RegExpExecArray | null = re.exec(content);\n while (match !== null) {\n if (match.index > lastIndex) {\n parts.push({ type: 'text', value: content.slice(lastIndex, match.index) });\n }\n parts.push({ type: 'file', fileId: match[1] });\n lastIndex = re.lastIndex;\n match = re.exec(content);\n }\n const tail = content.slice(lastIndex).replace(PARTIAL_FILE_MARKER_RE, '');\n if (tail) parts.push({ type: 'text', value: tail });\n return parts;\n}\n","import type { ChatAttachment } from '../../types';\nimport type { ParsedAction, ProtocolContext } from './types';\n\n/**\n * Normalize the raw `attachments` array from a backend `done` event into\n * typed {@link ChatAttachment} objects. Defensive: skips non-object entries\n * and entries without a `file_id`. Returns `undefined` when there is nothing\n * renderable so the `done` action stays lean for backends without #810.\n */\nexport function parseAttachments(raw: unknown): ChatAttachment[] | undefined {\n if (!Array.isArray(raw)) return undefined;\n const out: ChatAttachment[] = [];\n for (const item of raw) {\n if (!item || typeof item !== 'object') continue;\n const a = item as Record<string, unknown>;\n const fileId = a.file_id;\n if (typeof fileId !== 'string' || !fileId) continue;\n out.push({\n fileId,\n filename: typeof a.filename === 'string' ? a.filename : 'file',\n type: typeof a.type === 'string' ? a.type : undefined,\n size: typeof a.size === 'number' ? a.size : undefined,\n contentType: typeof a.content_type === 'string' ? a.content_type : undefined,\n fileTag: a.file_tag === 'working_file' ? 'working_file' : 'download_file',\n });\n }\n return out.length > 0 ? out : undefined;\n}\n\n/**\n * Parse an XTM One (REST) SSE event into a normalized action.\n */\nexport function parseRestEvent(evt: Record<string, unknown>, ctx: ProtocolContext): ParsedAction {\n const type = evt.type as string | undefined;\n\n if (type === 'error') {\n return { action: 'error', content: (evt.content as string) || '' };\n }\n\n if (type === 'status') {\n const st = evt.status as string;\n if (st === 'tool_done' || st === 'wind_down') {\n return { action: 'noop' };\n }\n if (st === 'streaming') {\n return { action: 'status', status: 'streaming' };\n }\n if (st === 'thinking_text') {\n return { action: 'status', status: 'thinking_text', thinkingContent: evt.content as string };\n }\n if (st === 'tool_start') {\n ctx.hasUsedTools = true;\n return { action: 'status', status: 'tool_start', tools: evt.tools as string[] | undefined };\n }\n if (st === 'thinking' && ctx.hasUsedTools) {\n return { action: 'status', status: 'analyzing' };\n }\n return { action: 'status', status: st, tools: evt.tools as string[] | undefined };\n }\n\n if (type === 'stream') {\n return { action: 'stream', content: evt.content as string };\n }\n\n if (type === 'done') {\n return {\n action: 'done',\n content: evt.content as string,\n conversationId: evt.conversation_id as string | undefined,\n toolNames: evt.tool_names as string[] | undefined,\n toolCallCount: evt.tool_call_count as number | undefined,\n iterations: evt.iterations as number | undefined,\n transferAgentId: evt.transfer_agent_id as string | undefined,\n transferAgentName: evt.transfer_agent_name as string | undefined,\n attachments: parseAttachments(evt.attachments),\n };\n }\n\n return { action: 'noop' };\n}\n","import type { ParsedAction, ProtocolContext } from './types';\n\n/**\n * Parse a Flowise-style SSE event into a normalized action.\n */\nexport function parseLegacyEvent(evt: Record<string, unknown>, ctx: ProtocolContext): ParsedAction {\n const eventType = evt.event as string | undefined;\n\n if (eventType === 'nextAgentFlow') {\n const data = evt.data as Record<string, unknown> | undefined;\n const nodeId = data?.nodeId as string | undefined;\n if (data?.status === 'INPROGRESS' && nodeId) {\n ctx.activeNodeId = nodeId;\n }\n return { action: 'noop' };\n }\n\n if (eventType === 'start') {\n return { action: 'noop' };\n }\n\n if (eventType === 'token') {\n const tokenData = ((evt.data as string) ?? '').replace(/<br\\s*\\/?>/g, '\\n');\n return { action: 'stream', content: tokenData };\n }\n\n if (eventType === 'agentReasoning') {\n const reasoning = evt.data as Record<string, unknown> | undefined;\n const usedTools = reasoning?.usedTools as Array<{ tool: string }> | undefined;\n if (usedTools?.length) {\n ctx.hasUsedTools = true;\n return { action: 'status', status: 'tool_start', tools: usedTools.map((t) => t.tool) };\n }\n if (ctx.hasUsedTools) {\n return { action: 'status', status: 'analyzing' };\n }\n return { action: 'status', status: 'thinking' };\n }\n\n if (eventType === 'usedTools') {\n ctx.hasUsedTools = true;\n const data = evt.data as Array<{ tool: string }> | undefined;\n const toolNames = Array.isArray(data) ? data.map((t) => t.tool) : [];\n return { action: 'status', status: 'tool_start', tools: toolNames };\n }\n\n if (eventType === 'metadata') {\n const data = evt.data as Record<string, unknown> | undefined;\n const chatId = data?.chatId as string | undefined;\n if (chatId) {\n return { action: 'set_chat_id', chatId };\n }\n return { action: 'noop' };\n }\n\n if (eventType === 'error') {\n return { action: 'error', content: (evt.data as string) || '' };\n }\n\n if (eventType === 'end') {\n return { action: 'done', content: '' };\n }\n\n return { action: 'noop' };\n}\n","import type { ParsedAction, ProtocolContext } from './types';\n\n/**\n * AG-UI protocol event types.\n * @see https://github.com/ag-ui-protocol/ag-ui\n */\n\n/**\n * Parse an AG-UI protocol SSE event into a normalized action.\n *\n * AG-UI uses a Start/Content/End lifecycle for messages and tool calls.\n * We map these to the same internal actions used by the other protocols.\n */\nexport function parseAgUiEvent(evt: Record<string, unknown>, ctx: ProtocolContext): ParsedAction {\n const type = evt.type as string | undefined;\n\n // --- Run lifecycle ---\n\n if (type === 'RUN_STARTED') {\n return { action: 'status', status: 'thinking' };\n }\n\n if (type === 'RUN_FINISHED') {\n return { action: 'done', content: '' };\n }\n\n if (type === 'RUN_ERROR') {\n return { action: 'error', content: (evt.message as string) || 'Unknown error' };\n }\n\n // --- Step lifecycle ---\n\n if (type === 'STEP_STARTED') {\n const stepName = evt.stepName as string | undefined;\n return { action: 'status', status: stepName || 'thinking' };\n }\n\n if (type === 'STEP_FINISHED') {\n return { action: 'noop' };\n }\n\n // --- Text message streaming ---\n\n if (type === 'TEXT_MESSAGE_START') {\n return { action: 'status', status: 'streaming' };\n }\n\n if (type === 'TEXT_MESSAGE_CONTENT') {\n const delta = evt.delta as string;\n if (delta) {\n return { action: 'stream', content: delta };\n }\n return { action: 'noop' };\n }\n\n if (type === 'TEXT_MESSAGE_END') {\n return { action: 'noop' };\n }\n\n // TEXT_MESSAGE_CHUNK is a convenience event that combines Start+Content+End\n if (type === 'TEXT_MESSAGE_CHUNK') {\n const delta = evt.delta as string | undefined;\n if (delta) {\n return { action: 'stream', content: delta };\n }\n return { action: 'noop' };\n }\n\n // --- Tool call lifecycle ---\n\n if (type === 'TOOL_CALL_START') {\n ctx.hasUsedTools = true;\n const toolName = evt.toolCallName as string | undefined;\n return { action: 'status', status: 'tool_start', tools: toolName ? [toolName] : [] };\n }\n\n if (type === 'TOOL_CALL_ARGS') {\n // Tool arguments streaming — no UI equivalent, skip\n return { action: 'noop' };\n }\n\n if (type === 'TOOL_CALL_END') {\n return { action: 'status', status: 'analyzing' };\n }\n\n if (type === 'TOOL_CALL_RESULT') {\n // Tool result — no direct UI mapping, skip\n return { action: 'noop' };\n }\n\n if (type === 'TOOL_CALL_CHUNK') {\n // Convenience form — treat like TOOL_CALL_START if it has a name\n const toolName = evt.toolCallName as string | undefined;\n if (toolName) {\n ctx.hasUsedTools = true;\n return { action: 'status', status: 'tool_start', tools: [toolName] };\n }\n return { action: 'noop' };\n }\n\n // --- Reasoning / thinking ---\n\n if (type === 'REASONING_START' || type === 'REASONING_MESSAGE_START') {\n return { action: 'status', status: 'thinking' };\n }\n\n if (type === 'REASONING_MESSAGE_CONTENT' || type === 'REASONING_MESSAGE_CHUNK') {\n // Reasoning text — show as thinking status (content not surfaced to chat)\n return { action: 'status', status: 'thinking' };\n }\n\n if (type === 'REASONING_MESSAGE_END' || type === 'REASONING_END' || type === 'REASONING_ENCRYPTED_VALUE') {\n return { action: 'noop' };\n }\n\n // --- State management ---\n\n if (type === 'STATE_SNAPSHOT' || type === 'STATE_DELTA' || type === 'MESSAGES_SNAPSHOT') {\n // State sync — not mapped to chat UI currently\n return { action: 'noop' };\n }\n\n // --- Activity events ---\n\n if (type === 'ACTIVITY_SNAPSHOT' || type === 'ACTIVITY_DELTA') {\n return { action: 'noop' };\n }\n\n // --- Pass-through / custom ---\n\n if (type === 'RAW' || type === 'CUSTOM') {\n return { action: 'noop' };\n }\n\n return { action: 'noop' };\n}\n","import { useRef, useState } from 'react';\nimport type { AgentStatusState, ApiEndpoints, BackendType, ChatFile, ChatMessage } from '../types';\nimport type { ParsedAction, ProtocolContext } from './protocols';\nimport { parseAgUiEvent, parseLegacyEvent, parseRestEvent } from './protocols';\n\nconst STORAGE_KEY = 'filigranChatConversationId';\nconst LEGACY_CHAT_ID_KEY = 'filigranChatLegacyChatId';\n\n/** Maximum number of files that can be attached to a single message. */\nconst DEFAULT_MAX_FILE_COUNT = 10;\n/** Maximum total size of all attached files (50 MB). */\nconst DEFAULT_MAX_TOTAL_SIZE = 50 * 1024 * 1024;\n\ninterface UseChatOptions {\n apiBaseUrl: string;\n apiEndpoints?: ApiEndpoints;\n backendType?: BackendType;\n agentSlug: string | null | undefined;\n requestHeaders?: Record<string, string>;\n t: (key: string) => string;\n maxFileCount?: number;\n maxTotalSize?: number;\n}\n\nexport interface TransferredAgent {\n id: string;\n name: string;\n}\n\ninterface UseChatReturn {\n messages: ChatMessage[];\n inputValue: string;\n setInputValue: (value: string) => void;\n isLoading: boolean;\n agentStatus: AgentStatusState | null;\n attachedFiles: ChatFile[];\n conversationId: string | null;\n transferredAgent: TransferredAgent | null;\n historyLoadedRef: React.MutableRefObject<boolean>;\n handleFileAdd: (fileList: FileList | null) => void;\n handlePaste: (e: React.ClipboardEvent) => void;\n handleSendMessage: () => Promise<void>;\n handleNewChat: () => void;\n handleStopGenerating: () => void;\n setAttachedFiles: React.Dispatch<React.SetStateAction<ChatFile[]>>;\n setMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>;\n setConversationId: React.Dispatch<React.SetStateAction<string | null>>;\n}\n\nfunction getParser(backendType: BackendType): (evt: Record<string, unknown>, ctx: ProtocolContext) => ParsedAction {\n switch (backendType) {\n case 'legacy':\n return parseLegacyEvent;\n case 'ag-ui':\n return parseAgUiEvent;\n default:\n return parseRestEvent;\n }\n}\n\nfunction buildRequestBody(\n backendType: BackendType,\n content: string,\n opts: { legacyChatId: string | null; conversationId: string | null; agentSlug: string | null | undefined },\n): Record<string, unknown> {\n switch (backendType) {\n case 'legacy':\n return { question: content, chatId: opts.legacyChatId ?? undefined, streaming: true };\n case 'ag-ui':\n return {\n threadId: opts.conversationId ?? crypto.randomUUID(),\n runId: crypto.randomUUID(),\n messages: [{ id: crypto.randomUUID(), role: 'user', content }],\n tools: [],\n context: [],\n state: {},\n forwardedProps: opts.agentSlug ? { agentSlug: opts.agentSlug } : {},\n };\n default:\n return { content, conversation_id: opts.conversationId, agent_slug: opts.agentSlug };\n }\n}\n\nexport function useChat({\n apiBaseUrl,\n apiEndpoints,\n backendType = 'rest',\n agentSlug,\n requestHeaders,\n t,\n maxFileCount = DEFAULT_MAX_FILE_COUNT,\n maxTotalSize = DEFAULT_MAX_TOTAL_SIZE,\n}: UseChatOptions): UseChatReturn {\n const isLegacy = backendType === 'legacy';\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [inputValue, setInputValue] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [agentStatus, setAgentStatus] = useState<AgentStatusState | null>(null);\n const [conversationId, setConversationId] = useState<string | null>(() => {\n if (typeof window === 'undefined') return null;\n return localStorage.getItem(STORAGE_KEY);\n });\n const [attachedFiles, setAttachedFiles] = useState<ChatFile[]>([]);\n const [transferredAgent, setTransferredAgent] = useState<TransferredAgent | null>(null);\n const [legacyChatId, setLegacyChatId] = useState<string | null>(() => {\n if (typeof window === 'undefined') return null;\n return localStorage.getItem(LEGACY_CHAT_ID_KEY);\n });\n\n const historyLoadedRef = useRef(false);\n const abortControllerRef = useRef<AbortController | null>(null);\n const hasUsedToolsRef = useRef(false);\n // Ref mirror of conversationId — always current across async boundaries\n const conversationIdRef = useRef(conversationId);\n // Mutex to prevent concurrent session creation\n const creatingSessionRef = useRef<Promise<string | null> | null>(null);\n // Abort controller for in-flight file uploads (cancelled on new chat)\n const uploadAbortRef = useRef<AbortController>(new AbortController());\n\n // Guard invalid consumer values and keep deterministic limits.\n const effectiveMaxFileCount = Number.isFinite(maxFileCount) && maxFileCount > 0 ? Math.floor(maxFileCount) : DEFAULT_MAX_FILE_COUNT;\n const effectiveMaxTotalSize = Number.isFinite(maxTotalSize) && maxTotalSize > 0 ? maxTotalSize : DEFAULT_MAX_TOTAL_SIZE;\n\n // Determine message endpoint URL\n const getMessagesUrl = () => {\n if (isLegacy || apiEndpoints?.singleEndpoint) {\n return apiBaseUrl; // POST directly to base URL\n }\n return `${apiBaseUrl}${apiEndpoints?.messages ?? '/chat/messages'}`;\n };\n\n // Determine upload endpoint URL (null disables file upload proxying)\n const getUploadUrl = (): string | null => {\n if (isLegacy || apiEndpoints?.singleEndpoint || apiEndpoints?.upload === null) {\n return null;\n }\n return `${apiBaseUrl}${apiEndpoints?.upload ?? '/chat/upload'}`;\n };\n\n // Determine sessions endpoint URL\n const getSessionsUrl = (): string | null => {\n if (isLegacy || apiEndpoints?.singleEndpoint || apiEndpoints?.sessions === null) {\n return null;\n }\n return `${apiBaseUrl}${apiEndpoints?.sessions ?? '/chat/sessions'}`;\n };\n\n /**\n * Update conversationId in both React state and the ref mirror.\n */\n const updateConversationId = (id: string | null) => {\n conversationIdRef.current = id;\n setConversationId(id);\n if (id) {\n localStorage.setItem(STORAGE_KEY, id);\n } else {\n localStorage.removeItem(STORAGE_KEY);\n }\n };\n\n /**\n * Ensure a conversation exists. Uses a mutex so concurrent callers\n * (e.g. multiple files selected at once) share a single session creation.\n */\n const ensureConversation = async (slug: string | null | undefined): Promise<string | null> => {\n // Fast path: already have one\n if (conversationIdRef.current) return conversationIdRef.current;\n\n // If another call is already creating, wait for it\n if (creatingSessionRef.current) return creatingSessionRef.current;\n\n const sessionsUrl = getSessionsUrl();\n if (!sessionsUrl) return null;\n\n const promise = (async () => {\n try {\n const res = await fetch(sessionsUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(requestHeaders ?? {}) },\n body: JSON.stringify({ agent_slug: slug }),\n });\n if (!res.ok) return null;\n const data = await res.json();\n const convId = (data?.conversation_id as string) ?? null;\n if (convId) {\n updateConversationId(convId);\n }\n return convId;\n } catch {\n return null;\n } finally {\n creatingSessionRef.current = null;\n }\n })();\n\n creatingSessionRef.current = promise;\n return promise;\n };\n\n /**\n * Upload a single file to the backend and return its file_id.\n */\n const uploadSingleFile = async (file: File, convId: string, signal: AbortSignal): Promise<string> => {\n const uploadUrl = getUploadUrl()!;\n const formData = new FormData();\n formData.append('conversation_id', convId);\n formData.append('file', file, file.name);\n\n const uploadHeaders = requestHeaders\n ? Object.fromEntries(\n Object.entries(requestHeaders).filter(([k]) => {\n const key = k.toLowerCase();\n return key !== 'content-type';\n }),\n )\n : undefined;\n\n const res = await fetch(uploadUrl, {\n method: 'POST',\n headers: uploadHeaders,\n body: formData,\n signal,\n });\n if (!res.ok) {\n throw new Error(`File upload failed: ${res.status}`);\n }\n const data = await res.json();\n const ids: string[] = data.file_ids ?? [];\n if (ids.length === 0) throw new Error('No file_id returned');\n return ids[0];\n };\n\n /**\n * Handle file selection: validate limits, add files to state immediately,\n * then upload them in the background. Each file chip shows its upload status.\n */\n const handleFileAdd = (fileList: FileList | null) => {\n if (!fileList || fileList.length === 0 || !getUploadUrl()) return;\n\n // Build the list of accepted files outside the state updater (pure logic)\n const incoming = Array.from(fileList);\n\n // We need current state to check limits — use a ref-like approach:\n // read attachedFiles via a one-shot updater that returns prev unchanged,\n // then compute outside. Simpler: just compute optimistically and let the\n // updater do the final gating.\n\n // Pre-generate stable IDs and entries so side effects use the same IDs\n const candidates: { file: File; tempId: string }[] = incoming.map((file) => ({\n file,\n tempId: crypto.randomUUID(),\n }));\n\n // Update state (pure — no side effects)\n let accepted: { file: File; tempId: string }[] = [];\n setAttachedFiles((prev) => {\n const currentCount = prev.length;\n const currentSize = prev.reduce((sum, f) => sum + f.size, 0);\n\n const slotsAvailable = effectiveMaxFileCount - currentCount;\n if (slotsAvailable <= 0) return prev;\n\n let sizeLeft = effectiveMaxTotalSize - currentSize;\n const filtered: { file: File; tempId: string }[] = [];\n for (const c of candidates.slice(0, slotsAvailable)) {\n if (c.file.size <= sizeLeft) {\n filtered.push(c);\n sizeLeft -= c.file.size;\n }\n }\n if (filtered.length === 0) return prev;\n\n accepted = filtered;\n\n const newEntries: ChatFile[] = filtered.map(({ file, tempId }) => ({\n name: file.name,\n type: file.type,\n size: file.size,\n rawFile: file,\n uploadStatus: 'pending' as const,\n fileId: tempId,\n }));\n\n return [...prev, ...newEntries];\n });\n\n // Launch uploads OUTSIDE the state updater (side effects)\n // Use setTimeout(0) to ensure state has settled after the updater\n setTimeout(() => {\n const signal = uploadAbortRef.current.signal;\n for (const { file, tempId } of accepted) {\n (async () => {\n try {\n const convId = await ensureConversation(agentSlug);\n if (!convId) {\n setAttachedFiles((p) => p.map((f) => (f.fileId === tempId ? { ...f, uploadStatus: 'error' } : f)));\n return;\n }\n const fileId = await uploadSingleFile(file, convId, signal);\n setAttachedFiles((p) => p.map((f) => (f.fileId === tempId ? { ...f, fileId, uploadStatus: 'done' } : f)));\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setAttachedFiles((p) => p.map((f) => (f.fileId === tempId ? { ...f, uploadStatus: 'error' } : f)));\n }\n })();\n }\n }, 0);\n };\n\n const handlePaste = (e: React.ClipboardEvent) => {\n const { files } = e.clipboardData;\n if (files.length > 0) {\n e.preventDefault();\n handleFileAdd(files);\n }\n };\n\n const handleSendMessage = async () => {\n if ((!inputValue.trim() && attachedFiles.length === 0) || isLoading) return;\n const content = inputValue.trim();\n\n const userMsg: ChatMessage = {\n id: crypto.randomUUID(),\n role: 'user',\n content,\n timestamp: new Date(),\n files: attachedFiles.length > 0 ? [...attachedFiles] : undefined,\n };\n setMessages((prev) => [...prev, userMsg]);\n setInputValue('');\n // Clear attachment chips after sending so the input returns to a clean state.\n setAttachedFiles([]);\n setIsLoading(true);\n setAgentStatus({ status: 'thinking' });\n hasUsedToolsRef.current = false;\n\n const assistantId = crypto.randomUUID();\n setMessages((prev) => [...prev, { id: assistantId, role: 'assistant', content: '', timestamp: new Date() }]);\n\n try {\n const controller = new AbortController();\n abortControllerRef.current = controller;\n\n // Collect file_ids from already-uploaded files (uploaded eagerly on selection)\n const fileIds = (userMsg.files ?? []).filter((f) => f.uploadStatus === 'done' && f.fileId).map((f) => f.fileId!);\n\n // Step 1: Send the message (with file_ids if files were uploaded)\n // Use conversationIdRef to get the latest value (may have been set by eager upload)\n const requestBody = buildRequestBody(backendType, content, {\n legacyChatId,\n conversationId: conversationIdRef.current,\n agentSlug,\n });\n if (fileIds.length > 0) {\n (requestBody as Record<string, unknown>).file_ids = fileIds;\n }\n\n setAgentStatus({ status: 'thinking' });\n\n const res = await fetch(getMessagesUrl(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(requestHeaders ?? {}) },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n if (!res.ok || !res.body) {\n setMessages((prev) =>\n prev.map((m) => (m.id === assistantId ? { ...m, content: t('Unable to connect. Please check the configuration.') } : m)),\n );\n return;\n }\n\n const parseEvent = getParser(backendType);\n const ctx: ProtocolContext = { hasUsedTools: false, activeNodeId: '' };\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let accumulated = '';\n let doneReceived = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n for (const rawLine of lines) {\n const line = rawLine.replace(/\\r$/, '');\n if (!line.startsWith('data:')) continue;\n const jsonStr = line.startsWith('data: ') ? line.slice(6) : line.slice(5);\n try {\n const evt = JSON.parse(jsonStr) as Record<string, unknown>;\n const parsed: ParsedAction = parseEvent(evt, ctx);\n\n // Sync ref → context for cross-event tracking\n ctx.hasUsedTools = ctx.hasUsedTools || hasUsedToolsRef.current;\n\n switch (parsed.action) {\n case 'status':\n if (parsed.status === 'tool_start') hasUsedToolsRef.current = true;\n if (parsed.status === 'thinking_text') {\n setAgentStatus((prev) => ({\n ...prev,\n status: prev?.status ?? 'thinking',\n thinkingContent: (prev?.thinkingContent ?? '') + (parsed.thinkingContent ?? ''),\n }));\n } else {\n setAgentStatus((prev) => ({\n status: parsed.status,\n tools: parsed.tools,\n thinkingContent: prev?.thinkingContent,\n }));\n }\n break;\n\n case 'stream':\n accumulated += parsed.content;\n setAgentStatus((prev) => ({ status: 'streaming', thinkingContent: prev?.thinkingContent }));\n setMessages((prev) => prev.map((m) => (m.id === assistantId ? { ...m, content: accumulated } : m)));\n break;\n\n case 'done':\n doneReceived = true;\n if (parsed.conversationId) {\n updateConversationId(parsed.conversationId);\n }\n if (parsed.transferAgentId && parsed.transferAgentName) {\n setTransferredAgent({ id: parsed.transferAgentId, name: parsed.transferAgentName });\n }\n setMessages((prev) =>\n prev.map((m) =>\n m.id === assistantId\n ? {\n ...m,\n content: parsed.content || accumulated,\n toolNames: parsed.toolNames,\n toolCallCount: parsed.toolCallCount,\n iterations: parsed.iterations,\n attachments: parsed.attachments,\n }\n : m,\n ),\n );\n break;\n\n case 'error':\n setMessages((prev) =>\n prev.map((m) =>\n m.id === assistantId ? { ...m, content: parsed.content || t('Unable to connect. Please check the configuration.') } : m,\n ),\n );\n return;\n\n case 'set_chat_id':\n setLegacyChatId(parsed.chatId);\n localStorage.setItem(LEGACY_CHAT_ID_KEY, parsed.chatId);\n break;\n\n case 'noop':\n break;\n }\n\n // Keep ref in sync with context\n hasUsedToolsRef.current = ctx.hasUsedTools;\n } catch {\n /* skip malformed SSE */\n }\n }\n }\n if (accumulated && !doneReceived) {\n setMessages((prev) => prev.map((m) => (m.id === assistantId ? { ...m, content: accumulated || 'No response.' } : m)));\n }\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setMessages((prev) => prev.map((m) => (m.id === assistantId ? { ...m, content: t('Sorry, an error occurred. Please try again.') } : m)));\n } finally {\n abortControllerRef.current = null;\n setIsLoading(false);\n setAgentStatus(null);\n hasUsedToolsRef.current = false;\n }\n };\n\n const handleNewChat = () => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n // Cancel any in-flight file uploads\n uploadAbortRef.current.abort();\n uploadAbortRef.current = new AbortController();\n creatingSessionRef.current = null;\n setMessages([]);\n setInputValue('');\n setAttachedFiles([]);\n setIsLoading(false);\n setAgentStatus(null);\n setTransferredAgent(null);\n hasUsedToolsRef.current = false;\n historyLoadedRef.current = false;\n if (isLegacy) {\n setLegacyChatId(null);\n localStorage.removeItem(LEGACY_CHAT_ID_KEY);\n } else {\n updateConversationId(null);\n }\n };\n\n const handleStopGenerating = () => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n setIsLoading(false);\n setAgentStatus(null);\n hasUsedToolsRef.current = false;\n setMessages((prev) => prev.filter((m) => !(m.role === 'assistant' && !m.content)));\n };\n\n return {\n messages,\n inputValue,\n setInputValue,\n isLoading,\n agentStatus,\n attachedFiles,\n conversationId,\n transferredAgent,\n historyLoadedRef,\n handleFileAdd,\n handlePaste,\n handleSendMessage,\n handleNewChat,\n handleStopGenerating,\n setAttachedFiles,\n setMessages,\n setConversationId,\n };\n}\n","import { useEffect, useState } from 'react';\nimport type { ApiEndpoints, BackendType, XtmAgent } from '../types';\n\nconst STORAGE_AGENT_KEY = 'filigranChatAgentSlug';\n\ninterface UseAgentsOptions {\n apiBaseUrl: string;\n apiEndpoints?: ApiEndpoints;\n backendType?: BackendType;\n requestHeaders?: Record<string, string>;\n}\n\ninterface UseAgentsReturn {\n agents: XtmAgent[];\n selectedAgent: XtmAgent | null;\n setSelectedAgent: React.Dispatch<React.SetStateAction<XtmAgent | null>>;\n agentMenuOpen: boolean;\n setAgentMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;\n handleSwitchAgent: (agent: XtmAgent, onSwitch?: () => void) => void;\n}\n\nexport function useAgents({ apiBaseUrl, apiEndpoints, backendType = 'rest', requestHeaders }: UseAgentsOptions): UseAgentsReturn {\n const [agents, setAgents] = useState<XtmAgent[]>([]);\n const [selectedAgent, setSelectedAgent] = useState<XtmAgent | null>(null);\n const [agentMenuOpen, setAgentMenuOpen] = useState(false);\n\n useEffect(() => {\n // Skip agents fetch if disabled, using single endpoint mode, or legacy backend\n if (apiEndpoints?.agents === null || apiEndpoints?.singleEndpoint || backendType === 'legacy') {\n return;\n }\n const agentsUrl = `${apiBaseUrl}${apiEndpoints?.agents ?? '/chat/agents'}`;\n fetch(agentsUrl, { headers: requestHeaders })\n .then((res) => (res.ok ? res.json() : []))\n .then((data: XtmAgent[]) => {\n setAgents(data);\n if (data.length > 0 && !selectedAgent) {\n const savedSlug = localStorage.getItem(STORAGE_AGENT_KEY);\n const match = savedSlug ? data.find((a) => a.slug === savedSlug) : null;\n setSelectedAgent(match || data[0]);\n }\n })\n .catch(() => {});\n }, [apiBaseUrl, apiEndpoints, backendType, requestHeaders]);\n\n const handleSwitchAgent = (agent: XtmAgent, onSwitch?: () => void) => {\n if (agent.id === selectedAgent?.id) {\n setAgentMenuOpen(false);\n return;\n }\n setSelectedAgent(agent);\n if (agent.slug) localStorage.setItem(STORAGE_AGENT_KEY, agent.slug);\n setAgentMenuOpen(false);\n onSwitch?.();\n };\n\n return {\n agents,\n selectedAgent,\n setSelectedAgent,\n agentMenuOpen,\n setAgentMenuOpen,\n handleSwitchAgent,\n };\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { ChatMode } from '../types';\n\nconst SIDEBAR_WIDTH = 400;\nconst SIDEBAR_WIDTH_STORAGE_KEY = 'filigranChatSidebarWidth';\nconst MAX_SIDEBAR_RATIO = 0.4;\n\ninterface UseSidebarResizeOptions {\n mode: ChatMode;\n resizable: boolean;\n onWidthChange?: (width: number) => void;\n onResizeStart?: () => void;\n onResizeEnd?: () => void;\n}\n\ninterface UseSidebarResizeReturn {\n sidebarWidth: number;\n handleResizeStart: (e: React.MouseEvent) => void;\n defaultWidth: number;\n isResizing: boolean;\n}\n\nexport function useSidebarResize({ mode, resizable, onWidthChange, onResizeStart, onResizeEnd }: UseSidebarResizeOptions): UseSidebarResizeReturn {\n const [sidebarWidth, setSidebarWidth] = useState<number>(() => {\n if (typeof window === 'undefined') return SIDEBAR_WIDTH;\n const stored = localStorage.getItem(SIDEBAR_WIDTH_STORAGE_KEY);\n if (stored) {\n const parsed = parseInt(stored, 10);\n if (!Number.isNaN(parsed) && parsed >= SIDEBAR_WIDTH) return parsed;\n }\n return SIDEBAR_WIDTH;\n });\n const [isResizing, setIsResizing] = useState(false);\n\n const isResizingRef = useRef(false);\n const sidebarWidthRef = useRef(sidebarWidth);\n sidebarWidthRef.current = sidebarWidth;\n const onWidthChangeRef = useRef(onWidthChange);\n onWidthChangeRef.current = onWidthChange;\n const onResizeEndRef = useRef(onResizeEnd);\n onResizeEndRef.current = onResizeEnd;\n\n // Notify parent of sidebar width when entering sidebar mode\n useEffect(() => {\n if (mode === 'sidebar' && resizable) {\n onWidthChangeRef.current?.(sidebarWidthRef.current);\n }\n }, [mode, resizable]);\n\n // Resize event handlers\n useEffect(() => {\n if (mode !== 'sidebar' || !resizable) return undefined;\n\n const handleMouseMove = (e: MouseEvent) => {\n if (!isResizingRef.current) return;\n e.preventDefault();\n const newWidth = window.innerWidth - e.clientX;\n const maxWidth = window.innerWidth * MAX_SIDEBAR_RATIO;\n const clamped = Math.min(Math.max(newWidth, SIDEBAR_WIDTH), maxWidth);\n setSidebarWidth(clamped);\n sidebarWidthRef.current = clamped;\n onWidthChangeRef.current?.(clamped);\n };\n\n const handleMouseUp = () => {\n if (!isResizingRef.current) return;\n isResizingRef.current = false;\n setIsResizing(false);\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n localStorage.setItem(SIDEBAR_WIDTH_STORAGE_KEY, String(sidebarWidthRef.current));\n onResizeEndRef.current?.();\n };\n\n const handleWindowResize = () => {\n const maxWidth = window.innerWidth * MAX_SIDEBAR_RATIO;\n if (sidebarWidthRef.current > maxWidth) {\n const clamped = Math.max(maxWidth, SIDEBAR_WIDTH);\n setSidebarWidth(clamped);\n sidebarWidthRef.current = clamped;\n onWidthChangeRef.current?.(clamped);\n }\n };\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n window.addEventListener('resize', handleWindowResize);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n window.removeEventListener('resize', handleWindowResize);\n };\n }, [mode, resizable]);\n\n const handleResizeStart = (e: React.MouseEvent) => {\n e.preventDefault();\n isResizingRef.current = true;\n setIsResizing(true);\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n onResizeStart?.();\n };\n\n return {\n sidebarWidth,\n handleResizeStart,\n defaultWidth: SIDEBAR_WIDTH,\n isResizing,\n };\n}\n","import type { IconProps } from '../../types';\n\nexport const AttachFileIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const BrainIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z\" />\n <path d=\"M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z\" />\n <path d=\"M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4\" />\n <path d=\"M17.599 6.5a3 3 0 0 0 .399-1.375\" />\n <path d=\"M6.003 5.125A3 3 0 0 0 6.401 6.5\" />\n <path d=\"M3.477 10.896a4 4 0 0 1 .585-.396\" />\n <path d=\"M19.938 10.5a4 4 0 0 1 .585.396\" />\n <path d=\"M6 18a4 4 0 0 1-1.967-.516\" />\n <path d=\"M19.967 17.484A4 4 0 0 1 18 18\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const CheckIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const ChevronDownIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const CloseIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const CopyIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const DatabaseIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" />\n <path d=\"M3 5V19A9 3 0 0 0 21 19V5\" />\n <path d=\"M3 12A9 3 0 0 0 21 12\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const DefaultLogoIcon = ({ className, size = 24 }: IconProps) => (\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"currentColor\" stroke=\"none\" className={className}>\n <path d=\"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const DownloadIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" x2=\"12\" y1=\"15\" y2=\"3\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const EditIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M12 20h9\" />\n <path d=\"M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const ExternalLinkIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M15 3h6v6\" />\n <path d=\"M10 14 21 3\" />\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const FileIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const FloatingIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M11 13H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h7\" />\n <rect width=\"12\" height=\"12\" x=\"10\" y=\"10\" rx=\"2\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const FullscreenExitIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3\" />\n <path d=\"M21 8h-3a2 2 0 0 1-2-2V3\" />\n <path d=\"M3 16h3a2 2 0 0 0 2 2v3\" />\n <path d=\"M16 21v-3a2 2 0 0 1 2-2h3\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const FullscreenIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3\" />\n <path d=\"M21 8V5a2 2 0 0 0-2-2h-3\" />\n <path d=\"M3 16v3a2 2 0 0 0 2 2h3\" />\n <path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const GlobeIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\" />\n <path d=\"M2 12h20\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const InfoIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const MailIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n <path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const SearchIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const SendIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"m22 2-7 20-4-9-9-4Z\" />\n <path d=\"m22 2-11 11\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const SidebarIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\n <path d=\"M15 3v18\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const SparklesIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const StopCircleIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <rect width=\"6\" height=\"6\" x=\"9\" y=\"9\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const TerminalIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <polyline points=\"4 17 10 11 4 5\" />\n <line x1=\"12\" x2=\"20\" y1=\"19\" y2=\"19\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const UserPlusIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2\" />\n <circle cx=\"9\" cy=\"7\" r=\"4\" />\n <line x1=\"19\" x2=\"19\" y1=\"8\" y2=\"14\" />\n <line x1=\"22\" x2=\"16\" y1=\"11\" y2=\"11\" />\n </svg>\n);\n","import type { IconProps } from '../../types';\n\nexport const WrenchIcon = ({ className, size = 24 }: IconProps) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z\" />\n </svg>\n);\n","import { useCallback, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useClickOutside } from '../hooks/useClickOutside';\n\ninterface DropdownProps {\n open: boolean;\n onClose: () => void;\n anchorRef: React.RefObject<HTMLElement | null>;\n placement?: 'bottom-start' | 'bottom-end';\n width?: number;\n children: React.ReactNode;\n}\n\nfunction findChatbotRoot(el: HTMLElement | null): HTMLElement {\n let node = el;\n while (node) {\n if (node.classList.contains('filigran-chatbot')) return node;\n node = node.parentElement;\n }\n return document.body;\n}\n\nexport const Dropdown = ({ open, onClose, anchorRef, placement = 'bottom-start', width = 280, children }: DropdownProps) => {\n const panelRef = useRef<HTMLDivElement>(null);\n const [pos, setPos] = useState({ top: 0, left: 0 });\n\n const stableOnClose = useCallback(() => onClose(), [onClose]);\n useClickOutside(panelRef, stableOnClose, open);\n\n useEffect(() => {\n if (!open || !anchorRef.current) return;\n const rect = anchorRef.current.getBoundingClientRect();\n const left = placement === 'bottom-end' ? rect.right - width : rect.left;\n setPos({ top: rect.bottom + 4, left });\n }, [open, anchorRef, placement, width]);\n\n if (!open) return null;\n\n const portalTarget = findChatbotRoot(anchorRef.current);\n\n return createPortal(\n <div\n ref={panelRef}\n className=\"fixed z-[10000] rounded-[10px] overflow-hidden border border-gray-200 dark:border-white/10 bg-white dark:bg-[#2a2a3e] shadow-xl\"\n style={{ top: pos.top, left: pos.left, width }}\n >\n {children}\n </div>,\n portalTarget,\n );\n};\n","import { useEffect, type RefObject } from 'react';\n\nexport function useClickOutside(ref: RefObject<HTMLElement | null>, handler: () => void, active = true) {\n useEffect(() => {\n if (!active) return undefined;\n const listener = (e: MouseEvent | TouchEvent) => {\n if (!ref.current || ref.current.contains(e.target as Node)) return;\n handler();\n };\n document.addEventListener('mousedown', listener);\n document.addEventListener('touchstart', listener);\n return () => {\n document.removeEventListener('mousedown', listener);\n document.removeEventListener('touchstart', listener);\n };\n }, [ref, handler, active]);\n}\n","interface SpinnerProps {\n size?: number;\n className?: string;\n}\n\nexport const Spinner = ({ size = 16, className = '' }: SpinnerProps) => (\n <div\n className={`animate-spin rounded-full border-2 border-current/20 border-t-[var(--chat-accent)] ${className}`}\n style={{ width: size, height: size }}\n />\n);\n","import { useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\ninterface TooltipProps {\n title: string;\n children: React.ReactElement;\n}\n\nfunction findChatbotRoot(el: HTMLElement | null): HTMLElement {\n let node = el;\n while (node) {\n if (node.classList.contains('filigran-chatbot')) return node;\n node = node.parentElement;\n }\n return document.body;\n}\n\nexport const Tooltip = ({ title, children }: TooltipProps) => {\n const ref = useRef<HTMLSpanElement>(null);\n const [show, setShow] = useState(false);\n const [pos, setPos] = useState({ top: 0, left: 0 });\n\n if (!title) return children;\n\n const handleEnter = () => {\n if (!ref.current) return;\n const rect = ref.current.getBoundingClientRect();\n setPos({\n top: rect.top - 4,\n left: rect.left + rect.width / 2,\n });\n setShow(true);\n };\n\n return (\n <span ref={ref} className=\"inline-flex\" onMouseEnter={handleEnter} onMouseLeave={() => setShow(false)}>\n {children}\n {show &&\n createPortal(\n <span\n className=\"pointer-events-none fixed z-[10001] -translate-x-1/2 -translate-y-full whitespace-nowrap rounded-md bg-gray-900 dark:bg-gray-100 px-2 py-1 text-xs text-white dark:text-gray-900 shadow-lg\"\n style={{ top: pos.top, left: pos.left }}\n role=\"tooltip\"\n >\n {title}\n </span>,\n findChatbotRoot(ref.current),\n )}\n </span>\n );\n};\n","import { useRef } from 'react';\nimport type { ChatMode, XtmAgent } from '../types';\nimport {\n ChevronDownIcon,\n CloseIcon,\n EditIcon,\n ExternalLinkIcon,\n FloatingIcon,\n FullscreenExitIcon,\n FullscreenIcon,\n SidebarIcon,\n UserPlusIcon,\n} from './icons';\nimport { Dropdown } from './Dropdown';\nimport { Spinner } from './Spinner';\nimport { Tooltip } from './Tooltip';\n\ninterface ChatHeaderProps {\n mode: ChatMode;\n agentName: string;\n agents: XtmAgent[];\n selectedAgent: XtmAgent | null;\n transferredFrom?: string;\n agentMenuOpen: boolean;\n onAgentMenuToggle: () => void;\n onAgentMenuClose: () => void;\n onSwitchAgent: (agent: XtmAgent) => void;\n modeMenuOpen: boolean;\n onModeMenuToggle: () => void;\n onModeMenuClose: () => void;\n onModeChange: (mode: ChatMode) => void;\n onNewChat: () => void;\n onClose: () => void;\n logoIcon: React.ReactNode;\n agentDashboardUrl?: string;\n t: (key: string) => string;\n}\n\nconst modeOptions: { mode: ChatMode; label: string; getIcon: (p: { size: number; className: string }) => React.ReactNode }[] = [\n { mode: 'floating', label: 'Floating', getIcon: (p) => <FloatingIcon {...p} /> },\n { mode: 'sidebar', label: 'Sidebar', getIcon: (p) => <SidebarIcon {...p} /> },\n { mode: 'fullscreen', label: 'Full screen', getIcon: (p) => <FullscreenIcon {...p} /> },\n];\n\nexport const ChatHeader = ({\n mode,\n agentName,\n agents,\n selectedAgent,\n transferredFrom,\n agentMenuOpen,\n onAgentMenuToggle,\n onAgentMenuClose,\n onSwitchAgent,\n modeMenuOpen,\n onModeMenuToggle,\n onModeMenuClose,\n onModeChange,\n onNewChat,\n onClose,\n logoIcon,\n agentDashboardUrl,\n t,\n}: ChatHeaderProps) => {\n const agentAnchorRef = useRef<HTMLButtonElement>(null);\n const modeAnchorRef = useRef<HTMLButtonElement>(null);\n\n const CurrentModeIcon = mode === 'sidebar' ? SidebarIcon : mode === 'fullscreen' ? FullscreenExitIcon : FloatingIcon;\n\n return (\n <div\n className={`flex items-center px-3 py-2 min-h-[48px] border-b border-gray-200 dark:border-white/10 bg-gradient-to-br from-[var(--chat-accent-dark)]/[0.13] to-[var(--chat-accent)]/[0.07] ${mode === 'floating' ? 'rounded-t-xl' : ''}`}\n >\n <div className=\"min-w-0\">\n <button\n ref={agentAnchorRef}\n type=\"button\"\n onClick={onAgentMenuToggle}\n className=\"flex items-center gap-1.5 text-sm font-semibold text-gray-900 dark:text-white px-2 py-1 rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 transition-colors\"\n >\n <span className=\"flex items-center text-[var(--chat-accent)] [&>svg]:w-[18px] [&>svg]:h-[18px]\">{logoIcon}</span>\n <span>{agentName}</span>\n <ChevronDownIcon size={16} className=\"text-gray-400 dark:text-white/30\" />\n </button>\n {transferredFrom && (\n <div className=\"pl-10 pr-2 text-[0.6rem] font-normal text-gray-400 dark:text-white/30\">\n {t('Transferred from')} {transferredFrom}\n </div>\n )}\n </div>\n\n <Dropdown open={agentMenuOpen} onClose={onAgentMenuClose} anchorRef={agentAnchorRef} width={280}>\n <span className=\"block px-4 pt-3 pb-1 text-[0.68rem] tracking-[1px] uppercase text-gray-400 dark:text-white/40\">\n {t('Switch to another agent')}\n </span>\n {agents.length === 0 && (\n <div className=\"px-4 py-2\">\n <Spinner size={16} />\n </div>\n )}\n <div>\n {agents.map((agent) => (\n <button\n key={agent.id}\n type=\"button\"\n onClick={() => onSwitchAgent(agent)}\n className={`w-full flex items-center gap-2 px-4 py-1.5 text-left hover:bg-gray-100 dark:hover:bg-white/10 transition-colors ${\n agent.id === selectedAgent?.id ? 'bg-[var(--chat-accent)]/10' : ''\n }`}\n >\n <div className=\"w-7 h-7 rounded-full flex items-center justify-center shrink-0 bg-gradient-to-br from-[var(--chat-accent)]/20 to-[var(--chat-accent)]/5\">\n <span className=\"text-[var(--chat-accent)] [&>svg]:w-4 [&>svg]:h-4\">{logoIcon}</span>\n </div>\n <div className=\"min-w-0\">\n <div className=\"text-[0.8125rem] font-medium text-gray-900 dark:text-white truncate\">{agent.name}</div>\n {agent.description && <div className=\"text-[0.7rem] text-gray-500 dark:text-white/40 truncate\">{agent.description}</div>}\n </div>\n </button>\n ))}\n </div>\n <div className=\"h-px bg-gray-200 dark:bg-white/10 mx-2\" />\n <div>\n {agentDashboardUrl && (\n <button\n type=\"button\"\n onClick={() => {\n onAgentMenuClose();\n window.open(`${agentDashboardUrl}/agents`, '_blank');\n }}\n className=\"w-full flex items-center gap-2 px-4 py-1.5 text-left hover:bg-gray-100 dark:hover:bg-white/10 transition-colors\"\n >\n <ExternalLinkIcon size={18} className=\"text-gray-400 dark:text-white/40 shrink-0\" />\n <span className=\"text-[0.8125rem] text-gray-700 dark:text-white/70\">{t('Browse agents')}</span>\n </button>\n )}\n {agentDashboardUrl && (\n <button\n type=\"button\"\n onClick={() => {\n onAgentMenuClose();\n window.open(`${agentDashboardUrl}/agents/new`, '_blank');\n }}\n className=\"w-full flex items-center gap-2 px-4 py-1.5 text-left hover:bg-gray-100 dark:hover:bg-white/10 transition-colors\"\n >\n <UserPlusIcon size={18} className=\"text-gray-400 dark:text-white/40 shrink-0\" />\n <span className=\"text-[0.8125rem] text-gray-700 dark:text-white/70\">{t('Create agent')}</span>\n </button>\n )}\n </div>\n </Dropdown>\n\n <div className=\"flex-1\" />\n\n <Tooltip title={t('New chat')}>\n <button\n type=\"button\"\n onClick={onNewChat}\n className=\"w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 text-gray-500 dark:text-white/40 hover:text-gray-700 dark:hover:text-white/70 transition-colors\"\n >\n <EditIcon size={18} />\n </button>\n </Tooltip>\n\n <Tooltip title={t('Switch view')}>\n <button\n ref={modeAnchorRef}\n type=\"button\"\n onClick={onModeMenuToggle}\n className=\"w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 text-gray-500 dark:text-white/40 hover:text-gray-700 dark:hover:text-white/70 transition-colors\"\n >\n <CurrentModeIcon size={18} />\n </button>\n </Tooltip>\n\n <Dropdown open={modeMenuOpen} onClose={onModeMenuClose} anchorRef={modeAnchorRef} placement=\"bottom-end\" width={180}>\n <span className=\"block px-4 pt-3 pb-1 text-[0.68rem] tracking-[1px] uppercase text-gray-400 dark:text-white/40\">{t('Switch to')}</span>\n <div className=\"pb-1\">\n {modeOptions.map((opt) => (\n <button\n key={opt.mode}\n type=\"button\"\n onClick={() => {\n onModeChange(opt.mode);\n onModeMenuClose();\n }}\n className={`w-full flex items-center gap-3 px-4 py-1 text-left hover:bg-gray-100 dark:hover:bg-white/10 transition-colors ${\n mode === opt.mode ? 'bg-[var(--chat-accent)]/10' : ''\n }`}\n >\n {opt.getIcon({ size: 18, className: 'text-gray-400 dark:text-white/40' })}\n <span className=\"text-[0.8125rem] text-gray-700 dark:text-white/70\">{t(opt.label)}</span>\n </button>\n ))}\n </div>\n </Dropdown>\n\n <Tooltip title={t('Close')}>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"w-8 h-8 flex items-center justify-center rounded-lg hover:bg-gray-100 dark:hover:bg-white/10 text-gray-500 dark:text-white/40 hover:text-gray-700 dark:hover:text-white/70 transition-colors\"\n >\n <CloseIcon size={18} />\n </button>\n </Tooltip>\n </div>\n );\n};\n","import { useRef, type KeyboardEvent } from 'react';\nimport type { ChatFile, ChatMode } from '../types';\nimport { AttachFileIcon, FileIcon, SendIcon, StopCircleIcon } from './icons';\nimport { Tooltip } from './Tooltip';\n\ninterface ChatInputProps {\n inputValue: string;\n onInputChange: (value: string) => void;\n onSend: () => void;\n onStop: () => void;\n isLoading: boolean;\n attachedFiles?: ChatFile[];\n onFileAdd?: (files: FileList | null) => void;\n onFileRemove?: (index: number) => void;\n onPaste?: (e: React.ClipboardEvent) => void;\n t: (key: string) => string;\n mode?: ChatMode;\n separatorColor?: string;\n}\n\nexport const ChatInput = ({\n inputValue,\n onInputChange,\n onSend,\n onStop,\n isLoading,\n attachedFiles = [],\n onFileAdd,\n onFileRemove,\n onPaste,\n t,\n mode,\n separatorColor,\n}: ChatInputProps) => {\n const fileInputRef = useRef<HTMLInputElement>(null);\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n onSend();\n }\n };\n\n const handleInput = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n onInputChange(e.target.value);\n const el = e.target;\n el.style.height = 'auto';\n el.style.height = `${Math.min(el.scrollHeight, 120)}px`;\n };\n\n const isFileManagementEnabled = Boolean(onFileAdd && onFileRemove && onPaste);\n const hasContent = inputValue.trim() || (isFileManagementEnabled && attachedFiles.length > 0);\n const hasFilesUploading = isFileManagementEnabled && attachedFiles.some((f) => f.uploadStatus === 'pending');\n const canSend = hasContent && !hasFilesUploading;\n\n return (\n <div\n className={`px-4 py-3 border-t border-gray-200 dark:border-white/10 ${mode === 'floating' ? 'rounded-b-xl' : ''}`}\n style={separatorColor ? { borderTopColor: separatorColor, borderTopWidth: 1 } : undefined}\n >\n {isFileManagementEnabled && attachedFiles.length > 0 && (\n <div className=\"flex gap-1.5 flex-wrap mb-2\">\n {attachedFiles.map((f, i) => (\n <span\n key={i}\n className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full border text-[0.7rem] ${\n f.uploadStatus === 'error'\n ? 'border-red-300 dark:border-red-500/30 text-red-500 dark:text-red-400'\n : f.uploadStatus === 'pending'\n ? 'border-gray-200 dark:border-white/10 text-gray-400 dark:text-white/40'\n : 'border-gray-200 dark:border-white/10 text-gray-600 dark:text-white/60'\n }`}\n >\n {f.uploadStatus === 'pending' ? (\n <span className=\"w-3.5 h-3.5 border border-current/30 border-t-current rounded-full animate-spin\" />\n ) : (\n <FileIcon size={14} />\n )}\n {f.name}\n {f.uploadStatus === 'error' && <span className=\"text-red-400 text-[0.6rem]\">✕</span>}\n <button\n type=\"button\"\n onClick={() => onFileRemove?.(i)}\n className=\"ml-0.5 text-gray-400 dark:text-white/30 hover:text-gray-600 dark:hover:text-white/60\"\n >\n ×\n </button>\n </span>\n ))}\n </div>\n )}\n\n <div className=\"flex items-center border border-gray-200 dark:border-white/10 rounded-xl px-2 py-1 transition-colors focus-within:border-[var(--chat-accent)]\">\n {isFileManagementEnabled && (\n <>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n hidden\n onChange={(e) => {\n onFileAdd?.(e.target.files);\n e.target.value = '';\n }}\n />\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n className=\"w-8 h-8 flex items-center justify-center shrink-0 rounded-lg text-gray-400 dark:text-white/30 hover:bg-gray-100 dark:hover:bg-white/10 mr-0.5 transition-colors\"\n >\n <AttachFileIcon size={18} />\n </button>\n </>\n )}\n <textarea\n ref={textareaRef}\n placeholder={t('Ask a question...')}\n value={inputValue}\n onChange={handleInput}\n onKeyDown={handleKeyDown}\n onPaste={onPaste}\n rows={1}\n className=\"flex-1 bg-transparent border-none outline-hidden resize-none text-[0.8125rem] py-1.5 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-white/30 filigran-chat-scrollable\"\n style={{ maxHeight: 120 }}\n />\n <Tooltip title={isLoading ? t('Stop generating') : hasFilesUploading ? t('Files uploading...') : ''}>\n <button\n type=\"button\"\n onClick={isLoading ? onStop : onSend}\n disabled={!isLoading && !canSend}\n className={`p-1.5 rounded-lg w-8 h-8 flex items-center justify-center transition-all duration-150 ${\n isLoading\n ? 'text-red-500 bg-red-500/10 hover:bg-red-500/20'\n : canSend\n ? 'text-[var(--chat-accent)] bg-[var(--chat-accent)]/10 hover:bg-[var(--chat-accent)]/20'\n : 'text-gray-300 dark:text-white/20 cursor-not-allowed'\n }`}\n >\n {isLoading ? <StopCircleIcon size={18} /> : <SendIcon size={18} />}\n </button>\n </Tooltip>\n </div>\n\n <p className=\"text-center text-[0.65rem] text-gray-400 dark:text-white/30 mt-1.5 opacity-70\">{t('Uses AI. Verify results.')}</p>\n </div>\n );\n};\n","import { useEffect, useRef } from 'react';\nimport type { AgentStatusState, IconProps } from '../types';\nimport {\n BrainIcon,\n DatabaseIcon,\n ExternalLinkIcon,\n GlobeIcon,\n MailIcon,\n SearchIcon,\n SparklesIcon,\n TerminalIcon,\n UserPlusIcon,\n WrenchIcon,\n} from './icons';\n\ninterface ChatThinkingProps {\n agentStatus: AgentStatusState | null;\n logoIcon?: React.ReactNode;\n t: (key: string) => string;\n}\n\ntype IconComponent = (props: IconProps) => React.JSX.Element;\n\ninterface StatusVisual {\n label: string;\n StatusIcon: IconComponent;\n showDots: boolean;\n}\n\nfunction resolveStatusVisual(agentStatus: AgentStatusState | null, t: (key: string) => string): StatusVisual {\n if (!agentStatus) {\n return { label: t('Thinking...'), StatusIcon: BrainIcon, showDots: false };\n }\n switch (agentStatus.status) {\n case 'tool_start': {\n const rawNames = agentStatus.tools ?? [];\n const lower = rawNames.map((n) => n.toLowerCase());\n\n // Delegation tools have dedicated statuses\n if (lower.some((n) => n === 'spawn_background_task')) {\n const count = rawNames.filter((n) => n === 'spawn_background_task').length;\n const label = count > 1 ? `${t('Delegating')} ${count} ${t('tasks')}…` : `${t('Delegating task')}…`;\n return { label, StatusIcon: UserPlusIcon, showDots: false };\n }\n if (lower.some((n) => n === 'check_task_status')) {\n const count = rawNames.filter((n) => n === 'check_task_status').length;\n const target = count > 1 ? `${count} ${t('background tasks')}` : t('background task');\n return { label: `${t('Waiting for')} ${target}…`, StatusIcon: SparklesIcon, showDots: false };\n }\n if (lower.some((n) => n === 'get_task_result')) {\n const count = rawNames.filter((n) => n === 'get_task_result').length;\n const from = count > 1 ? `${count} ${t('tasks')}` : t('task');\n return { label: `${t('Collecting results from')} ${from}…`, StatusIcon: SparklesIcon, showDots: false };\n }\n\n let StatusIcon: IconComponent = WrenchIcon;\n if (lower.some((n) => n.includes('search') || n.includes('list'))) {\n StatusIcon = SearchIcon;\n } else if (lower.some((n) => n.includes('read') || n.includes('get') || n.includes('query'))) {\n StatusIcon = DatabaseIcon;\n } else if (lower.some((n) => n.includes('send') || n.includes('create') || n.includes('draft') || n.includes('reply') || n.includes('flag'))) {\n StatusIcon = MailIcon;\n } else if (lower.some((n) => n.includes('code') || n.includes('execute'))) {\n StatusIcon = TerminalIcon;\n } else if (lower.some((n) => n.includes('web') || n.includes('browse'))) {\n StatusIcon = GlobeIcon;\n }\n let label: string;\n if (rawNames.length > 0) {\n const display = rawNames.map((n) => n.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase()));\n const unique = Array.from(new Set(display));\n label = unique.length === 1 ? `${unique[0]}…` : `${unique[0]} (+${unique.length - 1} more)…`;\n } else {\n label = t('Using tools…');\n }\n return { label, StatusIcon, showDots: false };\n }\n case 'analyzing':\n return { label: t('Analyzing results…'), StatusIcon: SparklesIcon, showDots: false };\n case 'composing':\n return { label: t('Composing answer…'), StatusIcon: BrainIcon, showDots: true };\n case 'consulting': {\n const consultName = agentStatus.tools?.[0] ?? 'agent';\n return { label: `${t('Consulting')} ${consultName}…`, StatusIcon: UserPlusIcon, showDots: false };\n }\n case 'delegating': {\n const count = agentStatus.tools?.filter((n) => n === 'spawn_background_task').length ?? 0;\n return {\n label: count > 1 ? `${t('Delegating')} ${count} ${t('tasks')}…` : `${t('Delegating task')}…`,\n StatusIcon: UserPlusIcon,\n showDots: false,\n };\n }\n case 'polling': {\n const checkCount = agentStatus.tools?.filter((n) => n === 'check_task_status').length ?? 0;\n const target = checkCount > 1 ? `${checkCount} ${t('background tasks')}` : t('background task');\n return { label: `${t('Waiting for')} ${target}…`, StatusIcon: SparklesIcon, showDots: false };\n }\n case 'collecting': {\n const fetchCount = agentStatus.tools?.filter((n) => n === 'get_task_result').length ?? 0;\n const from = fetchCount > 1 ? `${fetchCount} ${t('tasks')}` : t('task');\n return { label: `${t('Collecting results from')} ${from}…`, StatusIcon: SparklesIcon, showDots: false };\n }\n case 'transferring': {\n const targetName = agentStatus.tools?.[0] ?? 'agent';\n return { label: `${t('Transferring to')} ${targetName}…`, StatusIcon: ExternalLinkIcon, showDots: false };\n }\n case 'thinking':\n default:\n return { label: t('Thinking...'), StatusIcon: BrainIcon, showDots: false };\n }\n}\n\nfunction stripMarkdown(text: string): string {\n return text\n .replace(/```[\\s\\S]*?```/g, ' ')\n .replace(/`([^`]+)`/g, '$1')\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n .replace(/__(.+?)__/g, '$1')\n .replace(/\\*(.+?)\\*/g, '$1')\n .replace(/_(.+?)_/g, '$1')\n .replace(/#{1,6}\\s+/g, '')\n .replace(/[*\\->]+/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nexport function ThinkingTextBubble({ content }: { content: string }) {\n const ref = useRef<HTMLDivElement>(null);\n const cleaned = stripMarkdown(content);\n\n useEffect(() => {\n if (ref.current) ref.current.scrollTop = ref.current.scrollHeight;\n }, [cleaned]);\n\n if (cleaned.length < 3) return null;\n\n return (\n <div\n ref={ref}\n className=\"ml-11 max-w-[70%] max-h-20 overflow-hidden relative rounded-md border-l-2 bg-[var(--chat-accent)]/[0.03] pl-3 pr-3 py-2\"\n style={{ animation: 'reasoningGlow 3s ease-in-out infinite, chat-fade-in 0.5s ease-out' }}\n >\n <p className=\"text-[13px] leading-[1.35rem] text-gray-400 dark:text-white/40 break-words m-0\">{cleaned}</p>\n <div className=\"absolute inset-x-0 bottom-0 h-5 bg-gradient-to-t from-white/90 dark:from-[#1e1e2e]/90 to-transparent pointer-events-none\" />\n </div>\n );\n}\n\nexport const ChatThinking = ({ agentStatus, logoIcon, t }: ChatThinkingProps) => {\n const { label, StatusIcon, showDots } = resolveStatusVisual(agentStatus, t);\n const thinkingContent = agentStatus?.thinkingContent;\n\n return (\n <>\n <div className=\"flex gap-3 items-center justify-start\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-[var(--chat-accent)]/15 to-[var(--chat-accent)]/5\">\n <span className=\"text-[var(--chat-accent)] [&>svg]:w-4 [&>svg]:h-4\">{logoIcon}</span>\n </div>\n <div className=\"rounded-lg bg-gray-50 dark:bg-white/[0.03] px-4 py-3 relative overflow-hidden\">\n <div className=\"absolute inset-0 bg-gradient-to-r from-[var(--chat-accent)]/[0.03] via-transparent to-[var(--chat-accent)]/[0.03] animate-pulse pointer-events-none\" />\n <div className=\"relative flex items-center gap-2.5\">\n {showDots ? (\n <div className=\"flex gap-[3px] items-center h-3.5 w-3.5 justify-center\">\n {[0, 0.15, 0.3].map((delay, i) => (\n <span\n key={i}\n className=\"h-[5px] w-[5px] rounded-full bg-[var(--chat-accent)]/50\"\n style={{ animation: `chat-dot 1s ease-in-out infinite ${delay}s` }}\n />\n ))}\n </div>\n ) : (\n <StatusIcon size={14} className=\"text-[var(--chat-accent)] animate-pulse transition-all duration-300\" />\n )}\n <span className=\"text-sm text-gray-500 dark:text-white/50 transition-all duration-300\">{label}</span>\n </div>\n </div>\n </div>\n {thinkingContent && <ThinkingTextBubble content={thinkingContent} />}\n </>\n );\n};\n","import { useState } from 'react';\nimport Markdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport { CheckIcon, CopyIcon } from './icons';\n\ninterface MarkdownMessageProps {\n content: string;\n onRelativeLinkClick?: (href: string) => void;\n}\n\nconst isRelativeHref = (href?: string) => {\n if (!href) return false;\n if (href.startsWith('//')) return false;\n const hasAbsoluteScheme = /^[a-zA-Z][a-zA-Z\\d+.-]*:/.test(href);\n return !hasAbsoluteScheme;\n};\n\nexport const MarkdownMessage = ({ content, onRelativeLinkClick }: MarkdownMessageProps) => {\n const [copiedBlock, setCopiedBlock] = useState<string | null>(null);\n\n const handleCopyCode = (code: string) => {\n navigator.clipboard.writeText(code);\n setCopiedBlock(code);\n setTimeout(() => setCopiedBlock(null), 2000);\n };\n\n return (\n <Markdown\n remarkPlugins={[remarkGfm]}\n components={{\n p: ({ children }) => <p className=\"mb-3 last:mb-0 leading-7 break-words text-[0.8125rem] text-gray-900 dark:text-white/90\">{children}</p>,\n code: ({ className, children }) => {\n const match = /language-(\\w+)/.exec(className || '');\n const codeStr = String(children).replace(/\\n$/, '');\n if (match) {\n return (\n <div className=\"my-3 rounded-lg border border-gray-200 dark:border-white/10 overflow-hidden bg-gray-50 dark:bg-white/[0.03]\">\n <div className=\"flex items-center justify-between px-3 py-1.5 border-b border-gray-200 dark:border-white/10 bg-gray-100 dark:bg-white/[0.03]\">\n <span className=\"text-[0.7rem] text-gray-500 dark:text-white/40 font-mono\">{match[1]}</span>\n <button\n type=\"button\"\n onClick={() => handleCopyCode(codeStr)}\n className=\"p-0.5 rounded-sm hover:bg-gray-200 dark:hover:bg-white/10 transition-colors\"\n >\n {copiedBlock === codeStr ? (\n <CheckIcon size={14} className=\"text-green-500\" />\n ) : (\n <CopyIcon size={14} className=\"text-gray-400 dark:text-white/40\" />\n )}\n </button>\n </div>\n <pre className=\"m-0 px-3 py-2 overflow-x-auto\">\n <code className=\"font-mono text-xs leading-[1.7] text-gray-800 dark:text-white/90 whitespace-pre\">{codeStr}</code>\n </pre>\n </div>\n );\n }\n return (\n <code className=\"bg-gray-100 dark:bg-white/[0.08] px-1.5 py-0.5 rounded-sm font-mono text-xs text-[var(--chat-accent)]\">{children}</code>\n );\n },\n ul: ({ children }) => (\n <ul className=\"pl-5 mb-3 text-[0.8125rem] text-gray-900 dark:text-white/90 [&_li]:mb-1 marker:text-[var(--chat-accent)]/50\">{children}</ul>\n ),\n ol: ({ children }) => (\n <ol className=\"pl-5 mb-3 text-[0.8125rem] text-gray-900 dark:text-white/90 [&_li]:mb-1 marker:text-[var(--chat-accent)]/50\">{children}</ol>\n ),\n blockquote: ({ children }) => (\n <blockquote className=\"my-3 border-l-2 border-[var(--chat-accent)]/30 bg-[var(--chat-accent)]/[0.03] pl-4 pr-3 py-2 rounded-r-md italic text-gray-500 dark:text-white/60\">\n {children}\n </blockquote>\n ),\n a: ({ href, children }) => {\n const openInNewTab = !isRelativeHref(href);\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n if (!href || !isRelativeHref(href) || !onRelativeLinkClick) return;\n event.preventDefault();\n onRelativeLinkClick(href);\n };\n\n return (\n <a\n href={href}\n onClick={handleClick}\n target={openInNewTab ? '_blank' : undefined}\n rel={openInNewTab ? 'noopener noreferrer' : undefined}\n className=\"text-[var(--chat-accent)] underline underline-offset-2 hover:brightness-125\"\n >\n {children}\n </a>\n );\n },\n h1: ({ children }) => <h1 className=\"mt-4 first:mt-0 mb-2 font-bold text-base text-gray-900 dark:text-white\">{children}</h1>,\n h2: ({ children }) => <h2 className=\"mt-3 first:mt-0 mb-2 font-bold text-[0.9rem] text-gray-900 dark:text-white\">{children}</h2>,\n h3: ({ children }) => <h3 className=\"mt-3 first:mt-0 mb-1.5 font-semibold text-[0.85rem] text-gray-900 dark:text-white\">{children}</h3>,\n table: ({ children }) => (\n <div className=\"my-3 overflow-x-auto rounded-lg border border-gray-200 dark:border-white/10\">\n <table className=\"w-full border-collapse text-xs\">{children}</table>\n </div>\n ),\n th: ({ children }) => (\n <th className=\"px-3 py-2 text-left font-semibold bg-gray-50 dark:bg-white/[0.04] border-b border-gray-200 dark:border-white/10 text-gray-900 dark:text-white\">\n {children}\n </th>\n ),\n td: ({ children }) => (\n <td className=\"px-3 py-2 border-b border-gray-200 dark:border-white/10 text-gray-700 dark:text-white/80\">{children}</td>\n ),\n }}\n >\n {content}\n </Markdown>\n );\n};\n","import { useEffect, useRef, useState } from 'react';\nimport type { AgentStatusState, ChatAttachment, ChatMessage } from '../types';\nimport { splitFileMarkers } from '../utils';\nimport { DownloadIcon, FileIcon, InfoIcon } from './icons';\nimport { ChatThinking, ThinkingTextBubble } from './ChatThinking';\nimport { MarkdownMessage } from './MarkdownMessage';\n\ninterface ChatMessagesProps {\n messages: ChatMessage[];\n isLoading: boolean;\n agentStatus: AgentStatusState | null;\n agentName: string;\n logoIcon: React.ReactNode;\n onRelativeLinkClick?: (href: string) => void;\n /** Download an agent-generated file via the host app's backend proxy. */\n onDownloadFile?: (attachment: ChatAttachment) => void;\n t: (key: string) => string;\n}\n\nfunction formatFileSize(bytes?: number): string {\n if (!bytes || bytes <= 0) return '';\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport const ChatMessages = ({ messages, isLoading, agentStatus, agentName, logoIcon, onRelativeLinkClick, onDownloadFile, t }: ChatMessagesProps) => {\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const [toolDetailMsgId, setToolDetailMsgId] = useState<string | null>(null);\n\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n const renderAttachmentCard = (att: ChatAttachment, key: string) => {\n const isWorking = att.fileTag === 'working_file';\n const sizeLabel = formatFileSize(att.size);\n return (\n <button\n key={key}\n type=\"button\"\n onClick={() => onDownloadFile?.(att)}\n title={t('Download')}\n className={`group flex items-center gap-2 text-left rounded-lg border px-2.5 py-1.5 transition-colors cursor-pointer max-w-[90%] ${\n isWorking\n ? 'border-gray-200 dark:border-white/10 bg-transparent'\n : 'border-gray-200 dark:border-white/10 bg-gray-50 dark:bg-white/[0.04] hover:border-[var(--chat-accent)] hover:bg-[var(--chat-accent)]/5'\n }`}\n >\n <span className={`shrink-0 ${isWorking ? 'text-gray-400 dark:text-white/40' : 'text-[var(--chat-accent)]'}`}>\n <FileIcon size={16} />\n </span>\n <span className=\"flex flex-col min-w-0 flex-1\">\n <span className=\"truncate text-[0.75rem] text-gray-900 dark:text-white\">{att.filename}</span>\n {(att.type || sizeLabel) && (\n <span className=\"text-[0.65rem] text-gray-400 dark:text-white/40 uppercase\">\n {[att.type, sizeLabel].filter(Boolean).join(' · ')}\n </span>\n )}\n </span>\n <span className=\"shrink-0 text-gray-400 dark:text-white/30 group-hover:text-[var(--chat-accent)]\">\n <DownloadIcon size={15} />\n </span>\n </button>\n );\n };\n\n // Render assistant content as an ordered interleave of prose segments and\n // download cards, so a reply with markers like\n // `text [[FILE:a]] more text [[FILE:b]]` keeps the cards at their source\n // position. Cards only render when a download handler is wired\n // (`onDownloadFile`); attachments whose marker isn't found in the prose are\n // appended as a fallback. During streaming the attachments aren't hydrated\n // yet, so only prose (markers stripped) renders.\n const buildAssistantBlocks = (msg: ChatMessage, loading: boolean): React.ReactNode[] => {\n const parts = splitFileMarkers(msg.content);\n const attByFileId = new Map((msg.attachments ?? []).map((a) => [a.fileId, a] as const));\n const used = new Set<string>();\n const blocks: React.ReactNode[] = [];\n\n parts.forEach((part, i) => {\n if (part.type === 'text') {\n if (part.value.trim()) {\n blocks.push(\n <div key={`t-${i}`} className=\"max-w-[90%] pl-1 py-1 text-[0.8125rem] leading-7\">\n <MarkdownMessage content={part.value} onRelativeLinkClick={onRelativeLinkClick} />\n </div>,\n );\n }\n } else if (onDownloadFile) {\n const att = attByFileId.get(part.fileId);\n if (att) {\n used.add(part.fileId);\n blocks.push(renderAttachmentCard(att, `f-${part.fileId}-${i}`));\n }\n }\n });\n\n if (onDownloadFile) {\n (msg.attachments ?? []).forEach((att) => {\n if (!used.has(att.fileId)) {\n blocks.push(renderAttachmentCard(att, `orphan-${att.fileId}`));\n }\n });\n }\n\n // An assistant reply that is *only* a file marker leaves no prose; show a\n // subtle ellipsis (not an empty padded bubble) when nothing else rendered\n // and we're not still streaming.\n if (blocks.length === 0 && !loading) {\n blocks.push(\n <span key=\"empty\" className=\"pl-1 text-[0.8125rem] text-gray-400 dark:text-white/40 italic\">\n ...\n </span>,\n );\n }\n\n return blocks;\n };\n\n return (\n <div className=\"flex-1 overflow-y-auto px-4 py-3 flex flex-col gap-4 filigran-chat-scrollable\">\n {messages.map((msg, index) => {\n const isAssistant = msg.role === 'assistant';\n const isEmpty = !msg.content;\n // The streaming response is always the last message, so the live\n // cursor / thinking bubble / ChatThinking state must be gated on it —\n // otherwise every previously completed assistant message would also\n // show them while a *later* response is streaming.\n const isStreamingMessage = isLoading && index === messages.length - 1;\n const isThinking = isAssistant && isEmpty && isStreamingMessage;\n\n if (isThinking) {\n return (\n <div key={msg.id}>\n <ChatThinking agentStatus={agentStatus} logoIcon={logoIcon} t={t} />\n </div>\n );\n }\n\n return (\n <div key={msg.id} className={`flex flex-col ${isAssistant ? 'items-start' : 'items-end'}`}>\n {isAssistant && (\n <div className=\"flex items-center gap-1.5 mb-1\">\n <div className=\"w-6 h-6 rounded-lg flex items-center justify-center bg-gradient-to-br from-[var(--chat-accent)]/20 to-[var(--chat-accent)]/5\">\n <span className=\"text-[var(--chat-accent)] [&>svg]:w-3 [&>svg]:h-3\">{logoIcon}</span>\n </div>\n <span className=\"font-semibold text-xs text-gray-900 dark:text-white\">{agentName}</span>\n </div>\n )}\n\n {isAssistant && !isEmpty && isStreamingMessage && agentStatus?.thinkingContent && <ThinkingTextBubble content={agentStatus.thinkingContent} />}\n\n {msg.files && msg.files.length > 0 && (\n <div className=\"flex gap-1.5 flex-wrap mb-1.5\">\n {msg.files.map((f, i) => (\n <span\n key={i}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 rounded-full border border-gray-200 dark:border-white/10 text-[0.7rem] text-gray-600 dark:text-white/60\"\n >\n <FileIcon size={14} />\n {f.name}\n </span>\n ))}\n </div>\n )}\n\n {isAssistant ? (\n <div className=\"flex flex-col gap-1.5 w-full items-start\">\n {buildAssistantBlocks(msg, isStreamingMessage)}\n {!isEmpty && isStreamingMessage && (\n <span className=\"inline-block w-1.5 h-4 bg-[var(--chat-accent)]/70 rounded-xs ml-1 animate-pulse\" />\n )}\n </div>\n ) : (\n <div className=\"max-w-[90%] px-3.5 py-2 rounded-[14px_14px_4px_14px] bg-[var(--chat-accent-dark)] text-white text-[0.8125rem] leading-6\">\n {msg.content}\n </div>\n )}\n\n {isAssistant && !isEmpty && !isLoading && msg.toolNames && msg.toolNames.length > 0 && (\n <>\n <button\n type=\"button\"\n onClick={() => setToolDetailMsgId(toolDetailMsgId === msg.id ? null : msg.id)}\n className=\"mt-0.5 p-1 rounded-lg opacity-50 hover:opacity-100 hover:text-[var(--chat-accent)] transition-opacity\"\n title={t('Reasoning details')}\n >\n <InfoIcon size={14} />\n </button>\n {toolDetailMsgId === msg.id && (\n <div className=\"mt-1.5 p-3 rounded-lg bg-gray-50 dark:bg-white/[0.04] border border-gray-200 dark:border-white/10\">\n <p className=\"text-[0.7rem] text-gray-500 dark:text-white/40 mb-1.5\">\n {msg.iterations && msg.iterations > 1 ? `${msg.iterations} iterations · ` : ''}\n {msg.toolCallCount ?? msg.toolNames.length}{' '}\n {(msg.toolCallCount ?? msg.toolNames.length) === 1 ? t('tool call') : t('tool calls')}\n </p>\n <div className=\"flex flex-wrap gap-1\">\n {Array.from(new Set(msg.toolNames)).map((tn) => (\n <span\n key={tn}\n className=\"inline-flex items-center px-2 py-0.5 rounded-full border border-gray-200 dark:border-white/10 text-[0.68rem] font-mono text-gray-500 dark:text-white/40\"\n >\n {tn.replace(/_/g, ' ')}\n </span>\n ))}\n </div>\n </div>\n )}\n </>\n )}\n </div>\n );\n })}\n <div ref={messagesEndRef} />\n </div>\n );\n};\n","interface ChatWelcomeProps {\n firstName: string;\n logoIcon: React.ReactNode;\n promptSuggestions: string[];\n onPromptClick: (prompt: string) => void;\n t: (key: string) => string;\n}\n\nexport const ChatWelcome = ({ firstName, logoIcon, promptSuggestions, onPromptClick, t }: ChatWelcomeProps) => (\n <div className=\"flex-1 flex flex-col items-center justify-center px-6 pb-8\">\n <span className=\"text-[var(--chat-accent)] mb-4 [&>svg]:w-12 [&>svg]:h-12 drop-shadow-[0_0_12px_var(--chat-accent-40)]\">{logoIcon}</span>\n <h2 className=\"text-xl font-medium mb-6 text-center text-gray-900 dark:text-white\" style={{ fontFamily: '\"Geologica\", sans-serif' }}>\n {t('How can I help you, ')}\n {firstName}?\n </h2>\n <div className=\"w-full max-w-[320px]\">\n <span className=\"block text-center mb-2 text-[0.65rem] tracking-[1.5px] uppercase text-[var(--chat-accent)] font-semibold\">\n {t('Suggestions')}\n </span>\n {promptSuggestions.map((prompt) => (\n <button\n key={prompt}\n type=\"button\"\n onClick={() => onPromptClick(prompt)}\n className=\"w-full text-left text-[0.8125rem] text-gray-800 dark:text-white py-1.5 px-3 mb-1 rounded-lg border border-gray-200 dark:border-white/10 bg-transparent transition-colors hover:bg-[var(--chat-accent-10)] hover:border-[var(--chat-accent-50)]\"\n >\n {t(prompt)}\n </button>\n ))}\n </div>\n </div>\n);\n","import { type FunctionComponent, useCallback, useEffect, useState } from 'react';\nimport type { ChatAttachment, ChatMessage, ChatPanelProps } from '../types';\nimport { hexAlpha, identity } from '../utils';\nimport { parseAttachments } from '../hooks/protocols/parseRestEvent';\nimport { useChat } from '../hooks/useChat';\nimport { useAgents } from '../hooks/useAgents';\nimport { useSidebarResize } from '../hooks/useSidebarResize';\nimport { DefaultLogoIcon } from './icons';\nimport { ChatHeader } from './ChatHeader';\nimport { ChatInput } from './ChatInput';\nimport { ChatMessages } from './ChatMessages';\nimport { ChatWelcome } from './ChatWelcome';\n\nconst FLOATING_WIDTH = 380;\nconst FLOATING_HEIGHT = 560;\nconst SIDEBAR_GAP = 6;\n\nconst DEFAULT_SUGGESTIONS = [\n 'Help me create a new simulation scenario',\n 'What are the latest attack patterns?',\n 'How do I configure detection rules?',\n 'Summarize my recent findings',\n];\n\nexport const ChatPanel: FunctionComponent<ChatPanelProps> = ({\n mode,\n onClose,\n onModeChange,\n topOffset = 0,\n apiBaseUrl,\n apiEndpoints,\n agentDashboardUrl,\n user,\n t = identity,\n accentColor = '#7b5cff',\n logoIcon,\n promptSuggestions = DEFAULT_SUGGESTIONS,\n draftBorderColor,\n resizable = false,\n onWidthChange,\n onResizeStart,\n onResizeEnd,\n disableFileManagement = false,\n onRelativeLinkClick,\n onDownloadError,\n maxFileCount,\n maxTotalSize,\n requestHeaders,\n pushContentSelector,\n backendType = 'rest',\n}) => {\n const [modeMenuOpen, setModeMenuOpen] = useState(false);\n\n const { agents, selectedAgent, agentMenuOpen, setAgentMenuOpen, handleSwitchAgent } = useAgents({\n apiBaseUrl,\n apiEndpoints,\n backendType,\n requestHeaders,\n });\n\n const {\n messages,\n inputValue,\n setInputValue,\n isLoading,\n agentStatus,\n attachedFiles,\n conversationId,\n transferredAgent,\n historyLoadedRef,\n handleFileAdd,\n handlePaste,\n handleSendMessage,\n handleNewChat,\n handleStopGenerating,\n setAttachedFiles,\n setMessages,\n setConversationId,\n } = useChat({\n apiBaseUrl,\n apiEndpoints,\n backendType,\n agentSlug: selectedAgent?.slug,\n requestHeaders,\n t,\n maxFileCount,\n maxTotalSize,\n });\n\n const { sidebarWidth, handleResizeStart, defaultWidth, isResizing } = useSidebarResize({\n mode,\n resizable,\n onWidthChange,\n onResizeStart,\n onResizeEnd,\n });\n\n // Push content when sidebar mode is active using CSS variable\n useEffect(() => {\n const width = mode === 'sidebar' ? (resizable ? sidebarWidth : defaultWidth) : 0;\n const pushWidth = width > 0 ? width + SIDEBAR_GAP : 0;\n\n // Set CSS variable on :root for any component to use\n document.documentElement.style.setProperty('--chatbot-sidebar-width', `${pushWidth}px`);\n document.documentElement.style.setProperty('--chatbot-transition', isResizing ? 'none' : 'all 225ms cubic-bezier(0.4, 0, 0.2, 1)');\n\n // Also apply to pushContentSelector if provided (for simple cases)\n if (pushContentSelector) {\n const contentElement = document.querySelector<HTMLElement>(pushContentSelector);\n if (contentElement) {\n const originalPaddingRight = contentElement.style.paddingRight;\n const originalTransition = contentElement.style.transition;\n\n contentElement.style.paddingRight = pushWidth > 0 ? `${pushWidth}px` : '';\n contentElement.style.transition = isResizing ? 'none' : 'padding-right 225ms cubic-bezier(0.4, 0, 0.2, 1)';\n\n return () => {\n contentElement.style.paddingRight = originalPaddingRight;\n contentElement.style.transition = originalTransition;\n document.documentElement.style.setProperty('--chatbot-sidebar-width', '0px');\n };\n }\n }\n\n return () => {\n document.documentElement.style.setProperty('--chatbot-sidebar-width', '0px');\n };\n }, [pushContentSelector, mode, sidebarWidth, defaultWidth, resizable, isResizing]);\n\n const resolvedLogo = logoIcon ?? <DefaultLogoIcon size={24} />;\n const firstName = user.firstName;\n const agentName = transferredAgent?.name || selectedAgent?.name || 'Assistant';\n\n // Download an agent-generated file. The URL is resolved against the host\n // app's own backend proxy (apiBaseUrl), NOT the upstream chat service:\n // the proxy mints any upstream token server-side, so the user stays\n // authenticated to the host platform (e.g. OpenCTI / OpenAEV) only and\n // never logs in to the upstream service. Same-origin cookies +\n // requestHeaders (CSRF / draft context) carry the host-app auth.\n // Downloads need a path to build the URL from. Enabled for the REST\n // backend unless explicitly disabled (`download === null`). In\n // single-endpoint mode there is no per-path routing, so a download path\n // must be provided explicitly (e.g. an OpenCTI-style proxy route);\n // otherwise the default REST `/chat/files` path is used.\n const downloadPathProvided =\n apiEndpoints?.download !== null && apiEndpoints?.download !== undefined;\n const canDownload =\n backendType === 'rest' &&\n apiEndpoints?.download !== null &&\n (!apiEndpoints?.singleEndpoint || downloadPathProvided);\n\n const handleDownloadFile = useCallback(\n async (att: ChatAttachment) => {\n const base = apiEndpoints?.download ?? '/chat/files';\n const url = `${apiBaseUrl}${base}/${encodeURIComponent(att.fileId)}/download`;\n try {\n const res = await fetch(url, {\n method: 'GET',\n credentials: 'include',\n headers: { ...(requestHeaders ?? {}) },\n });\n if (!res.ok) throw new Error(`Download failed: ${res.status}`);\n const blob = await res.blob();\n const objectUrl = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = objectUrl;\n link.download = att.filename || 'download';\n document.body.appendChild(link);\n link.click();\n link.remove();\n URL.revokeObjectURL(objectUrl);\n } catch (err) {\n // Surface the failure (403/404/5xx/network) to the host so it can\n // notify the user — the chatbot has no toast surface of its own.\n // If the host doesn't provide a handler the error is intentionally\n // not thrown further (a rejected click handler has nowhere to go).\n onDownloadError?.(err, att);\n }\n },\n [apiBaseUrl, apiEndpoints, requestHeaders, onDownloadError],\n );\n\n const cssVars = {\n '--chat-accent': accentColor,\n '--chat-accent-10': hexAlpha(accentColor, 0.1),\n '--chat-accent-40': hexAlpha(accentColor, 0.25),\n '--chat-accent-50': hexAlpha(accentColor, 0.5),\n '--chat-accent-dark': accentColor,\n } as React.CSSProperties;\n\n // Load conversation history when agent is selected\n useEffect(() => {\n // Skip session history if disabled, using single endpoint mode, or non-REST backend\n if (apiEndpoints?.sessions === null || apiEndpoints?.singleEndpoint || backendType === 'legacy' || backendType === 'ag-ui') return;\n if (!conversationId || historyLoadedRef.current || !selectedAgent) return;\n historyLoadedRef.current = true;\n const sessionsUrl = `${apiBaseUrl}${apiEndpoints?.sessions ?? '/chat/sessions'}`;\n\n const clearStaleConversation = () => {\n setConversationId(null);\n localStorage.removeItem('filigranChatConversationId');\n };\n\n fetch(sessionsUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...(requestHeaders ?? {}) },\n body: JSON.stringify({\n conversation_id: conversationId,\n agent_slug: selectedAgent.slug,\n }),\n })\n .then((res) => {\n if (!res.ok) {\n clearStaleConversation();\n return null;\n }\n return res.json();\n })\n .then((data) => {\n if (!data?.messages?.length) return;\n const restored: ChatMessage[] = data.messages.map(\n (m: { role: string; content: string; attachments?: unknown }, i: number) => ({\n id: `restored-${i}`,\n role: m.role as 'user' | 'assistant',\n content: m.content,\n timestamp: new Date(),\n // Re-surface agent-generated download chips on conversation\n // restore (the [[FILE:…]] markers in content are stripped at\n // render time by ChatMessages).\n attachments: m.role === 'assistant' ? parseAttachments(m.attachments) : undefined,\n }),\n );\n setMessages(restored);\n })\n .catch(() => {\n clearStaleConversation();\n });\n }, [conversationId, selectedAgent, apiBaseUrl, apiEndpoints, historyLoadedRef, requestHeaders, setMessages, setConversationId]);\n\n const onSwitchAgent = (agent: typeof selectedAgent) => {\n if (!agent) return;\n handleSwitchAgent(agent, () => {\n handleNewChat();\n });\n };\n\n const containerClasses = (() => {\n const base = 'filigran-chatbot';\n switch (mode) {\n case 'sidebar':\n return `${base} fixed right-0 bottom-0 flex flex-col bg-white dark:bg-[#1e1e2e] border-l border-gray-200 dark:border-white/10 z-[1200]`;\n case 'floating':\n return `${base} fixed bottom-5 right-5 flex flex-col bg-white dark:bg-[#1e1e2e] rounded-xl shadow-[0_8px_32px_rgba(0,0,0,0.15)] dark:shadow-[0_8px_32px_rgba(0,0,0,0.4)] z-[1300] border border-gray-200 dark:border-white/10`;\n case 'fullscreen':\n return `${base} fixed right-0 bottom-0 left-0 flex flex-col bg-gray-50 dark:bg-[#161622] z-[1400]`;\n default:\n return base;\n }\n })();\n\n const containerStyle: React.CSSProperties = {\n ...cssVars,\n ...(mode === 'sidebar'\n ? { top: topOffset, width: resizable ? sidebarWidth : defaultWidth }\n : mode === 'floating'\n ? { width: FLOATING_WIDTH, height: FLOATING_HEIGHT }\n : { top: topOffset }),\n };\n\n return (\n <div className={containerClasses} style={containerStyle}>\n {mode === 'sidebar' && resizable && (\n <div onMouseDown={handleResizeStart} className=\"absolute top-0 -left-1 bottom-0 w-2 cursor-col-resize z-10 group\">\n <div className=\"absolute top-0 left-1/2 -translate-x-1/2 bottom-0 w-0.5 rounded-sm bg-[var(--chat-accent)] opacity-0 transition-opacity group-hover:opacity-100 group-active:opacity-100\" />\n </div>\n )}\n <ChatHeader\n mode={mode}\n agentName={agentName}\n agents={agents}\n selectedAgent={selectedAgent}\n transferredFrom={transferredAgent ? selectedAgent?.name : undefined}\n agentMenuOpen={agentMenuOpen}\n onAgentMenuToggle={() => setAgentMenuOpen((p) => !p)}\n onAgentMenuClose={() => setAgentMenuOpen(false)}\n onSwitchAgent={onSwitchAgent}\n modeMenuOpen={modeMenuOpen}\n onModeMenuToggle={() => setModeMenuOpen((p) => !p)}\n onModeMenuClose={() => setModeMenuOpen(false)}\n onModeChange={onModeChange}\n onNewChat={handleNewChat}\n onClose={onClose}\n logoIcon={resolvedLogo}\n agentDashboardUrl={agentDashboardUrl}\n t={t}\n />\n {messages.length === 0 ? (\n <ChatWelcome firstName={firstName} logoIcon={resolvedLogo} promptSuggestions={promptSuggestions} onPromptClick={setInputValue} t={t} />\n ) : (\n <ChatMessages\n messages={messages}\n isLoading={isLoading}\n agentStatus={agentStatus}\n agentName={agentName}\n logoIcon={resolvedLogo}\n onRelativeLinkClick={onRelativeLinkClick}\n onDownloadFile={canDownload ? handleDownloadFile : undefined}\n t={t}\n />\n )}\n <ChatInput\n inputValue={inputValue}\n onInputChange={setInputValue}\n onSend={handleSendMessage}\n onStop={handleStopGenerating}\n isLoading={isLoading}\n attachedFiles={disableFileManagement ? [] : attachedFiles}\n onFileAdd={disableFileManagement ? undefined : handleFileAdd}\n onFileRemove={disableFileManagement ? undefined : (i) => setAttachedFiles((prev) => prev.filter((_, j) => j !== i))}\n onPaste={disableFileManagement ? undefined : handlePaste}\n t={t}\n mode={mode}\n separatorColor={draftBorderColor}\n />\n </div>\n );\n};\n","import type { FunctionComponent } from 'react';\nimport type { ChatToggleButtonProps } from '../types';\nimport { hexAlpha } from '../utils';\nimport { DefaultLogoIcon } from './icons';\n\nexport const ChatToggleButton: FunctionComponent<ChatToggleButtonProps> = ({\n isOpen,\n onToggle,\n label = 'Ask Assistant',\n accentColor = '#7b5cff',\n icon,\n}) => {\n const resolvedIcon = icon ?? <DefaultLogoIcon size={16} />;\n\n return (\n <button\n type=\"button\"\n onClick={onToggle}\n className=\"filigran-chatbot inline-flex items-center gap-1.5 px-3 py-[3px] text-[0.8125rem] font-medium whitespace-nowrap rounded-md border transition-colors\"\n style={{\n borderColor: isOpen ? accentColor : hexAlpha(accentColor, 0.5),\n color: accentColor,\n backgroundColor: isOpen ? hexAlpha(accentColor, 0.1) : 'transparent',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.borderColor = accentColor;\n e.currentTarget.style.backgroundColor = hexAlpha(accentColor, 0.1);\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.borderColor = isOpen ? accentColor : hexAlpha(accentColor, 0.5);\n e.currentTarget.style.backgroundColor = isOpen ? hexAlpha(accentColor, 0.1) : 'transparent';\n }}\n >\n <span className=\"[&>svg]:w-4 [&>svg]:h-4\">{resolvedIcon}</span>\n {label}\n </button>\n );\n};\n"],"names":["hexAlpha","hex","alpha","Math","round","toString","padStart","identity","key","PARTIAL_FILE_MARKER_RE","parseAttachments","raw","Array","isArray","out","item","a","fileId","file_id","push","filename","type","undefined","size","contentType","content_type","fileTag","file_tag","length","parseRestEvent","evt","ctx","action","content","st","status","thinkingContent","hasUsedTools","tools","conversationId","conversation_id","toolNames","tool_names","toolCallCount","tool_call_count","iterations","transferAgentId","transfer_agent_id","transferAgentName","transfer_agent_name","attachments","parseLegacyEvent","eventType","event","data","nodeId","activeNodeId","replace","reasoning","usedTools","map","t","tool","chatId","parseAgUiEvent","message","stepName","delta","toolName","toolCallName","STORAGE_KEY","LEGACY_CHAT_ID_KEY","DEFAULT_MAX_TOTAL_SIZE","useChat","apiBaseUrl","apiEndpoints","backendType","agentSlug","requestHeaders","maxFileCount","maxTotalSize","isLegacy","messages","setMessages","useState","inputValue","setInputValue","isLoading","setIsLoading","agentStatus","setAgentStatus","setConversationId","window","localStorage","getItem","attachedFiles","setAttachedFiles","transferredAgent","setTransferredAgent","legacyChatId","setLegacyChatId","historyLoadedRef","useRef","abortControllerRef","hasUsedToolsRef","conversationIdRef","creatingSessionRef","uploadAbortRef","AbortController","effectiveMaxFileCount","Number","isFinite","floor","effectiveMaxTotalSize","getUploadUrl","singleEndpoint","upload","updateConversationId","id","current","setItem","removeItem","ensureConversation","async","slug","sessionsUrl","sessions","promise","res","fetch","method","headers","body","JSON","stringify","agent_slug","ok","json","convId","uploadSingleFile","file","signal","uploadUrl","formData","FormData","append","name","uploadHeaders","Object","fromEntries","entries","filter","k","toLowerCase","Error","ids","file_ids","handleFileAdd","fileList","candidates","from","tempId","crypto","randomUUID","accepted","prev","currentCount","currentSize","reduce","sum","f","slotsAvailable","sizeLeft","filtered","c","slice","newEntries","rawFile","uploadStatus","setTimeout","p","err","DOMException","handlePaste","e","files","clipboardData","preventDefault","handleSendMessage","trim","userMsg","role","timestamp","Date","assistantId","controller","fileIds","requestBody","opts","question","streaming","threadId","runId","context","state","forwardedProps","buildRequestBody","m","parseEvent","getParser","reader","getReader","decoder","TextDecoder","buffer","accumulated","doneReceived","done","value","read","decode","stream","lines","split","pop","rawLine","line","startsWith","jsonStr","parsed","parse","handleNewChat","abort","handleStopGenerating","STORAGE_AGENT_KEY","SIDEBAR_WIDTH","SIDEBAR_WIDTH_STORAGE_KEY","AttachFileIcon","className","_jsx","xmlns","width","height","viewBox","fill","stroke","strokeWidth","strokeLinecap","strokeLinejoin","children","d","BrainIcon","_jsxs","CheckIcon","ChevronDownIcon","CloseIcon","CopyIcon","x","y","rx","ry","DatabaseIcon","cx","cy","DefaultLogoIcon","DownloadIcon","points","x1","x2","y1","y2","EditIcon","ExternalLinkIcon","FileIcon","FloatingIcon","FullscreenExitIcon","FullscreenIcon","GlobeIcon","r","InfoIcon","MailIcon","SearchIcon","SendIcon","SidebarIcon","SparklesIcon","StopCircleIcon","TerminalIcon","UserPlusIcon","WrenchIcon","Dropdown","open","onClose","anchorRef","placement","panelRef","pos","setPos","top","left","ref","handler","active","useEffect","listener","contains","target","document","addEventListener","removeEventListener","useClickOutside","useCallback","rect","getBoundingClientRect","right","bottom","portalTarget","el","node","classList","parentElement","findChatbotRoot","createPortal","style","Spinner","Tooltip","title","show","setShow","onMouseEnter","onMouseLeave","modeOptions","mode","label","getIcon","ChatHeader","agentName","agents","selectedAgent","transferredFrom","agentMenuOpen","onAgentMenuToggle","onAgentMenuClose","onSwitchAgent","modeMenuOpen","onModeMenuToggle","onModeMenuClose","onModeChange","onNewChat","logoIcon","agentDashboardUrl","agentAnchorRef","modeAnchorRef","CurrentModeIcon","onClick","agent","description","opt","ChatInput","onInputChange","onSend","onStop","onFileAdd","onFileRemove","onPaste","separatorColor","fileInputRef","textareaRef","isFileManagementEnabled","Boolean","hasContent","hasFilesUploading","some","canSend","borderTopColor","borderTopWidth","i","multiple","hidden","onChange","click","placeholder","min","scrollHeight","onKeyDown","shiftKey","rows","maxHeight","disabled","ThinkingTextBubble","cleaned","scrollTop","animation","ChatThinking","StatusIcon","showDots","rawNames","lower","n","count","includes","display","toUpperCase","unique","Set","consultName","checkCount","fetchCount","targetName","resolveStatusVisual","_Fragment","delay","isRelativeHref","href","test","MarkdownMessage","onRelativeLinkClick","copiedBlock","setCopiedBlock","Markdown","remarkPlugins","remarkGfm","components","code","match","exec","codeStr","String","handleCopyCode","navigator","clipboard","writeText","ul","ol","blockquote","openInNewTab","rel","h1","h2","h3","table","th","td","ChatMessages","onDownloadFile","messagesEndRef","toolDetailMsgId","setToolDetailMsgId","scrollIntoView","behavior","renderAttachmentCard","att","isWorking","sizeLabel","bytes","toFixed","join","buildAssistantBlocks","msg","loading","parts","re","lastIndex","index","tail","splitFileMarkers","attByFileId","Map","used","blocks","forEach","part","get","add","has","isAssistant","isEmpty","isStreamingMessage","tn","ChatWelcome","firstName","promptSuggestions","onPromptClick","fontFamily","prompt","DEFAULT_SUGGESTIONS","ChatPanel","topOffset","user","accentColor","draftBorderColor","resizable","onWidthChange","onResizeStart","onResizeEnd","disableFileManagement","onDownloadError","pushContentSelector","setModeMenuOpen","setAgentMenuOpen","handleSwitchAgent","setAgents","setSelectedAgent","then","savedSlug","find","catch","onSwitch","useAgents","sidebarWidth","handleResizeStart","defaultWidth","isResizing","setSidebarWidth","stored","parseInt","isNaN","setIsResizing","isResizingRef","sidebarWidthRef","onWidthChangeRef","onResizeEndRef","handleMouseMove","newWidth","innerWidth","clientX","maxWidth","clamped","max","handleMouseUp","cursor","userSelect","handleWindowResize","useSidebarResize","pushWidth","documentElement","setProperty","contentElement","querySelector","originalPaddingRight","paddingRight","originalTransition","transition","resolvedLogo","downloadPathProvided","download","canDownload","handleDownloadFile","url","encodeURIComponent","credentials","blob","objectUrl","URL","createObjectURL","link","createElement","appendChild","remove","revokeObjectURL","cssVars","clearStaleConversation","restored","containerClasses","base","containerStyle","onMouseDown","_","j","ChatToggleButton","isOpen","onToggle","icon","resolvedIcon","borderColor","color","backgroundColor","currentTarget"],"mappings":"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"}
|