@meetsmore-oss/use-ai-client 1.2.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/dist/bundled.js +34913 -0
- package/dist/bundled.js.map +1 -0
- package/dist/chunk-EGD4LT6R.js +46 -0
- package/dist/chunk-EGD4LT6R.js.map +1 -0
- package/dist/chunk-EGKUR4C7.js +13 -0
- package/dist/chunk-EGKUR4C7.js.map +1 -0
- package/dist/index.d.ts +1953 -0
- package/dist/index.js +4502 -0
- package/dist/index.js.map +1 -0
- package/dist/types-INERONQV.js +9 -0
- package/dist/types-INERONQV.js.map +1 -0
- package/dist/types-TVUXB3NB.js +9 -0
- package/dist/types-TVUXB3NB.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/useAI.ts","../src/providers/useAIProvider.tsx","../src/types.ts","../src/theme/strings.ts","../src/theme/theme.ts","../src/components/UseAIFloatingButton.tsx","../src/components/UseAIChatPanel.tsx","../src/components/MarkdownContent.tsx","../src/components/FileChip.tsx","../src/hooks/useSlashCommands.tsx","../src/commands/types.ts","../src/components/CommandAutocomplete.tsx","../src/hooks/useFileUpload.tsx","../src/fileUpload/types.ts","../src/hooks/useDropdownState.tsx","../src/components/UseAIFloatingChatWrapper.tsx","../src/components/UseAIChat.tsx","../src/client.ts","../src/defineTool.ts","../src/providers/chatRepository/LocalStorageChatRepository.ts","../src/fileUpload/EmbedFileUploadBackend.ts","../src/hooks/useChatManagement.ts","../src/hooks/useAgentSelection.ts","../src/hooks/useCommandManagement.ts","../src/commands/LocalStorageCommandRepository.ts","../src/hooks/useToolRegistry.ts","../src/hooks/usePromptState.ts","../src/hooks/useStableTools.ts","../src/useAIWorkflow.ts","../src/index.ts"],"sourcesContent":["import { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport { useAIContext } from './providers/useAIProvider';\nimport { type ToolsDefinition } from './defineTool';\nimport { useStableTools } from './hooks/useStableTools';\nimport type { AGUIEvent, RunErrorEvent } from './types';\nimport { EventType } from './types';\n\nfunction namespaceTools(tools: ToolsDefinition, namespace: string): ToolsDefinition {\n const namespacedTools: ToolsDefinition = {};\n\n for (const [toolName, tool] of Object.entries(tools)) {\n const namespacedName = `${namespace}_${toolName}`;\n namespacedTools[namespacedName] = tool;\n }\n\n return namespacedTools;\n}\n\n/**\n * Options for configuring the useAI hook.\n */\nexport interface UseAIOptions {\n /** Tools to make available to the AI for this component */\n tools?: ToolsDefinition;\n /** Callback function invoked when an error occurs */\n onError?: (error: Error) => void;\n /** Optional ID for namespacing tools to avoid naming conflicts */\n id?: string;\n /** Optional UI context or description to send to the AI */\n prompt?: string;\n /**\n * Mark this component as invisible (no visual state).\n * When true, tool responses are sent immediately without waiting for prompt changes.\n * Use this for provider-type components that expose tools but don't render UI.\n * @default false\n */\n invisible?: boolean;\n /**\n * Optional array of suggestion strings to display as call-to-action prompts\n * when the chat is empty. The chat UI will randomly select up to 4 suggestions\n * to display to the user.\n */\n suggestions?: string[];\n /**\n * Whether the AI features are enabled for this hook.\n * When false, tools are not registered and the hook returns a disabled state.\n * Useful for feature flagging AI functionality.\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * Return value from the useAI hook.\n */\nexport interface UseAIResult {\n /** The AI's response text, or null if no response yet */\n response: string | null;\n /** Whether the AI is currently processing a request */\n loading: boolean;\n /** Error object if an error occurred, or null */\n error: Error | null;\n /** Function to send a prompt to the AI */\n generate: (prompt: string) => Promise<void>;\n /** Whether the client is connected to the server */\n connected: boolean;\n /** Ref to attach to the component for context extraction */\n ref: React.RefObject<HTMLDivElement>;\n}\n\n/**\n * React hook for integrating AI capabilities into a component.\n * Registers tools with the AI server and provides methods to interact with the AI.\n */\nexport function useAI(options: UseAIOptions = {}): UseAIResult {\n const { enabled = true } = options;\n const { connected, tools, client, prompts } = useAIContext();\n const { register: registerTools, unregister: unregisterTools } = tools;\n const { update: updatePrompt, registerWaiter, unregisterWaiter } = prompts;\n const [response, setResponse] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const hookId = useRef(`useAI-${Math.random().toString(36).substr(2, 9)}`);\n const toolsRef = useRef<ToolsDefinition>({});\n const componentRef = useRef<HTMLDivElement>(null);\n\n // Prompt change tracking\n const promptChangeResolvers = useRef<Array<() => void>>([]);\n\n // Stabilize tools to prevent render loops from unstable references.\n // This allows users to define tools inline without memoization.\n const stableTools = useStableTools(options.tools);\n\n // Derive a key for effect dependencies (based on tool names only)\n const toolsKey = useMemo(() => {\n if (!options.tools) return '';\n return Object.keys(options.tools).sort().join(',');\n }, [options.tools]);\n\n const memoizedSuggestions = useMemo(() => options.suggestions, [options.suggestions]);\n\n useEffect(() => {\n if (componentRef.current) {\n componentRef.current.setAttribute('data-useai-context', 'true');\n }\n }, []);\n\n // Create waitForPromptChange function that resolves when prompt changes (with timeout)\n const waitForPromptChange = useCallback((): Promise<void> => {\n return new Promise<void>(resolve => {\n const timeoutMs = 100;\n\n const timeoutId = setTimeout(() => {\n const index = promptChangeResolvers.current.indexOf(resolveAndCleanup);\n if (index !== -1) {\n promptChangeResolvers.current.splice(index, 1);\n }\n resolve();\n }, timeoutMs);\n\n const resolveAndCleanup = () => {\n clearTimeout(timeoutId);\n resolve();\n };\n\n promptChangeResolvers.current.push(resolveAndCleanup);\n });\n }, []);\n\n // Register/unregister the waiter with the provider\n useEffect(() => {\n if (!enabled || options.invisible) return;\n\n registerWaiter(hookId.current, waitForPromptChange);\n return () => {\n unregisterWaiter(hookId.current);\n };\n }, [enabled, options.invisible, registerWaiter, unregisterWaiter, waitForPromptChange]);\n\n // Update prompt and resolve any pending waiters when prompt changes\n useEffect(() => {\n if (!enabled) return;\n updatePrompt(hookId.current, options.prompt, memoizedSuggestions);\n\n // Resolve any pending prompt change waiters\n if (promptChangeResolvers.current.length > 0) {\n promptChangeResolvers.current.forEach(resolve => resolve());\n promptChangeResolvers.current = [];\n }\n }, [enabled, options.prompt, memoizedSuggestions, updatePrompt]);\n\n // Store updatePrompt in a ref to avoid stale closure in unmount cleanup\n const updatePromptRef = useRef(updatePrompt);\n updatePromptRef.current = updatePrompt;\n\n // Cleanup prompt and suggestions on unmount only (separate effect with empty deps)\n useEffect(() => {\n const id = hookId.current;\n return () => {\n updatePromptRef.current(id, undefined, undefined);\n };\n }, []);\n\n // Register tools\n useEffect(() => {\n if (!enabled) return;\n if (stableTools) {\n const componentId = options.id || componentRef.current?.id;\n const toolsToRegister = componentId\n ? namespaceTools(stableTools, componentId)\n : stableTools;\n\n registerTools(hookId.current, toolsToRegister, { invisible: options.invisible });\n toolsRef.current = toolsToRegister;\n }\n\n return () => {\n if (stableTools) {\n unregisterTools(hookId.current);\n }\n };\n }, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools]);\n\n useEffect(() => {\n if (!enabled || !client) return;\n\n const unsubscribe = client.onEvent(hookId.current, (event: AGUIEvent) => {\n handleAGUIEvent(event);\n });\n\n return () => {\n unsubscribe();\n };\n }, [enabled, client]);\n\n const handleAGUIEvent = useCallback(async (event: AGUIEvent) => {\n switch (event.type) {\n case EventType.TEXT_MESSAGE_END: {\n const content = client?.currentMessageContent;\n if (content) {\n setResponse(content);\n setLoading(false);\n }\n break;\n }\n\n case EventType.RUN_ERROR: {\n const errorEvent = event as RunErrorEvent;\n const error = new Error(errorEvent.message);\n setError(error);\n setLoading(false);\n options.onError?.(error);\n break;\n }\n }\n }, [client, options.onError]);\n\n const generate = useCallback(async (prompt: string) => {\n if (!enabled) {\n const error = new Error('AI features are disabled');\n setError(error);\n options.onError?.(error);\n return;\n }\n\n if (!client?.isConnected()) {\n const error = new Error('Not connected to server');\n setError(error);\n options.onError?.(error);\n return;\n }\n\n setLoading(true);\n setError(null);\n setResponse(null);\n\n try {\n client.sendPrompt(prompt);\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Unknown error');\n setError(error);\n setLoading(false);\n options.onError?.(error);\n }\n }, [enabled, client, options.onError]);\n\n return {\n response,\n loading,\n error,\n generate,\n connected: enabled && connected,\n ref: componentRef,\n };\n}\n","import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback, useRef } from 'react';\nimport type { UseAIConfig, AGUIEvent, ToolCallEndEvent, RunErrorEvent, AgentInfo, TextMessageContentEvent } from '../types';\nimport { EventType, ErrorCode } from '../types';\nimport { UseAIFloatingButton } from '../components/UseAIFloatingButton';\nimport { UseAIChatPanel, type Message } from '../components/UseAIChatPanel';\nimport { UseAIFloatingChatWrapper, CloseButton } from '../components/UseAIFloatingChatWrapper';\nimport { __UseAIChatContext, type ChatUIContextValue } from '../components/UseAIChat';\nimport { UseAIClient } from '../client';\nimport { convertToolsToDefinitions, executeDefinedTool, type ToolsDefinition } from '../defineTool';\nimport type { ChatRepository, Chat, PersistedMessageContent, PersistedContentPart } from './chatRepository/types';\nimport { LocalStorageChatRepository } from './chatRepository/LocalStorageChatRepository';\nimport type { FileAttachment, FileUploadConfig } from '../fileUpload/types';\nimport { EmbedFileUploadBackend } from '../fileUpload/EmbedFileUploadBackend';\nimport type { MultimodalContent } from '@meetsmore-oss/use-ai-core';\nimport type { CommandRepository, SavedCommand } from '../commands/types';\nimport { useChatManagement } from '../hooks/useChatManagement';\nimport { useAgentSelection } from '../hooks/useAgentSelection';\nimport { useCommandManagement } from '../hooks/useCommandManagement';\nimport { useToolRegistry } from '../hooks/useToolRegistry';\nimport { usePromptState } from '../hooks/usePromptState';\nimport { ThemeContext, StringsContext, defaultTheme, defaultStrings } from '../theme';\nimport type { UseAITheme, UseAIStrings } from '../theme';\n\n/**\n * Chat management context (from useChatManagement hook).\n */\nexport interface ChatContextValue {\n /** The current chat ID */\n currentId: string | null;\n /** Creates a new chat and switches to it */\n create: () => Promise<string>;\n /** Loads an existing chat by ID */\n load: (chatId: string) => Promise<void>;\n /** Deletes a chat by ID */\n delete: (chatId: string) => Promise<void>;\n /** Lists all available chats */\n list: () => Promise<Array<Omit<Chat, 'messages'>>>;\n /** Clears the current chat messages */\n clear: () => Promise<void>;\n}\n\n/**\n * Agent selection context (from useAgentSelection hook).\n */\nexport interface AgentContextValue {\n /** List of available agents from the server */\n available: AgentInfo[];\n /** The default agent ID from the server */\n default: string | null;\n /** The currently selected agent ID (null means use server default) */\n selected: string | null;\n /** Sets the agent to use for requests */\n set: (agentId: string | null) => void;\n}\n\n/**\n * Command management context (from useCommandManagement hook).\n */\nexport interface CommandContextValue {\n /** List of saved slash commands */\n list: SavedCommand[];\n /** Refreshes the commands list from storage */\n refresh: () => Promise<void>;\n /** Saves a new command */\n save: (name: string, text: string) => Promise<string>;\n /** Renames an existing command */\n rename: (id: string, newName: string) => Promise<void>;\n /** Deletes a command by ID */\n delete: (id: string) => Promise<void>;\n}\n\n/**\n * Tool registry context (from useToolRegistry hook).\n */\nexport interface ToolRegistryContextValue {\n /** Registers tools for a specific component */\n register: (id: string, tools: ToolsDefinition, options?: { invisible?: boolean }) => void;\n /** Unregisters tools for a specific component */\n unregister: (id: string) => void;\n}\n\n/**\n * Prompt management context.\n */\nexport interface PromptsContextValue {\n /** Updates the prompt and suggestions for a specific component */\n update: (id: string, prompt?: string, suggestions?: string[]) => void;\n /** Registers a waiter function for a component */\n registerWaiter: (id: string, waiter: () => Promise<void>) => void;\n /** Unregisters a waiter function */\n unregisterWaiter: (id: string) => void;\n}\n\n/**\n * Context value provided by UseAIProvider.\n * Contains connection state and methods for managing tools and prompts.\n */\nexport interface UseAIContextValue {\n /** The WebSocket URL of the UseAI server */\n serverUrl: string;\n /** Whether the client is connected to the server */\n connected: boolean;\n /** The underlying WebSocket client instance */\n client: UseAIClient | null;\n /** Tool registry (from useToolRegistry hook) */\n tools: ToolRegistryContextValue;\n /** Prompt management */\n prompts: PromptsContextValue;\n /** Chat management (from useChatManagement hook) */\n chat: ChatContextValue;\n /** Agent selection (from useAgentSelection hook) */\n agents: AgentContextValue;\n /** Command management (from useCommandManagement hook) */\n commands: CommandContextValue;\n}\n\n/**\n * React context for UseAI provider state.\n * @internal This is exported only for testing purposes and should not be used directly.\n * Use the {@link useAIContext} hook instead.\n */\nexport const __UseAIContext = createContext<UseAIContextValue | null>(null);\n\n/**\n * Flag to track if the \"no provider\" warning has been logged.\n * Prevents spamming the console with repeated warnings.\n */\nlet hasWarnedAboutMissingProvider = false;\n\n/**\n * No-op context value returned when UseAIProvider is not present.\n * Allows hooks to gracefully degrade instead of crashing.\n */\nconst noOpContextValue: UseAIContextValue = {\n serverUrl: '',\n connected: false,\n client: null,\n tools: {\n register: () => {},\n unregister: () => {},\n },\n prompts: {\n update: () => {},\n registerWaiter: () => {},\n unregisterWaiter: () => {},\n },\n chat: {\n currentId: null,\n create: async () => '',\n load: async () => {},\n delete: async () => {},\n list: async () => [],\n clear: async () => {},\n },\n agents: {\n available: [],\n default: null,\n selected: null,\n set: () => {},\n },\n commands: {\n list: [],\n refresh: async () => {},\n save: async () => '',\n rename: async () => {},\n delete: async () => {},\n },\n};\n\n/**\n * Props for custom floating button component.\n * Used to customize the appearance and behavior of the AI chat trigger button.\n */\nexport interface FloatingButtonProps {\n /** Callback when the button is clicked */\n onClick: () => void;\n /** Whether the client is connected to the server */\n connected: boolean;\n /** Whether there are unread messages */\n hasUnread?: boolean;\n}\n\n/**\n * Props for custom chat panel component.\n * Used to customize the appearance and behavior of the AI chat interface.\n */\nexport interface ChatPanelProps {\n /** Whether the chat panel is open */\n isOpen: boolean;\n /** Callback when the panel should close */\n onClose: () => void;\n /** Callback when a message is sent */\n onSendMessage: (message: string) => void;\n /** Array of messages in the conversation */\n messages: Message[];\n /** Whether the AI is currently processing */\n loading: boolean;\n /** Whether the client is connected to the server */\n connected: boolean;\n /** Optional array of suggestion strings to display when chat is empty */\n suggestions?: string[];\n /** List of available agents from the server */\n availableAgents?: AgentInfo[];\n /** The default agent ID from the server */\n defaultAgent?: string | null;\n /** The currently selected agent ID */\n selectedAgent?: string | null;\n /** Callback when agent is changed */\n onAgentChange?: (agentId: string | null) => void;\n}\n\nexport interface UseAIProviderProps extends UseAIConfig {\n children: ReactNode;\n systemPrompt?: string;\n CustomButton?: React.ComponentType<FloatingButtonProps> | null;\n CustomChat?: React.ComponentType<ChatPanelProps> | null;\n /**\n * Custom chat repository for message persistence.\n * Defaults to LocalStorageChatRepository if not provided.\n */\n chatRepository?: ChatRepository;\n /**\n * Callback to provide HTTP headers for MCP endpoints.\n * Called each time AI is invoked by use-ai.\n * Returns a mapping of MCP endpoint patterns to header configurations.\n *\n * Patterns can be:\n * - Constant strings: `https://api.example.com` - Exact match\n * - Glob patterns: `https://*.meetsmore.com` - Wildcard matching using picomatch\n *\n * @example\n * ```typescript\n * mcpHeadersProvider={() => ({\n * // Exact match\n * 'https://api.example.com': {\n * headers: { 'Authorization': `Bearer ${userToken}` }\n * },\n * // Wildcard subdomain\n * 'https://*.meetsmore.com': {\n * headers: { 'X-API-Key': apiKey }\n * },\n * // Multiple wildcards\n * '*://*.example.com': {\n * headers: { 'X-Custom': 'value' }\n * }\n * })}\n * ```\n */\n mcpHeadersProvider?: () => import('@meetsmore-oss/use-ai-core').McpHeadersMap | Promise<import('@meetsmore-oss/use-ai-core').McpHeadersMap>;\n /**\n * Configuration for file uploads.\n * File upload is enabled by default with EmbedFileUploadBackend, 10MB max size,\n * and accepts images and PDFs.\n *\n * Set to `false` to disable file uploads.\n *\n * @default { backend: EmbedFileUploadBackend, maxFileSize: 10MB, acceptedTypes: ['image/*', 'application/pdf'] }\n *\n * @example\n * ```typescript\n * // Custom config\n * fileUploadConfig={{\n * backend: new EmbedFileUploadBackend(),\n * maxFileSize: 5 * 1024 * 1024, // 5MB\n * acceptedTypes: ['image/*'],\n * }}\n *\n * // Disable file uploads\n * fileUploadConfig={false}\n * ```\n */\n fileUploadConfig?: FileUploadConfig | false;\n /**\n * Custom command repository for slash command persistence.\n * Defaults to LocalStorageCommandRepository if not provided.\n */\n commandRepository?: CommandRepository;\n /**\n * Whether to render the built-in chat UI (floating button + panel).\n * Set to false when using the `<UseAIChat>` component to control chat placement.\n * @default true\n */\n renderChat?: boolean;\n /**\n * Custom theme for all chat UI components.\n * Partial allows overriding only specific values.\n */\n theme?: Partial<UseAITheme>;\n /**\n * Custom strings for all chat UI components.\n * Useful for internationalization (i18n) or branding.\n * Partial allows overriding only specific strings.\n */\n strings?: Partial<UseAIStrings>;\n /**\n * List of agent IDs to show in the UI.\n * When provided, only agents with these IDs will be shown (if they exist on the server).\n *\n * @example\n * ```typescript\n * <UseAIProvider\n * serverUrl=\"wss://your-server.com\"\n * visibleAgentIds={['claude-sonnet', 'claude-opus']}\n * >\n * <App />\n * </UseAIProvider>\n * ```\n */\n visibleAgentIds?: AgentInfo['id'][];\n}\n\n/**\n * Default file upload configuration.\n * - Backend: EmbedFileUploadBackend (base64 data URLs)\n * - Max size: 10MB\n * - Accepted types: images and PDFs\n */\nconst DEFAULT_FILE_UPLOAD_CONFIG: FileUploadConfig = {\n backend: new EmbedFileUploadBackend(),\n maxFileSize: 10 * 1024 * 1024, // 10MB\n acceptedTypes: ['image/*', 'application/pdf'],\n};\n\n/**\n * Provider component that manages AI client connection and tool registration.\n * Must wrap all components that use the useAI hook.\n *\n * Features:\n * - Establishes and maintains WebSocket connection to UseAI server\n * - Aggregates tools from all child useAI hooks\n * - Handles tool execution requests from the AI\n * - Provides floating button and chat panel UI\n *\n * @param props - Provider configuration and children\n *\n * @example\n * ```typescript\n * import { UseAIProvider } from '@meetsmore-oss/use-ai-client';\n *\n * function App() {\n * return (\n * <UseAIProvider\n * serverUrl=\"wss://your-server.com\"\n * systemPrompt=\"You are a helpful assistant for managing todos\"\n * >\n * <YourApp />\n * </UseAIProvider>\n * );\n * }\n * ```\n */\nexport function UseAIProvider({\n serverUrl,\n children,\n systemPrompt,\n CustomButton,\n CustomChat,\n chatRepository,\n mcpHeadersProvider,\n fileUploadConfig: fileUploadConfigProp,\n commandRepository,\n renderChat = true,\n theme: customTheme,\n strings: customStrings,\n visibleAgentIds,\n}: UseAIProviderProps) {\n // Compute effective file upload config: use default if undefined, disable if false\n const fileUploadConfig = fileUploadConfigProp === false\n ? undefined\n : (fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG);\n\n // Merge custom theme/strings with defaults\n const theme = { ...defaultTheme, ...customTheme };\n const strings = { ...defaultStrings, ...customStrings };\n\n const [connected, setConnected] = useState(false);\n const [isChatOpen, setIsChatOpen] = useState(false);\n const [loading, setLoading] = useState(false);\n const [streamingText, setStreamingText] = useState('');\n // Track which chat the current streaming text belongs to\n const streamingChatIdRef = useRef<string | null>(null);\n\n const clientRef = useRef<UseAIClient | null>(null);\n const repositoryRef = useRef<ChatRepository>(\n chatRepository || new LocalStorageChatRepository()\n );\n\n // Initialize tool registry hook\n const {\n registerTools,\n unregisterTools,\n isInvisible,\n aggregatedTools,\n hasTools,\n aggregatedToolsRef,\n toolOwnershipRef,\n } = useToolRegistry();\n\n // Initialize prompt state hook\n const {\n updatePrompt,\n registerWaiter,\n unregisterWaiter,\n getWaiter,\n aggregatedSuggestions,\n promptsRef,\n } = usePromptState({\n systemPrompt,\n clientRef,\n connected,\n });\n\n // Initialize chat management hook\n const chatManagement = useChatManagement({\n repository: repositoryRef.current,\n clientRef,\n });\n\n const {\n currentChatId,\n pendingChatId,\n messages,\n displayedChatId,\n createNewChat,\n loadChat,\n deleteChat,\n listChats,\n clearCurrentChat,\n activatePendingChat,\n saveUserMessage,\n saveAIResponse,\n } = chatManagement;\n\n // Initialize agent selection hook\n const {\n availableAgents,\n defaultAgent,\n selectedAgent,\n setAgent,\n } = useAgentSelection({ clientRef, connected, visibleAgentIds });\n\n // Initialize command management hook\n const {\n commands,\n refreshCommands,\n saveCommand,\n renameCommand,\n deleteCommand,\n } = useCommandManagement({ repository: commandRepository });\n\n useEffect(() => {\n console.log('[UseAIProvider] Initializing client with serverUrl:', serverUrl);\n const client = new UseAIClient(serverUrl);\n\n // Set MCP headers provider if provided\n if (mcpHeadersProvider) {\n client.setMcpHeadersProvider(mcpHeadersProvider);\n }\n\n // Subscribe to connection state changes (handles initial connection, reconnection, and disconnection)\n const unsubscribeConnection = client.onConnectionStateChange((isConnected) => {\n console.log('[UseAIProvider] Connection state changed:', isConnected);\n setConnected(isConnected);\n });\n\n console.log('[UseAIProvider] Connecting...');\n client.connect();\n\n const unsubscribe = client.onEvent('globalChat', async (event: AGUIEvent) => {\n if (event.type === EventType.TOOL_CALL_END) {\n const toolCallEnd = event as ToolCallEndEvent;\n const toolCallId = toolCallEnd.toolCallId;\n\n // Get the accumulated tool call data\n const toolCallData = client['currentToolCalls'].get(toolCallId);\n if (!toolCallData) {\n console.error(`[Provider] Tool call ${toolCallId} not found`);\n return;\n }\n\n const name = toolCallData.name;\n const input = JSON.parse(toolCallData.args);\n\n // Check if this tool belongs to a useAI hook (not a workflow)\n // If the tool doesn't exist in our aggregated tools, it's a workflow tool\n // and will be handled by useAIWorkflow's event listener\n if (!aggregatedToolsRef.current[name]) {\n console.log(`[Provider] Tool \"${name}\" not found in useAI tools, skipping (likely a workflow tool)`);\n return;\n }\n\n try {\n const ownerId = toolOwnershipRef.current.get(name);\n console.log(`[useAI] Tool \"${name}\" owned by component:`, ownerId);\n\n console.log('[useAI] Executing tool...');\n const result = await executeDefinedTool(aggregatedToolsRef.current, name, input);\n\n // Check if result indicates an error - if so, skip waiting for prompt change\n // Error results typically don't trigger state changes\n const isErrorResult = result && typeof result === 'object' &&\n ('error' in result || (result as Record<string, unknown>).success === false);\n\n // Check if component is invisible (no visual state to wait for)\n const ownerIsInvisible = ownerId ? isInvisible(ownerId) : false;\n\n // Wait for prompt to update (via waiter registered by useAI) unless it's an error or invisible\n if (ownerId && !isErrorResult && !ownerIsInvisible) {\n const waiter = getWaiter(ownerId);\n if (waiter) {\n console.log(`[useAI] Waiting for prompt change from ${ownerId}...`);\n await waiter();\n console.log('[useAI] Prompt change wait complete');\n }\n } else if (isErrorResult) {\n console.log('[useAI] Tool returned error, skipping prompt wait');\n } else if (ownerIsInvisible) {\n console.log('[useAI] Component is invisible, skipping prompt wait');\n }\n\n // Build updated state\n let updatedState: unknown = null;\n if (ownerId) {\n const prompt = promptsRef.current.get(ownerId);\n if (prompt) {\n updatedState = { context: prompt };\n console.log(`[useAI] Updated state from ${ownerId}`);\n }\n }\n\n client.sendToolResponse(toolCallId, result, updatedState);\n } catch (err) {\n console.error('Tool execution error:', err);\n client.sendToolResponse(toolCallId, {\n error: err instanceof Error ? err.message : 'Unknown error',\n });\n }\n } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {\n // Update streaming text in real-time for UI display\n const contentEvent = event as TextMessageContentEvent;\n setStreamingText(prev => prev + contentEvent.delta);\n } else if (event.type === EventType.TEXT_MESSAGE_END) {\n const content = client.currentMessageContent;\n if (content) {\n console.log('[Provider] Received text message:', content.substring(0, 100));\n saveAIResponse(content); // Fire-and-forget is intentional here\n setStreamingText(''); // Clear streaming text now that message is complete\n streamingChatIdRef.current = null; // Clear streaming chat association\n setLoading(false);\n }\n } else if (event.type === EventType.RUN_ERROR) {\n const errorEvent = event as RunErrorEvent;\n const errorCode = errorEvent.message as ErrorCode;\n console.error('[Provider] Run error:', errorCode);\n\n // Get error message from strings (customizable via strings prop)\n const userMessage = strings.errors[errorCode] || strings.errors[ErrorCode.UNKNOWN_ERROR];\n\n // Display error message in chat UI with error styling\n saveAIResponse(userMessage, 'error'); // Fire-and-forget is intentional here\n setStreamingText(''); // Clear any partial streaming text\n streamingChatIdRef.current = null; // Clear streaming chat association\n\n setLoading(false);\n }\n });\n\n clientRef.current = client;\n\n return () => {\n unsubscribeConnection();\n unsubscribe();\n client.disconnect();\n };\n }, [serverUrl]);\n\n // Update MCP headers provider when it changes\n useEffect(() => {\n const client = clientRef.current;\n if (!client) return;\n\n if (mcpHeadersProvider) {\n client.setMcpHeadersProvider(mcpHeadersProvider);\n }\n }, [mcpHeadersProvider]);\n\n const lastRegisteredToolsRef = useRef<string>('');\n\n useEffect(() => {\n const client = clientRef.current;\n if (!client || !client.isConnected() || !hasTools) return;\n\n const toolKeys = Object.keys(aggregatedTools).sort().join(',');\n if (toolKeys === lastRegisteredToolsRef.current) {\n console.log('[Provider] Skipping re-registration, tools unchanged');\n return;\n }\n\n lastRegisteredToolsRef.current = toolKeys;\n console.log('[Provider] Registering tools:', toolKeys);\n\n try {\n const toolDefinitions = convertToolsToDefinitions(aggregatedTools);\n console.log(`[Provider] Registering ${toolDefinitions.length} tools`);\n // Only register tools here - state is updated separately via updatePrompt\n client.registerTools(toolDefinitions);\n } catch (err) {\n console.error('Failed to register tools:', err);\n }\n }, [hasTools, aggregatedTools, connected]);\n\n const handleSendMessage = useCallback(async (message: string, attachments?: FileAttachment[]) => {\n if (!clientRef.current) return;\n\n // Clear any previous streaming text when starting a new message\n setStreamingText('');\n\n // Activate pending chat if exists (user is sending first message to it)\n const activatedChatId = activatePendingChat();\n const activeChatId = activatedChatId || currentChatId;\n\n // Track which chat this streaming response belongs to\n streamingChatIdRef.current = activeChatId;\n\n // Build content for storage and sending\n let persistedContent: PersistedMessageContent = message;\n let multimodalContent: MultimodalContent[] | undefined;\n\n if (attachments && attachments.length > 0) {\n // Use the configured backend or default to EmbedFileUploadBackend\n const backend = fileUploadConfig?.backend ?? new EmbedFileUploadBackend();\n\n // Build persisted content (metadata only) for storage\n const persistedParts: PersistedContentPart[] = [];\n if (message.trim()) {\n persistedParts.push({ type: 'text', text: message });\n }\n for (const attachment of attachments) {\n persistedParts.push({\n type: 'file',\n file: {\n name: attachment.file.name,\n size: attachment.file.size,\n mimeType: attachment.file.type,\n },\n });\n }\n persistedContent = persistedParts;\n\n // Build multimodal content (with URLs) for sending to AI\n const contentParts: MultimodalContent[] = [];\n if (message.trim()) {\n contentParts.push({ type: 'text', text: message });\n }\n\n // Convert files to URLs\n for (const attachment of attachments) {\n try {\n const url = await backend.prepareForSend(attachment.file);\n if (attachment.file.type.startsWith('image/')) {\n contentParts.push({ type: 'image', url });\n } else {\n contentParts.push({\n type: 'file',\n url,\n mimeType: attachment.file.type,\n name: attachment.file.name,\n });\n }\n } catch (error) {\n console.error('[Provider] Failed to prepare file for send:', error);\n }\n }\n\n multimodalContent = contentParts;\n }\n\n // Save user message to storage\n if (activeChatId) {\n await saveUserMessage(activeChatId, persistedContent);\n }\n\n // State is already up-to-date via updatePrompt calls from useAI hooks\n setLoading(true);\n await clientRef.current.sendPrompt(message, multimodalContent);\n }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig]);\n\n const value: UseAIContextValue = {\n serverUrl,\n connected,\n client: clientRef.current,\n tools: {\n register: registerTools,\n unregister: unregisterTools,\n },\n prompts: {\n update: updatePrompt,\n registerWaiter,\n unregisterWaiter,\n },\n chat: {\n currentId: currentChatId,\n create: createNewChat,\n load: loadChat,\n delete: deleteChat,\n list: listChats,\n clear: clearCurrentChat,\n },\n agents: {\n available: availableAgents,\n default: defaultAgent,\n selected: selectedAgent,\n set: setAgent,\n },\n commands: {\n list: commands,\n refresh: refreshCommands,\n save: saveCommand,\n rename: renameCommand,\n delete: deleteCommand,\n },\n };\n\n // Only show streaming text if it belongs to the currently displayed chat\n // This prevents streaming from a previous chat appearing when switching chats\n const effectiveStreamingText = streamingChatIdRef.current === displayedChatId ? streamingText : '';\n\n // Chat UI context value - used by UseAIChat component\n const chatUIContextValue: ChatUIContextValue = {\n connected,\n loading,\n sendMessage: handleSendMessage,\n messages,\n streamingText: effectiveStreamingText,\n suggestions: aggregatedSuggestions,\n fileUploadConfig,\n history: {\n currentId: displayedChatId,\n create: createNewChat,\n load: loadChat,\n delete: deleteChat,\n list: listChats,\n },\n agents: {\n available: availableAgents,\n default: defaultAgent,\n selected: selectedAgent,\n set: setAgent,\n },\n commands: {\n list: commands,\n save: saveCommand,\n rename: renameCommand,\n delete: deleteCommand,\n },\n ui: {\n isOpen: isChatOpen,\n setOpen: setIsChatOpen,\n },\n };\n\n // Use custom components if provided, or defaults (unless explicitly null to disable UI)\n // When either component is explicitly null, disable the UI entirely\n const isUIDisabled = CustomButton === null || CustomChat === null;\n const ButtonComponent = isUIDisabled ? null : (CustomButton || UseAIFloatingButton);\n const hasCustomChat = CustomChat !== undefined && CustomChat !== null;\n\n // Common props for the chat panel\n const chatPanelProps = {\n onSendMessage: handleSendMessage,\n messages,\n loading,\n connected,\n streamingText: effectiveStreamingText,\n currentChatId: displayedChatId,\n onNewChat: createNewChat,\n onLoadChat: loadChat,\n onDeleteChat: deleteChat,\n onListChats: listChats,\n suggestions: aggregatedSuggestions,\n availableAgents,\n defaultAgent,\n selectedAgent,\n onAgentChange: setAgent,\n fileUploadConfig,\n commands,\n onSaveCommand: saveCommand,\n onRenameCommand: renameCommand,\n onDeleteCommand: deleteCommand,\n };\n\n // Render function for default floating chat UI\n const renderDefaultChat = () => {\n if (isUIDisabled) return null;\n\n return (\n <UseAIFloatingChatWrapper isOpen={isChatOpen} onClose={() => setIsChatOpen(false)}>\n <UseAIChatPanel\n {...chatPanelProps}\n closeButton={<CloseButton onClick={() => setIsChatOpen(false)} />}\n />\n </UseAIFloatingChatWrapper>\n );\n };\n\n // Render function for custom chat UI (backward compatibility)\n const renderCustomChat = () => {\n if (!CustomChat) return null;\n\n return (\n <CustomChat\n isOpen={isChatOpen}\n onClose={() => setIsChatOpen(false)}\n onSendMessage={handleSendMessage}\n messages={messages}\n loading={loading}\n connected={connected}\n suggestions={aggregatedSuggestions}\n availableAgents={availableAgents}\n defaultAgent={defaultAgent}\n selectedAgent={selectedAgent}\n onAgentChange={setAgent}\n />\n );\n };\n\n // Render built-in chat UI only when renderChat is true\n const renderBuiltInChat = () => {\n if (!renderChat) return null;\n\n return (\n <>\n {ButtonComponent && (\n <ButtonComponent\n onClick={() => setIsChatOpen(true)}\n connected={connected}\n />\n )}\n {hasCustomChat ? renderCustomChat() : renderDefaultChat()}\n </>\n );\n };\n\n return (\n <ThemeContext.Provider value={theme}>\n <StringsContext.Provider value={strings}>\n <__UseAIContext.Provider value={value}>\n <__UseAIChatContext.Provider value={chatUIContextValue}>\n {children}\n {renderBuiltInChat()}\n </__UseAIChatContext.Provider>\n </__UseAIContext.Provider>\n </StringsContext.Provider>\n </ThemeContext.Provider>\n );\n}\n\n/**\n * Hook to access the UseAI context.\n * When used outside a UseAIProvider, returns a no-op context and logs a warning.\n * This allows components with useAI hooks to render even when UseAIProvider\n * is conditionally not rendered (e.g., feature flagged).\n *\n * @returns The UseAI context value (or no-op if provider is missing)\n *\n * @example\n * ```typescript\n * function MyComponent() {\n * const { connected, client } = useAIContext();\n * return <div>Connected: {connected}</div>;\n * }\n * ```\n */\nexport function useAIContext(): UseAIContextValue {\n const context = useContext(__UseAIContext);\n if (!context) {\n if (!hasWarnedAboutMissingProvider) {\n console.warn(\n '[use-ai] useAI hook used without UseAIProvider. AI features will be disabled. ' +\n 'Wrap your app in <UseAIProvider> to enable AI features.'\n );\n hasWarnedAboutMissingProvider = true;\n }\n return noOpContextValue;\n }\n return context;\n}\n","/**\n * Configuration for the UseAI client provider.\n */\nexport interface UseAIConfig {\n /** The WebSocket URL of the UseAI server */\n serverUrl: string;\n}\n\n// Re-export all types from @meetsmore-oss/use-ai-core for convenience\nexport type {\n ToolDefinition,\n // AG-UI types\n Tool,\n Message,\n Context,\n RunAgentInput,\n State,\n RunAgentMessage,\n ToolResultMessage,\n AbortRunMessage,\n // AG-UI event types\n RunStartedEvent,\n RunFinishedEvent,\n RunErrorEvent,\n StepStartedEvent,\n StepFinishedEvent,\n TextMessageStartEvent,\n TextMessageContentEvent,\n TextMessageEndEvent,\n TextMessageChunkEvent,\n ToolCallStartEvent,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n ToolCallChunkEvent,\n ToolCallResultEvent,\n StateSnapshotEvent,\n StateDeltaEvent,\n MessagesSnapshotEvent,\n RawEvent,\n CustomEvent,\n ActivitySnapshotEvent,\n ActivityDeltaEvent,\n AGUIEvent,\n // use-ai extensions\n WorkflowStatus,\n UseAIClientMessage,\n RunWorkflowMessage,\n McpHeadersConfig,\n McpHeadersMap,\n AgentInfo,\n UseAIForwardedProps,\n // Multimodal content types\n TextContent,\n ImageContent,\n FileContent,\n MultimodalContent,\n UserMessageContent,\n} from '@meetsmore-oss/use-ai-core';\n\nexport { EventType, ErrorCode } from '@meetsmore-oss/use-ai-core';\n","import { createContext, useContext } from \"react\";\n\n/**\n * Default text labels for the chat UI.\n * Use for internationalization (i18n) or branding.\n */\nexport const defaultStrings = {\n // Chat panel header\n header: {\n /** Header title when no chat history feature */\n aiAssistant: 'AI Assistant',\n /** Label for new chat button tooltip */\n newChat: 'New Chat',\n /** Delete chat confirmation message */\n deleteConfirm: 'Delete this chat from history?',\n /** Delete button tooltip */\n deleteChat: 'Delete Chat',\n /** Connection status: online */\n online: 'Online',\n /** Connection status: offline */\n offline: 'Offline',\n },\n\n // Chat history dropdown\n chatHistory: {\n /** Chat history: no chats message */\n noChatHistory: 'No chat history yet',\n /** Chat history: active chat indicator */\n active: 'Active',\n },\n\n // Empty chat state\n emptyChat: {\n /** Empty chat welcome message */\n startConversation: 'Start a conversation with the AI assistant',\n /** Empty chat help text */\n askMeToHelp: 'Ask me to help with your tasks!',\n },\n\n // Chat input\n input: {\n /** Input placeholder when connected */\n placeholder: 'Type a message...',\n /** Input placeholder when connecting */\n connectingPlaceholder: 'Connecting...',\n /** Send button text */\n send: 'Send',\n /** Loading indicator text */\n thinking: 'Thinking',\n },\n\n // File upload\n fileUpload: {\n /** Attach files button tooltip */\n attachFiles: 'Attach Files',\n /** Drop zone text when dragging files */\n dropFilesHere: 'Drop files here',\n /** File size error (use {filename} and {maxSize} placeholders) */\n fileSizeError: 'File \"{filename}\" exceeds {maxSize}MB limit',\n /** File type error (use {type} placeholder) */\n fileTypeError: 'File type \"{type}\" is not accepted',\n },\n\n // Floating button\n floatingButton: {\n /** Floating button title when connected */\n openAssistant: 'Open AI Assistant',\n /** Floating button title when connecting */\n connectingToAssistant: 'Connecting to AI...',\n },\n\n // Slash commands\n commands: {\n /** No saved commands empty state */\n noSavedCommands: 'No saved commands yet',\n /** No matching commands message */\n noMatchingCommands: 'No matching commands',\n /** Delete command button tooltip */\n deleteCommand: 'Delete command',\n /** Command name input placeholder */\n commandNamePlaceholder: 'command-name',\n /** Save command button tooltip */\n saveCommand: 'Save command',\n /** Error when command name already exists */\n commandNameExists: 'Command name already exists',\n /** Error when rename is not supported */\n renameNotSupported: 'Rename not supported',\n /** Error when save is not supported */\n saveNotSupported: 'Save not supported',\n /** Error when rename fails */\n renameFailed: 'Failed to rename',\n /** Error when save fails */\n saveFailed: 'Failed to save',\n },\n\n // Error messages (from server error codes)\n errors: {\n /** Error when AI service is overloaded */\n API_OVERLOADED: 'The AI service is currently experiencing high demand. Please try again in a moment.',\n /** Error when rate limited */\n RATE_LIMITED: 'Too many requests. Please wait a moment before trying again.',\n /** Error for unknown/unexpected errors */\n UNKNOWN_ERROR: 'An unexpected error occurred. Please try again.',\n },\n};\n\n/**\n * Customizable text labels for the chat UI.\n */\nexport type UseAIStrings = typeof defaultStrings;\n\nexport const StringsContext = createContext<UseAIStrings>(defaultStrings);\n\n/**\n * Hook to access the current strings.\n * Returns the strings from UseAIProvider, or defaults if not inside a provider.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const strings = useStrings();\n * return <button>{strings.input.send}</button>;\n * }\n * ```\n */\nexport function useStrings(): UseAIStrings {\n return useContext(StringsContext);\n}\n","import { createContext, useContext } from \"react\";\n\n/**\n * Default theme configuration for the chat UI.\n * All colors support CSS color values (hex, rgb, hsl, gradients, etc.)\n */\nexport const defaultTheme = {\n // Primary colors\n /** Primary color for buttons, links, active states */\n primaryColor: '#667eea',\n /** Primary gradient for user messages and buttons */\n primaryGradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',\n /** Translucent primary color for overlays (e.g., drop zone) */\n primaryColorTranslucent: 'rgba(102, 126, 234, 0.15)',\n\n // Backgrounds\n /** Panel background color */\n backgroundColor: 'white',\n /** Assistant message bubble background */\n assistantMessageBackground: '#f3f4f6',\n /** Hover background for buttons and items */\n hoverBackground: '#f3f4f6',\n /** Active/selected item background */\n activeBackground: '#f0f0ff',\n /** Disabled button background */\n buttonDisabledBackground: '#e5e7eb',\n\n // Text colors\n /** Primary text color */\n textColor: '#1f2937',\n /** Secondary/muted text color */\n secondaryTextColor: '#6b7280',\n /** Placeholder text color */\n placeholderTextColor: '#9ca3af',\n\n // Status colors\n /** Online status indicator color */\n onlineColor: '#10b981',\n /** Offline status indicator color */\n offlineColor: '#6b7280',\n /** Unread notification indicator color */\n unreadIndicatorColor: '#ff4444',\n\n // Error/danger colors\n /** Error message background */\n errorBackground: '#fee2e2',\n /** Error message text color */\n errorTextColor: '#dc2626',\n /** Danger/destructive action color (e.g., delete) */\n dangerColor: '#ef4444',\n\n // Borders and dividers\n /** Border color for dividers and inputs */\n borderColor: '#e5e7eb',\n /** Dashed border color (e.g., file placeholder) */\n dashedBorderColor: '#d1d5db',\n\n // Shadows\n /** Panel box shadow */\n panelShadow: '0 8px 32px rgba(0, 0, 0, 0.12)',\n /** Dropdown box shadow */\n dropdownShadow: '0 4px 16px rgba(0, 0, 0, 0.15)',\n /** Button box shadow */\n buttonShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n /** Button hover box shadow */\n buttonHoverShadow: '0 6px 16px rgba(0, 0, 0, 0.2)',\n\n // Typography\n /** Font family */\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n\n // Backdrop\n /** Modal backdrop color */\n backdropColor: 'rgba(0, 0, 0, 0.3)',\n};\n\n/**\n * Theme configuration for the chat UI.\n */\nexport type UseAITheme = typeof defaultTheme;\n\nexport const ThemeContext = createContext<UseAITheme>(defaultTheme);\n\n/**\n * Hook to access the current theme.\n * Returns the theme from UseAIProvider, or defaults if not inside a provider.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const theme = useTheme();\n * return <div style={{ color: theme.primaryColor }}>Hello</div>;\n * }\n * ```\n */\nexport function useTheme(): UseAITheme {\n return useContext(ThemeContext);\n}\n","import React from 'react';\nimport { useTheme, useStrings } from '../theme';\n\ninterface UseAIFloatingButtonProps {\n onClick: () => void;\n connected: boolean;\n hasUnread?: boolean;\n}\n\nexport function UseAIFloatingButton({\n onClick,\n connected,\n hasUnread = false,\n}: UseAIFloatingButtonProps) {\n const strings = useStrings();\n const theme = useTheme();\n\n return (\n <button\n data-testid=\"ai-button\"\n className=\"ai-floating-button\"\n onClick={onClick}\n style={{\n position: 'fixed',\n bottom: '24px',\n right: '24px',\n width: '56px',\n height: '56px',\n borderRadius: '50%',\n border: 'none',\n background: connected ? theme.primaryGradient : theme.offlineColor,\n color: 'white',\n fontSize: '20px',\n fontWeight: 'bold',\n cursor: connected ? 'pointer' : 'not-allowed',\n boxShadow: theme.buttonShadow,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'transform 0.2s, box-shadow 0.2s',\n zIndex: 1000,\n fontFamily: theme.fontFamily,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (connected) {\n e.currentTarget.style.transform = 'scale(1.1)';\n e.currentTarget.style.boxShadow = theme.buttonHoverShadow;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.transform = 'scale(1)';\n e.currentTarget.style.boxShadow = theme.buttonShadow;\n }}\n disabled={!connected}\n title={connected ? strings.floatingButton.openAssistant : strings.floatingButton.connectingToAssistant}\n >\n AI\n {hasUnread && (\n <span\n style={{\n position: 'absolute',\n top: '4px',\n right: '4px',\n width: '12px',\n height: '12px',\n borderRadius: '50%',\n background: theme.unreadIndicatorColor,\n border: '2px solid white',\n }}\n />\n )}\n </button>\n );\n}\n","import React, { useState, useRef, useEffect } from 'react';\nimport type { Chat, PersistedMessageContent, PersistedContentPart } from '../providers/chatRepository/types';\nimport type { AgentInfo } from '../types';\nimport type { FileAttachment, FileUploadConfig } from '../fileUpload/types';\nimport { MarkdownContent } from './MarkdownContent';\nimport { FileChip, FilePlaceholder } from './FileChip';\nimport type { SavedCommand } from '../commands/types';\nimport { useSlashCommands } from '../hooks/useSlashCommands';\nimport { useFileUpload } from '../hooks/useFileUpload';\nimport { useDropdownState } from '../hooks/useDropdownState';\nimport { useTheme, useStrings } from '../theme';\nimport type { UseAIStrings, UseAITheme } from '../theme';\n\n// Re-export types for backwards compatibility\nexport type UseAIChatPanelStrings = UseAIStrings;\nexport type UseAIChatPanelTheme = UseAITheme;\n\n/**\n * Display mode for chat messages.\n */\ntype MessageDisplayMode = 'default' | 'error';\n\n/**\n * Represents a single message in the AI conversation.\n */\ninterface Message {\n /** Unique identifier for the message */\n id: string;\n /** The role of the message sender */\n role: 'user' | 'assistant';\n /** The message content - string or multimodal content */\n content: PersistedMessageContent;\n /** When the message was created */\n timestamp: Date;\n /** Display mode for styling the message bubble */\n displayMode?: MessageDisplayMode;\n}\n\n/**\n * Helper to extract text content from message content.\n */\nfunction getTextContent(content: PersistedMessageContent): string {\n if (typeof content === 'string') {\n return content;\n }\n return content\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map(part => part.text)\n .join('\\n');\n}\n\n/**\n * Helper to check if content has file attachments.\n */\nfunction hasFileContent(content: PersistedMessageContent): content is PersistedContentPart[] {\n return Array.isArray(content) && content.some(part => part.type === 'file');\n}\n\n/**\n * Props for the chat panel component.\n */\nexport interface UseAIChatPanelProps {\n onSendMessage: (message: string, attachments?: FileAttachment[]) => void;\n messages: Message[];\n loading: boolean;\n connected: boolean;\n /** Currently streaming text from assistant (real-time updates) */\n streamingText?: string;\n currentChatId?: string | null;\n onNewChat?: () => Promise<string | void>;\n onLoadChat?: (chatId: string) => Promise<void>;\n onDeleteChat?: (chatId: string) => Promise<void>;\n onListChats?: () => Promise<Array<Omit<Chat, 'messages'>>>;\n suggestions?: string[];\n availableAgents?: AgentInfo[];\n defaultAgent?: string | null;\n selectedAgent?: string | null;\n onAgentChange?: (agentId: string | null) => void;\n fileUploadConfig?: FileUploadConfig;\n commands?: SavedCommand[];\n onSaveCommand?: (name: string, text: string) => Promise<string>;\n onRenameCommand?: (id: string, newName: string) => Promise<void>;\n onDeleteCommand?: (id: string) => Promise<void>;\n /** Optional close button to render in header (for floating mode) */\n closeButton?: React.ReactNode;\n}\n\n/**\n * Chat panel content - fills its container.\n * Use directly for embedded mode, or wrap with UseAIFloatingChatWrapper for floating mode.\n */\nexport function UseAIChatPanel({\n onSendMessage,\n messages,\n loading,\n connected,\n streamingText = '',\n currentChatId,\n onNewChat,\n onLoadChat,\n onDeleteChat,\n onListChats,\n suggestions,\n availableAgents,\n defaultAgent,\n selectedAgent,\n onAgentChange,\n fileUploadConfig,\n commands = [],\n onSaveCommand,\n onRenameCommand,\n onDeleteCommand,\n closeButton,\n}: UseAIChatPanelProps) {\n const strings = useStrings();\n const theme = useTheme();\n\n const [input, setInput] = useState('');\n const chatHistoryDropdown = useDropdownState();\n const agentDropdown = useDropdownState();\n const [chatHistory, setChatHistory] = useState<Array<Omit<Chat, 'messages'>>>([]);\n const messagesEndRef = useRef<HTMLDivElement>(null);\n const [displayedSuggestions, setDisplayedSuggestions] = useState<string[]>([]);\n\n // Message hover state for save button\n const [hoveredMessageId, setHoveredMessageId] = useState<string | null>(null);\n\n // File upload hook\n const {\n attachments,\n fileError,\n enabled: fileUploadEnabled,\n acceptedTypes,\n fileInputRef,\n removeAttachment,\n clearAttachments,\n openFilePicker,\n handleFileInputChange,\n getDropZoneProps,\n DropZoneOverlay,\n } = useFileUpload({\n config: fileUploadConfig,\n disabled: loading,\n resetDependency: currentChatId,\n });\n\n // Slash commands hook\n const slashCommands = useSlashCommands({\n commands,\n onCommandSelect: (text) => setInput(text),\n onSaveCommand,\n onRenameCommand,\n onDeleteCommand,\n });\n\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [messages]);\n\n // Randomly select up to 4 suggestions when messages become empty\n useEffect(() => {\n if (!suggestions || suggestions.length === 0) {\n setDisplayedSuggestions([]);\n return;\n }\n\n // Shuffle array and take up to 4 items\n const shuffled = [...suggestions].sort(() => Math.random() - 0.5);\n setDisplayedSuggestions(shuffled.slice(0, 4));\n }, [messages.length, suggestions]);\n\n const handleSend = () => {\n // Allow sending if there's text or attachments\n const hasContent = input.trim() || attachments.length > 0;\n if (!hasContent || !connected || loading) return;\n\n onSendMessage(input, attachments.length > 0 ? attachments : undefined);\n setInput('');\n clearAttachments();\n slashCommands.closeAutocomplete();\n };\n\n // Handle input change with slash command detection\n const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n const value = e.target.value;\n setInput(value);\n slashCommands.handleInputChange(value);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n // Let slash commands hook handle keyboard navigation\n if (slashCommands.handleKeyDown(e)) {\n return;\n }\n\n // Normal send on Enter (except during IME composition, e.g: Japanese input)\n // On Safari, `isComposing` becomes false when pressing Enter to confirm IME input.\n // We use `e.keyCode` to handle this Safari-specific behavior, even though it is deprecated.\n // Reference: https://zenn.dev/spacemarket/articles/149aa284ef7b08\n if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing && !(e.keyCode === 229)) {\n e.preventDefault();\n handleSend();\n }\n };\n\n const handleNewChat = async () => {\n if (onNewChat) {\n await onNewChat();\n }\n };\n\n const handleDeleteChat = async () => {\n if (onDeleteChat && currentChatId && confirm(strings.header.deleteConfirm)) {\n await onDeleteChat(currentChatId);\n if (onNewChat) {\n await onNewChat();\n }\n }\n };\n\n const handleLoadChat = async (chatId: string) => {\n if (onLoadChat) {\n await onLoadChat(chatId);\n chatHistoryDropdown.close();\n }\n };\n\n return (\n <div\n onClick={() => {\n // Dismiss inline save command UI when clicking anywhere in the chat panel\n slashCommands.cancelInlineSave();\n }}\n {...getDropZoneProps()}\n style={{\n width: '100%',\n height: '100%',\n background: theme.backgroundColor,\n display: 'flex',\n flexDirection: 'column',\n fontFamily: theme.fontFamily,\n position: 'relative',\n }}\n >\n {/* Drop zone overlay (shows when dragging files) */}\n {DropZoneOverlay}\n\n {/* Header */}\n <div\n style={{\n padding: '12px 16px',\n borderBottom: `1px solid ${theme.borderColor}`,\n background: theme.backgroundColor,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '12px',\n }}\n >\n {/* Left side: Chat dropdown */}\n <div style={{ flex: 1, minWidth: 0, position: 'relative' }}>\n {onListChats ? (\n <button\n data-testid=\"chat-history-dropdown-button\"\n onClick={async () => {\n const chats = await onListChats();\n setChatHistory(chats);\n chatHistoryDropdown.toggle();\n }}\n style={{\n background: 'transparent',\n border: 'none',\n padding: '6px 8px',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n fontSize: '14px',\n fontWeight: '600',\n color: theme.textColor,\n borderRadius: '6px',\n transition: 'background 0.2s',\n width: '100%',\n textAlign: 'left',\n overflow: 'hidden',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = theme.hoverBackground;\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = 'transparent';\n }}\n >\n <span style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n flex: 1,\n minWidth: 0,\n }}>\n {/* Get current chat title */}\n {(() => {\n if (messages.length > 0) {\n const firstUserMsg = messages.find((m: Message) => m.role === 'user');\n if (firstUserMsg) {\n const textContent = getTextContent(firstUserMsg.content);\n const maxLength = 30;\n return textContent.length > maxLength\n ? textContent.substring(0, maxLength) + '...'\n : textContent || strings.header.newChat;\n }\n }\n return strings.header.newChat;\n })()}\n </span>\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" style={{ flexShrink: 0 }}>\n <path d=\"M3 4.5L6 7.5L9 4.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n </svg>\n </button>\n ) : (\n <div style={{ fontSize: '14px', fontWeight: '600', color: theme.textColor, padding: '6px 8px' }}>\n {strings.header.aiAssistant}\n </div>\n )}\n </div>\n\n {/* Right side: Actions */}\n <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>\n {/* Model selector */}\n {availableAgents && availableAgents.length > 1 && onAgentChange && (\n <div style={{ position: 'relative' }}>\n <button\n data-testid=\"agent-selector\"\n onClick={agentDropdown.toggle}\n style={{\n background: 'transparent',\n border: 'none',\n padding: '6px 8px',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n fontSize: '13px',\n fontWeight: '500',\n color: theme.secondaryTextColor,\n borderRadius: '6px',\n transition: 'all 0.2s',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = theme.hoverBackground;\n e.currentTarget.style.color = theme.textColor;\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = 'transparent';\n e.currentTarget.style.color = theme.secondaryTextColor;\n }}\n title=\"Select AI model\"\n >\n <span style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n maxWidth: '120px',\n }}>\n {(() => {\n const agent = availableAgents.find((a: AgentInfo) => a.id === (selectedAgent ?? defaultAgent));\n return agent?.name || 'AI';\n })()}\n </span>\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 12 12\" fill=\"none\" style={{ flexShrink: 0 }}>\n <path d=\"M3 4.5L6 7.5L9 4.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n </svg>\n </button>\n\n {/* Agent Selector Dropdown */}\n {agentDropdown.isOpen && (\n <div\n style={{\n position: 'absolute',\n top: '100%',\n left: '50%',\n transform: 'translateX(-50%)',\n marginTop: '4px',\n minWidth: '180px',\n maxWidth: '280px',\n background: theme.backgroundColor,\n borderRadius: '8px',\n boxShadow: '0 4px 16px rgba(0, 0, 0, 0.15)',\n zIndex: 1003,\n overflow: 'hidden',\n padding: '4px',\n }}\n >\n {availableAgents.map((agent: AgentInfo) => {\n const isSelected = agent.id === (selectedAgent ?? defaultAgent);\n return (\n <div\n key={agent.id}\n data-testid=\"agent-option\"\n onClick={() => {\n onAgentChange(agent.id === defaultAgent ? null : agent.id);\n agentDropdown.close();\n }}\n style={{\n padding: '8px 12px',\n background: isSelected ? theme.activeBackground : 'transparent',\n borderRadius: '6px',\n cursor: 'pointer',\n transition: 'background 0.15s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: '8px',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelected) {\n e.currentTarget.style.background = theme.hoverBackground;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {\n if (!isSelected) {\n e.currentTarget.style.background = 'transparent';\n }\n }}\n >\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{\n fontSize: '13px',\n fontWeight: isSelected ? '600' : '500',\n color: isSelected ? theme.primaryColor : theme.textColor,\n }}>\n {agent.name}\n </div>\n {agent.annotation && (\n <div style={{\n fontSize: '11px',\n color: theme.secondaryTextColor,\n marginTop: '2px',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}>\n {agent.annotation}\n </div>\n )}\n </div>\n {isSelected && (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" style={{ flexShrink: 0 }}>\n <path d=\"M2 7L5.5 10.5L12 4\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n </svg>\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n )}\n\n {/* New Chat button */}\n {onNewChat && (\n <button\n data-testid=\"new-chat-button\"\n onClick={handleNewChat}\n style={{\n background: 'transparent',\n border: 'none',\n borderRadius: '6px',\n padding: '6px 8px',\n color: theme.secondaryTextColor,\n fontSize: '13px',\n cursor: 'pointer',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = theme.hoverBackground;\n e.currentTarget.style.color = theme.textColor;\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = 'transparent';\n e.currentTarget.style.color = theme.secondaryTextColor;\n }}\n title={strings.header.newChat}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M8 3.5V12.5M3.5 8H12.5\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\"/>\n </svg>\n </button>\n )}\n\n {/* Delete button */}\n {onDeleteChat && messages.length > 0 && (\n <button\n data-testid=\"delete-chat-button\"\n onClick={handleDeleteChat}\n style={{\n background: 'transparent',\n border: 'none',\n borderRadius: '6px',\n padding: '6px 8px',\n color: theme.secondaryTextColor,\n fontSize: '13px',\n cursor: 'pointer',\n transition: 'all 0.2s',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = theme.hoverBackground;\n e.currentTarget.style.color = theme.textColor;\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = 'transparent';\n e.currentTarget.style.color = theme.secondaryTextColor;\n }}\n title={strings.header.deleteChat}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M2 4H14M6.5 7V11M9.5 7V11M3 4L4 13C4 13.5304 4.21071 14.0391 4.58579 14.4142C4.96086 14.7893 5.46957 15 6 15H10C10.5304 15 11.0391 14.7893 11.4142 14.4142C11.7893 14.0391 12 13.5304 12 13L13 4M5.5 4V2.5C5.5 2.23478 5.60536 1.98043 5.79289 1.79289C5.98043 1.60536 6.23478 1.5 6.5 1.5H9.5C9.76522 1.5 10.0196 1.60536 10.2071 1.79289C10.3946 1.98043 10.5 2.23478 10.5 2.5V4\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n </svg>\n </button>\n )}\n\n {/* Optional close button (passed in for floating mode) */}\n {closeButton}\n </div>\n </div>\n\n {/* Chat History Dropdown */}\n {chatHistoryDropdown.isOpen && onListChats && (\n <div\n style={{\n position: 'absolute',\n top: '60px',\n left: '16px',\n width: '320px',\n maxHeight: '400px',\n background: theme.backgroundColor,\n borderRadius: '8px',\n boxShadow: theme.panelShadow,\n zIndex: 1003,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n >\n {/* Chat List */}\n <div\n style={{\n flex: 1,\n overflowY: 'auto',\n padding: '8px',\n }}\n >\n {chatHistory.length === 0 ? (\n <div\n style={{\n textAlign: 'center',\n color: theme.secondaryTextColor,\n padding: '32px 16px',\n fontSize: '13px',\n }}\n >\n <p style={{ margin: 0 }}>{strings.chatHistory.noChatHistory}</p>\n </div>\n ) : (\n chatHistory.map((chat) => (\n <div\n key={chat.id}\n data-testid=\"chat-history-item\"\n onClick={() => handleLoadChat(chat.id)}\n style={{\n padding: '10px 12px',\n marginBottom: '4px',\n background: currentChatId === chat.id ? theme.activeBackground : 'transparent',\n borderRadius: '6px',\n cursor: 'pointer',\n transition: 'background 0.15s',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLDivElement>) => {\n if (currentChatId !== chat.id) {\n e.currentTarget.style.background = theme.hoverBackground;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLDivElement>) => {\n if (currentChatId !== chat.id) {\n e.currentTarget.style.background = 'transparent';\n }\n }}\n >\n <div style={{ fontSize: '13px', fontWeight: '500', color: theme.textColor, marginBottom: '4px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>\n {chat.title || strings.header.newChat}\n </div>\n <div style={{ fontSize: '11px', color: theme.secondaryTextColor }}>\n {new Date(chat.updatedAt).toLocaleDateString([], { month: 'short', day: 'numeric' })}\n {currentChatId === chat.id && (\n <span style={{\n marginLeft: '8px',\n color: theme.primaryColor,\n fontWeight: '600',\n }}>\n • {strings.chatHistory.active}\n </span>\n )}\n </div>\n </div>\n ))\n )}\n </div>\n </div>\n )}\n\n {/* Backdrops to close dropdowns */}\n {chatHistoryDropdown.Backdrop}\n {agentDropdown.Backdrop}\n\n {/* Messages */}\n <div\n style={{\n flex: 1,\n overflowY: 'auto',\n padding: '16px',\n display: 'flex',\n flexDirection: 'column',\n gap: '12px',\n }}\n >\n {messages.length === 0 && (\n <div\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n padding: '40px 20px',\n gap: '20px',\n }}\n >\n <div style={{ textAlign: 'center', color: theme.secondaryTextColor, fontSize: '14px' }}>\n <p style={{ margin: 0, fontSize: '32px', marginBottom: '12px' }}>💬</p>\n <p style={{ margin: 0 }}>{strings.emptyChat.startConversation}</p>\n <p style={{ margin: '8px 0 0', fontSize: '12px' }}>\n {strings.emptyChat.askMeToHelp}\n </p>\n </div>\n\n {/* Suggestions */}\n {displayedSuggestions.length > 0 && (\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(2, 1fr)',\n gap: '8px',\n width: '100%',\n maxWidth: '320px',\n }}\n >\n {displayedSuggestions.map((suggestion, index) => (\n <button\n key={index}\n data-testid=\"chat-suggestion-button\"\n onClick={() => {\n if (connected && !loading) {\n onSendMessage(suggestion);\n }\n }}\n disabled={!connected || loading}\n style={{\n padding: '10px 14px',\n background: theme.backgroundColor,\n border: `1px solid ${theme.borderColor}`,\n borderRadius: '8px',\n fontSize: '13px',\n color: theme.textColor,\n cursor: connected && !loading ? 'pointer' : 'not-allowed',\n textAlign: 'left',\n transition: 'all 0.2s',\n lineHeight: '1.4',\n opacity: connected && !loading ? 1 : 0.5,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (connected && !loading) {\n e.currentTarget.style.background = theme.hoverBackground;\n e.currentTarget.style.transform = 'translateY(-1px)';\n e.currentTarget.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.08)';\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = theme.backgroundColor;\n e.currentTarget.style.transform = 'translateY(0)';\n e.currentTarget.style.boxShadow = 'none';\n }}\n >\n {suggestion}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n\n {messages.map((message: Message) => (\n <div\n key={message.id}\n data-testid={`chat-message-${message.role}`}\n className={`chat-message chat-message-${message.role}`}\n style={{\n display: 'flex',\n flexDirection: 'column',\n alignItems: message.role === 'user' ? 'flex-end' : 'flex-start',\n }}\n onMouseEnter={() => message.role === 'user' && setHoveredMessageId(message.id)}\n onMouseLeave={() => setHoveredMessageId(null)}\n >\n <div\n style={{\n position: 'relative',\n maxWidth: '80%',\n }}\n >\n {/* Save as command button - appears on hover for user messages */}\n {message.role === 'user' && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && (\n <button\n data-testid=\"save-command-button\"\n onClick={(e) => {\n e.stopPropagation();\n const messageText = getTextContent(message.content);\n slashCommands.startSavingCommand(message.id, messageText);\n }}\n title=\"Save as slash command\"\n style={{\n position: 'absolute',\n top: '-8px',\n right: '-8px',\n width: '24px',\n height: '24px',\n borderRadius: '50%',\n border: 'none',\n background: theme.backgroundColor,\n boxShadow: '0 2px 6px rgba(0, 0, 0, 0.15)',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: theme.primaryColor,\n transition: 'all 0.15s',\n zIndex: 10,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.transform = 'scale(1.1)';\n e.currentTarget.style.boxShadow = '0 3px 8px rgba(0, 0, 0, 0.2)';\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.transform = 'scale(1)';\n e.currentTarget.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.15)';\n }}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\" />\n <polyline points=\"17 21 17 13 7 13 7 21\" />\n <polyline points=\"7 3 7 8 15 8\" />\n </svg>\n </button>\n )}\n <div\n data-testid=\"chat-message-content\"\n className={`chat-message-content${message.role === 'assistant' ? ' markdown-content' : ''}`}\n style={{\n padding: '10px 14px',\n borderRadius: slashCommands.isSavingCommand(message.id)\n ? '12px 12px 0 0'\n : '12px',\n background: message.displayMode === 'error'\n ? theme.errorBackground\n : message.role === 'user'\n ? theme.primaryGradient\n : theme.assistantMessageBackground,\n color: message.displayMode === 'error'\n ? theme.errorTextColor\n : message.role === 'user' ? 'white' : theme.textColor,\n fontSize: '14px',\n lineHeight: '1.5',\n wordWrap: 'break-word',\n }}\n >\n {/* Render file placeholders for user messages with files */}\n {message.role === 'user' && hasFileContent(message.content) && (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px', marginBottom: '8px' }}>\n {message.content\n .filter((part: PersistedContentPart): part is { type: 'file'; file: { name: string; size: number; mimeType: string } } => part.type === 'file')\n .map((part: { type: 'file'; file: { name: string; size: number; mimeType: string } }, idx: number) => (\n <FilePlaceholder\n key={idx}\n name={part.file.name}\n size={part.file.size}\n />\n ))}\n </div>\n )}\n {message.role === 'assistant' ? (\n <MarkdownContent content={getTextContent(message.content)} />\n ) : (\n getTextContent(message.content)\n )}\n </div>\n {/* Inline save command UI - glued to chat bubble */}\n {slashCommands.renderInlineSaveUI({\n messageId: message.id,\n messageText: getTextContent(message.content),\n })}\n </div>\n <div\n style={{\n fontSize: '11px',\n color: theme.secondaryTextColor,\n marginTop: '4px',\n padding: '0 4px',\n }}\n >\n {message.timestamp.toLocaleTimeString([], {\n hour: '2-digit',\n minute: '2-digit'\n })}\n </div>\n </div>\n ))}\n\n {loading && (\n <div\n style={{\n display: 'flex',\n alignItems: 'flex-start',\n }}\n >\n <div\n className=\"markdown-content\"\n style={{\n padding: '10px 14px',\n borderRadius: '12px',\n background: theme.assistantMessageBackground,\n fontSize: '14px',\n lineHeight: '1.5',\n color: theme.textColor,\n maxWidth: '80%',\n }}\n >\n {streamingText ? (\n <MarkdownContent content={streamingText} />\n ) : (\n <>\n <span style={{ opacity: 0.6 }}>{strings.input.thinking}</span>\n <span className=\"dots\" style={{ marginLeft: '4px' }}>...</span>\n </>\n )}\n </div>\n </div>\n )}\n\n <div ref={messagesEndRef} />\n </div>\n\n {/* Input */}\n <div\n style={{\n padding: '16px',\n borderTop: `1px solid ${theme.borderColor}`,\n }}\n >\n {/* File error message */}\n {fileError && (\n <div\n data-testid=\"file-error\"\n style={{\n marginBottom: '8px',\n padding: '8px 12px',\n background: theme.errorBackground,\n color: theme.errorTextColor,\n borderRadius: '6px',\n fontSize: '13px',\n }}\n >\n {fileError}\n </div>\n )}\n\n {/* File chips */}\n {attachments.length > 0 && (\n <div\n data-testid=\"file-attachments\"\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: '8px',\n marginBottom: '8px',\n }}\n >\n {attachments.map((attachment) => (\n <FileChip\n key={attachment.id}\n attachment={attachment}\n onRemove={() => removeAttachment(attachment.id)}\n disabled={loading}\n />\n ))}\n </div>\n )}\n\n <div\n style={{\n display: 'flex',\n gap: '8px',\n position: 'relative',\n }}\n >\n {/* Command Autocomplete */}\n {slashCommands.AutocompleteComponent}\n\n {/* Hidden file input */}\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n style={{ display: 'none' }}\n onChange={handleFileInputChange}\n accept={acceptedTypes?.join(',')}\n />\n\n {/* Input container with textarea and file button */}\n <div\n style={{\n flex: 1,\n display: 'flex',\n alignItems: 'center',\n border: `1px solid ${theme.borderColor}`,\n borderRadius: '8px',\n background: theme.backgroundColor,\n overflow: 'hidden',\n }}\n >\n <textarea\n data-testid=\"chat-input\"\n className=\"chat-input\"\n value={input}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n placeholder={connected ? strings.input.placeholder : strings.input.connectingPlaceholder}\n disabled={!connected || loading}\n style={{\n flex: 1,\n padding: '10px 12px',\n border: 'none',\n fontSize: '14px',\n resize: 'none',\n minHeight: '44px',\n maxHeight: '120px',\n fontFamily: 'inherit',\n outline: 'none',\n background: 'transparent',\n }}\n rows={1}\n />\n\n {/* File picker button inside input */}\n {fileUploadEnabled && (\n <button\n data-testid=\"file-picker-button\"\n onClick={openFilePicker}\n disabled={!connected || loading}\n style={{\n padding: '6px',\n marginRight: '6px',\n background: 'transparent',\n border: 'none',\n borderRadius: '6px',\n cursor: connected && !loading ? 'pointer' : 'not-allowed',\n color: theme.secondaryTextColor,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'all 0.15s',\n opacity: connected && !loading ? 1 : 0.5,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (connected && !loading) {\n e.currentTarget.style.color = theme.primaryColor;\n e.currentTarget.style.background = theme.activeBackground;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.color = theme.secondaryTextColor;\n e.currentTarget.style.background = 'transparent';\n }}\n title={strings.fileUpload.attachFiles}\n >\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"16\" />\n <line x1=\"8\" y1=\"12\" x2=\"16\" y2=\"12\" />\n </svg>\n </button>\n )}\n </div>\n\n <button\n data-testid=\"chat-send-button\"\n className=\"chat-send-button\"\n onClick={handleSend}\n disabled={!connected || loading || (!input.trim() && attachments.length === 0)}\n style={{\n padding: '10px 16px',\n background: connected && !loading && (input.trim() || attachments.length > 0)\n ? theme.primaryGradient\n : theme.buttonDisabledBackground,\n color: connected && !loading && (input.trim() || attachments.length > 0) ? 'white' : theme.secondaryTextColor,\n border: 'none',\n borderRadius: '8px',\n cursor: connected && !loading && (input.trim() || attachments.length > 0) ? 'pointer' : 'not-allowed',\n fontSize: '14px',\n fontWeight: '600',\n minWidth: '60px',\n transition: 'all 0.2s',\n }}\n >\n {strings.input.send}\n </button>\n </div>\n </div>\n\n <style>{`\n /* Markdown content styles */\n .markdown-content > :first-child {\n margin-top: 0 !important;\n }\n .markdown-content > :last-child {\n margin-bottom: 0 !important;\n }\n .markdown-content p:last-child {\n margin-bottom: 0 !important;\n }\n .markdown-content ul:last-child,\n .markdown-content ol:last-child {\n margin-bottom: 0 !important;\n }\n .markdown-content pre:last-child {\n margin-bottom: 0 !important;\n }\n `}</style>\n </div>\n );\n}\n\nexport type { Message };\n","import React from 'react';\nimport ReactMarkdown from 'react-markdown';\n\ninterface MarkdownContentProps {\n content: string;\n}\n\n/**\n * Renders markdown content with appropriate styling for the chat panel.\n */\nexport function MarkdownContent({ content }: MarkdownContentProps) {\n return (\n <ReactMarkdown\n components={{\n // Override default element rendering for better chat styling\n p: ({ children }) => <p style={{ margin: '0 0 0.5em 0' }}>{children}</p>,\n // Ensure last paragraph has no margin\n h1: ({ children }) => <h1 style={{ margin: '0 0 0.5em 0', fontSize: '1.25em', fontWeight: 600 }}>{children}</h1>,\n h2: ({ children }) => <h2 style={{ margin: '0 0 0.5em 0', fontSize: '1.15em', fontWeight: 600 }}>{children}</h2>,\n h3: ({ children }) => <h3 style={{ margin: '0 0 0.5em 0', fontSize: '1.05em', fontWeight: 600 }}>{children}</h3>,\n ul: ({ children }) => <ul style={{ margin: '0 0 0.5em 0', paddingLeft: '1.5em' }}>{children}</ul>,\n ol: ({ children }) => <ol style={{ margin: '0 0 0.5em 0', paddingLeft: '1.5em' }}>{children}</ol>,\n li: ({ children }) => <li style={{ marginBottom: '0.25em' }}>{children}</li>,\n code: ({ className, children, ...props }) => {\n // Check if this is inline code or a code block\n const isInline = !className;\n if (isInline) {\n return (\n <code\n style={{\n backgroundColor: 'rgba(0, 0, 0, 0.1)',\n padding: '0.1em 0.3em',\n borderRadius: '3px',\n fontSize: '0.9em',\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Consolas, monospace',\n }}\n {...props}\n >\n {children}\n </code>\n );\n }\n return (\n <code className={className} {...props}>\n {children}\n </code>\n );\n },\n pre: ({ children }) => (\n <pre\n style={{\n margin: '0.5em 0',\n padding: '0.75em',\n backgroundColor: 'rgba(0, 0, 0, 0.1)',\n borderRadius: '6px',\n overflow: 'auto',\n fontSize: '0.85em',\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Consolas, monospace',\n }}\n >\n {children}\n </pre>\n ),\n blockquote: ({ children }) => (\n <blockquote\n style={{\n margin: '0.5em 0',\n paddingLeft: '1em',\n borderLeft: '3px solid rgba(0, 0, 0, 0.2)',\n color: 'inherit',\n opacity: 0.9,\n }}\n >\n {children}\n </blockquote>\n ),\n a: ({ children, href }) => (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{\n color: 'inherit',\n textDecoration: 'underline',\n textUnderlineOffset: '2px',\n }}\n >\n {children}\n </a>\n ),\n hr: () => (\n <hr\n style={{\n margin: '0.75em 0',\n border: 'none',\n borderTop: '1px solid rgba(0, 0, 0, 0.2)',\n }}\n />\n ),\n table: ({ children }) => (\n <div style={{ overflowX: 'auto', margin: '0.5em 0' }}>\n <table\n style={{\n borderCollapse: 'collapse',\n fontSize: '0.9em',\n width: '100%',\n }}\n >\n {children}\n </table>\n </div>\n ),\n th: ({ children }) => (\n <th\n style={{\n padding: '0.4em 0.6em',\n borderBottom: '2px solid rgba(0, 0, 0, 0.2)',\n textAlign: 'left',\n fontWeight: 600,\n }}\n >\n {children}\n </th>\n ),\n td: ({ children }) => (\n <td\n style={{\n padding: '0.4em 0.6em',\n borderBottom: '1px solid rgba(0, 0, 0, 0.1)',\n }}\n >\n {children}\n </td>\n ),\n }}\n >\n {content}\n </ReactMarkdown>\n );\n}\n","import React from 'react';\nimport type { FileAttachment } from '../fileUpload/types';\nimport { useTheme } from '../theme';\n\n/**\n * Props for the FileChip component.\n */\nexport interface FileChipProps {\n /** The file attachment to display */\n attachment: FileAttachment;\n /** Callback when the remove button is clicked */\n onRemove: () => void;\n /** Whether the chip is disabled (e.g., during sending) */\n disabled?: boolean;\n}\n\n/**\n * Formats file size in human-readable format.\n */\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * Truncates filename if too long, preserving the extension.\n */\nfunction truncateFilename(name: string, maxLength: number = 20): string {\n if (name.length <= maxLength) return name;\n\n const lastDot = name.lastIndexOf('.');\n const ext = lastDot > 0 ? name.substring(lastDot) : '';\n const baseName = lastDot > 0 ? name.substring(0, lastDot) : name;\n\n const maxBaseLength = maxLength - ext.length - 3; // 3 for \"...\"\n if (maxBaseLength < 5) return name.substring(0, maxLength - 3) + '...';\n\n return baseName.substring(0, maxBaseLength) + '...' + ext;\n}\n\n/**\n * A chip component that displays a file attachment with preview and remove button.\n *\n * Features:\n * - Shows image preview for image files\n * - Displays file icon for non-image files\n * - Shows truncated filename and file size\n * - Has a remove button (×)\n */\nexport function FileChip({ attachment, onRemove, disabled }: FileChipProps) {\n const theme = useTheme();\n const { file, preview } = attachment;\n const isImage = file.type.startsWith('image/');\n\n return (\n <div\n data-testid=\"file-chip\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: '8px',\n padding: '6px 10px',\n background: theme.hoverBackground,\n borderRadius: '8px',\n fontSize: '13px',\n color: theme.textColor,\n maxWidth: '200px',\n }}\n >\n {/* Preview or icon */}\n {isImage && preview ? (\n <img\n src={preview}\n alt={file.name}\n style={{\n width: '24px',\n height: '24px',\n borderRadius: '4px',\n objectFit: 'cover',\n }}\n />\n ) : (\n <div\n style={{\n width: '24px',\n height: '24px',\n borderRadius: '4px',\n background: theme.borderColor,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '12px',\n }}\n >\n 📎\n </div>\n )}\n\n {/* File info */}\n <div style={{ flex: 1, minWidth: 0, overflow: 'hidden' }}>\n <div\n style={{\n fontWeight: 500,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n title={file.name}\n >\n {truncateFilename(file.name)}\n </div>\n <div style={{ fontSize: '11px', color: theme.secondaryTextColor }}>\n {formatFileSize(file.size)}\n </div>\n </div>\n\n {/* Remove button */}\n <button\n data-testid=\"file-chip-remove\"\n onClick={onRemove}\n disabled={disabled}\n style={{\n background: 'transparent',\n border: 'none',\n padding: '2px 4px',\n cursor: disabled ? 'not-allowed' : 'pointer',\n color: theme.placeholderTextColor,\n fontSize: '16px',\n lineHeight: 1,\n borderRadius: '4px',\n transition: 'all 0.15s',\n opacity: disabled ? 0.5 : 1,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (!disabled) {\n e.currentTarget.style.background = theme.borderColor;\n e.currentTarget.style.color = theme.textColor;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = 'transparent';\n e.currentTarget.style.color = theme.placeholderTextColor;\n }}\n >\n ×\n </button>\n </div>\n );\n}\n\n/**\n * Props for the FilePlaceholder component.\n */\nexport interface FilePlaceholderProps {\n /** File name to display */\n name: string;\n /** File size in bytes */\n size: number;\n}\n\n/**\n * A placeholder component shown for files that are no longer available\n * (e.g., when loading a persisted message with file references).\n */\nexport function FilePlaceholder({ name, size }: FilePlaceholderProps) {\n const theme = useTheme();\n\n return (\n <div\n data-testid=\"file-placeholder\"\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: '8px',\n padding: '6px 10px',\n background: theme.backgroundColor,\n border: `1px dashed ${theme.dashedBorderColor}`,\n borderRadius: '8px',\n fontSize: '13px',\n color: theme.placeholderTextColor,\n maxWidth: '200px',\n }}\n >\n <span>📎</span>\n <div style={{ flex: 1, minWidth: 0, overflow: 'hidden' }}>\n <div\n style={{\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n title={name}\n >\n {truncateFilename(name)}\n </div>\n <div style={{ fontSize: '11px' }}>\n {formatFileSize(size)}\n </div>\n </div>\n </div>\n );\n}\n","import React, { useState, useRef, useEffect, useCallback } from 'react';\nimport type { SavedCommand } from '../commands/types';\nimport { validateCommandName } from '../commands/types';\nimport { CommandAutocomplete, getFilteredCommandsCount } from '../components/CommandAutocomplete';\nimport { useTheme, useStrings } from '../theme';\n\nconst MAX_VISIBLE_ITEMS = 8;\n\n/**\n * Options for the useSlashCommands hook.\n */\nexport interface UseSlashCommandsOptions {\n /** List of saved slash commands */\n commands: SavedCommand[];\n /** Callback when a command is selected (via click or keyboard) */\n onCommandSelect?: (text: string) => void;\n /** Callback to save a new command */\n onSaveCommand?: (name: string, text: string) => Promise<string>;\n /** Callback to rename an existing command */\n onRenameCommand?: (id: string, newName: string) => Promise<void>;\n /** Callback to delete a command */\n onDeleteCommand?: (id: string) => Promise<void>;\n}\n\n/**\n * Props for the inline save UI component.\n */\nexport interface InlineSaveProps {\n /** The message ID being saved */\n messageId: string;\n /** The text content of the message */\n messageText: string;\n}\n\n/**\n * Return value from the useSlashCommands hook.\n */\nexport interface UseSlashCommandsReturn {\n /** Whether the autocomplete dropdown is visible */\n isAutocompleteVisible: boolean;\n\n /**\n * Process input changes to detect slash command prefix.\n * Returns true if the input starts with '/' and autocomplete was triggered.\n */\n handleInputChange: (value: string) => boolean;\n\n /**\n * Process keyboard events for autocomplete navigation.\n * Returns true if the event was handled by the hook.\n * When a command is selected via Enter, onCommandSelect will be called.\n */\n handleKeyDown: (e: React.KeyboardEvent) => boolean;\n\n /**\n * Manually close the autocomplete dropdown.\n */\n closeAutocomplete: () => void;\n\n /**\n * Renders the autocomplete dropdown component.\n * Returns null if autocomplete should not be shown.\n */\n AutocompleteComponent: React.ReactNode;\n\n /**\n * Start saving a message as a slash command.\n */\n startSavingCommand: (messageId: string, messageText: string) => void;\n\n /**\n * Check if a specific message is currently being saved as a command.\n */\n isSavingCommand: (messageId: string) => boolean;\n\n /**\n * Cancel the inline save operation.\n */\n cancelInlineSave: () => void;\n\n /**\n * Renders the inline save UI component for a specific message.\n * Returns null if not saving for this message.\n */\n renderInlineSaveUI: (props: InlineSaveProps) => React.ReactNode;\n}\n\n/**\n * Composable hook for slash commands functionality.\n * Manages autocomplete state, keyboard navigation, and inline save operations.\n *\n * @example\n * ```tsx\n * const {\n * isAutocompleteVisible,\n * handleInputChange,\n * handleKeyDown,\n * AutocompleteComponent,\n * startSavingCommand,\n * isSavingCommand,\n * renderInlineSaveUI,\n * } = useSlashCommands({\n * commands,\n * onCommandSelect: (text) => setInput(text),\n * onSaveCommand,\n * onDeleteCommand,\n * });\n *\n * // In your input handler\n * const onInputChange = (e) => {\n * const value = e.target.value;\n * setInput(value);\n * handleInputChange(value);\n * };\n *\n * // In your keydown handler\n * const onKeyDown = (e) => {\n * if (handleKeyDown(e)) {\n * return; // Event was handled by slash commands\n * }\n * // Handle other key events...\n * };\n *\n * // Render\n * return (\n * <div style={{ position: 'relative' }}>\n * {AutocompleteComponent}\n * <textarea ... />\n * </div>\n * );\n * ```\n */\nexport function useSlashCommands({\n commands,\n onCommandSelect,\n onSaveCommand,\n onRenameCommand,\n onDeleteCommand,\n}: UseSlashCommandsOptions): UseSlashCommandsReturn {\n const strings = useStrings();\n const theme = useTheme();\n // Autocomplete state\n const [showAutocomplete, setShowAutocomplete] = useState(false);\n const [searchPrefix, setSearchPrefix] = useState('');\n const [highlightedIndex, setHighlightedIndex] = useState(0);\n\n // Inline save state\n const [savingMessageId, setSavingMessageId] = useState<string | null>(null);\n const [savingMessageText, setSavingMessageText] = useState<string>('');\n const [commandNameInput, setCommandNameInput] = useState('');\n const [commandSaveError, setCommandSaveError] = useState<string | null>(null);\n const commandNameInputRef = useRef<HTMLInputElement>(null);\n\n // Focus command name input when save UI appears\n useEffect(() => {\n if (savingMessageId) {\n setTimeout(() => commandNameInputRef.current?.focus(), 0);\n }\n }, [savingMessageId]);\n\n /**\n * Handle command selection.\n */\n const selectCommand = useCallback((command: SavedCommand) => {\n setShowAutocomplete(false);\n onCommandSelect?.(command.text);\n }, [onCommandSelect]);\n\n /**\n * Handle input change to detect slash command prefix.\n */\n const handleInputChange = useCallback((value: string): boolean => {\n if (value.startsWith('/') && commands.length > 0) {\n setSearchPrefix(value.slice(1));\n setShowAutocomplete(true);\n setHighlightedIndex(0);\n return true;\n } else {\n setShowAutocomplete(false);\n return false;\n }\n }, [commands.length]);\n\n /**\n * Handle keyboard navigation for autocomplete.\n */\n const handleKeyDown = useCallback((e: React.KeyboardEvent): boolean => {\n if (!showAutocomplete) {\n return false;\n }\n\n const filteredCount = getFilteredCommandsCount(commands, searchPrefix);\n\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setHighlightedIndex(i => Math.min(i + 1, filteredCount - 1));\n return true;\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n setHighlightedIndex(i => Math.max(i - 1, 0));\n return true;\n } else if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n const filteredCommands = commands\n .filter(c => c.name.toLowerCase().startsWith(searchPrefix.toLowerCase()))\n .slice(0, MAX_VISIBLE_ITEMS);\n if (filteredCommands[highlightedIndex]) {\n selectCommand(filteredCommands[highlightedIndex]);\n }\n return true;\n } else if (e.key === 'Escape') {\n e.preventDefault();\n setShowAutocomplete(false);\n return true;\n }\n\n return false;\n }, [showAutocomplete, commands, searchPrefix, highlightedIndex, selectCommand]);\n\n /**\n * Close the autocomplete dropdown.\n */\n const closeAutocomplete = useCallback(() => {\n setShowAutocomplete(false);\n }, []);\n\n /**\n * Handle delete command from autocomplete.\n */\n const handleDeleteCommand = useCallback((command: SavedCommand) => {\n if (onDeleteCommand) {\n onDeleteCommand(command.id);\n }\n }, [onDeleteCommand]);\n\n /**\n * Start saving a message as a command.\n */\n const startSavingCommand = useCallback((messageId: string, messageText: string) => {\n const existingCommand = commands.find(c => c.text === messageText);\n setSavingMessageId(messageId);\n setSavingMessageText(messageText);\n setCommandNameInput(existingCommand?.name || '');\n setCommandSaveError(null);\n }, [commands]);\n\n /**\n * Check if a message is being saved.\n */\n const isSavingCommand = useCallback((messageId: string): boolean => {\n return savingMessageId === messageId;\n }, [savingMessageId]);\n\n /**\n * Cancel inline save operation.\n */\n const cancelInlineSave = useCallback(() => {\n setSavingMessageId(null);\n setSavingMessageText('');\n setCommandNameInput('');\n setCommandSaveError(null);\n }, []);\n\n /**\n * Handle inline save command submission.\n */\n const handleInlineSaveCommand = useCallback(async () => {\n if (!savingMessageId || !savingMessageText.trim()) return;\n\n const name = commandNameInput.trim();\n\n // Validate name\n const validationError = validateCommandName(name);\n if (validationError) {\n setCommandSaveError(validationError);\n return;\n }\n\n // Check if this message text already exists as a command\n const existingCommand = commands.find(c => c.text === savingMessageText);\n\n if (existingCommand) {\n // Renaming existing command\n if (existingCommand.name === name) {\n // Name unchanged, just close\n cancelInlineSave();\n return;\n }\n\n // Check if new name conflicts with another command\n if (commands.some(c => c.name === name && c.id !== existingCommand.id)) {\n setCommandSaveError(strings.commands.commandNameExists);\n return;\n }\n\n if (!onRenameCommand) {\n setCommandSaveError(strings.commands.renameNotSupported);\n return;\n }\n\n try {\n await onRenameCommand(existingCommand.id, name);\n cancelInlineSave();\n } catch (err) {\n setCommandSaveError(err instanceof Error ? err.message : strings.commands.renameFailed);\n }\n } else {\n // Creating new command\n if (commands.some(c => c.name === name)) {\n setCommandSaveError(strings.commands.commandNameExists);\n return;\n }\n\n if (!onSaveCommand) {\n setCommandSaveError(strings.commands.saveNotSupported);\n return;\n }\n\n try {\n await onSaveCommand(name, savingMessageText);\n cancelInlineSave();\n } catch (err) {\n setCommandSaveError(err instanceof Error ? err.message : strings.commands.saveFailed);\n }\n }\n }, [savingMessageId, savingMessageText, commandNameInput, commands, onRenameCommand, onSaveCommand, cancelInlineSave, strings]);\n\n /**\n * Render the autocomplete dropdown.\n */\n const AutocompleteComponent = showAutocomplete && commands.length > 0 ? (\n <CommandAutocomplete\n commands={commands}\n searchPrefix={searchPrefix}\n highlightedIndex={highlightedIndex}\n onSelect={selectCommand}\n onDelete={onDeleteCommand ? handleDeleteCommand : undefined}\n onHighlightChange={setHighlightedIndex}\n onClose={closeAutocomplete}\n />\n ) : null;\n\n /**\n * Render the inline save UI for a specific message.\n */\n const renderInlineSaveUI = useCallback(({ messageId, messageText }: InlineSaveProps): React.ReactNode => {\n if (savingMessageId !== messageId) {\n return null;\n }\n\n return (\n <div\n data-testid=\"inline-save-command\"\n onClick={(e) => e.stopPropagation()}\n style={{\n padding: '6px 10px',\n background: theme.hoverBackground,\n borderRadius: '0 0 12px 12px',\n display: 'flex',\n flexDirection: 'column',\n gap: '4px',\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>\n <span style={{ color: theme.primaryColor, fontSize: '13px', fontWeight: 500 }}>/</span>\n <input\n ref={commandNameInputRef}\n data-testid=\"command-name-input\"\n type=\"text\"\n value={commandNameInput}\n onChange={(e) => {\n setCommandNameInput(e.target.value);\n setCommandSaveError(null);\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleInlineSaveCommand();\n } else if (e.key === 'Escape') {\n cancelInlineSave();\n }\n }}\n placeholder={strings.commands.commandNamePlaceholder}\n style={{\n flex: 1,\n border: commandSaveError ? `1px solid ${theme.dangerColor}` : `1px solid ${theme.borderColor}`,\n borderRadius: '6px',\n padding: '5px 8px',\n fontSize: '13px',\n outline: 'none',\n background: theme.backgroundColor,\n minWidth: 0,\n }}\n />\n <button\n data-testid=\"save-command-confirm\"\n onClick={handleInlineSaveCommand}\n disabled={!commandNameInput.trim()}\n style={{\n padding: '5px',\n border: 'none',\n borderRadius: '6px',\n background: 'transparent',\n color: commandNameInput.trim() ? theme.primaryColor : theme.dashedBorderColor,\n cursor: commandNameInput.trim() ? 'pointer' : 'not-allowed',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n title={strings.commands.saveCommand}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z\" />\n <polyline points=\"17 21 17 13 7 13 7 21\" />\n <polyline points=\"7 3 7 8 15 8\" />\n </svg>\n </button>\n </div>\n {commandSaveError && (\n <div\n data-testid=\"command-save-error\"\n style={{\n fontSize: '11px',\n color: theme.dangerColor,\n paddingLeft: '2px',\n }}\n >\n {commandSaveError}\n </div>\n )}\n </div>\n );\n }, [savingMessageId, commandNameInput, commandSaveError, handleInlineSaveCommand, cancelInlineSave, theme, strings]);\n\n return {\n isAutocompleteVisible: showAutocomplete,\n handleInputChange,\n handleKeyDown,\n closeAutocomplete,\n AutocompleteComponent,\n startSavingCommand,\n isSavingCommand,\n cancelInlineSave,\n renderInlineSaveUI,\n };\n}\n","/**\n * A saved slash command.\n */\nexport interface SavedCommand {\n /** Unique identifier */\n id: string;\n /** Command name (without the leading slash) */\n name: string;\n /** The saved text */\n text: string;\n /** When the command was created */\n createdAt: Date;\n /** When the command was last used */\n lastUsedAt?: Date;\n}\n\n/**\n * Options for creating a command.\n */\nexport interface CreateCommandOptions {\n name: string;\n text: string;\n}\n\n/**\n * Options for listing commands.\n */\nexport interface ListCommandsOptions {\n /** Filter commands by name prefix */\n namePrefix?: string;\n /** Limit number of results */\n limit?: number;\n}\n\n/**\n * Abstract repository interface for command persistence.\n */\nexport interface CommandRepository {\n createCommand(options: CreateCommandOptions): Promise<string>;\n loadCommand(id: string): Promise<SavedCommand | null>;\n loadCommandByName(name: string): Promise<SavedCommand | null>;\n updateCommand(command: SavedCommand): Promise<void>;\n deleteCommand(id: string): Promise<void>;\n listCommands(options?: ListCommandsOptions): Promise<SavedCommand[]>;\n deleteAll(): Promise<void>;\n}\n\n/**\n * Generates a unique command ID.\n */\nexport function generateCommandId(): string {\n return `cmd_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * Validates a command name.\n * Commands must be kebab-case: lowercase letters, numbers, and hyphens only.\n * Returns null if valid, or an error message if invalid.\n */\nexport function validateCommandName(name: string): string | null {\n if (!name.trim()) {\n return 'Command name is required';\n }\n if (!/^[a-z0-9-]+$/.test(name)) {\n return 'Only lowercase letters, numbers, and hyphens allowed (kebab-case)';\n }\n if (name.length > 50) {\n return 'Command name must be 50 characters or less';\n }\n return null;\n}\n","import React, { useEffect, useRef } from 'react';\nimport type { SavedCommand } from '../commands/types';\nimport { useTheme, useStrings } from '../theme';\n\ninterface CommandAutocompleteProps {\n commands: SavedCommand[];\n searchPrefix: string;\n highlightedIndex: number;\n onSelect: (command: SavedCommand) => void;\n onDelete?: (command: SavedCommand) => void;\n onHighlightChange: (index: number) => void;\n onClose: () => void;\n}\n\nconst MAX_VISIBLE_ITEMS = 8;\n\n/**\n * Dropdown autocomplete for slash commands.\n * Appears above the input when user types '/'.\n */\nexport function CommandAutocomplete({\n commands,\n searchPrefix,\n highlightedIndex,\n onSelect,\n onDelete,\n onHighlightChange,\n onClose,\n}: CommandAutocompleteProps) {\n const strings = useStrings();\n const theme = useTheme();\n\n const listRef = useRef<HTMLDivElement>(null);\n const itemRefs = useRef<(HTMLDivElement | null)[]>([]);\n\n // Filter commands by prefix\n const filteredCommands = commands.filter(c =>\n c.name.toLowerCase().startsWith(searchPrefix.toLowerCase())\n ).slice(0, MAX_VISIBLE_ITEMS);\n\n // Scroll highlighted item into view\n useEffect(() => {\n const item = itemRefs.current[highlightedIndex];\n if (item && listRef.current) {\n const listRect = listRef.current.getBoundingClientRect();\n const itemRect = item.getBoundingClientRect();\n\n if (itemRect.bottom > listRect.bottom) {\n item.scrollIntoView({ block: 'end' });\n } else if (itemRect.top < listRect.top) {\n item.scrollIntoView({ block: 'start' });\n }\n }\n }, [highlightedIndex]);\n\n if (filteredCommands.length === 0) {\n return (\n <div\n data-testid=\"command-autocomplete\"\n style={{\n position: 'absolute',\n bottom: '100%',\n left: 0,\n right: 0,\n marginBottom: '8px',\n background: theme.backgroundColor,\n borderRadius: '8px',\n boxShadow: theme.dropdownShadow,\n overflow: 'hidden',\n zIndex: 1005,\n }}\n >\n <div\n style={{\n padding: '12px 16px',\n color: theme.secondaryTextColor,\n fontSize: '13px',\n textAlign: 'center',\n }}\n >\n {commands.length === 0\n ? strings.commands.noSavedCommands\n : strings.commands.noMatchingCommands}\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-testid=\"command-autocomplete\"\n ref={listRef}\n style={{\n position: 'absolute',\n bottom: '100%',\n left: 0,\n right: 0,\n marginBottom: '8px',\n background: theme.backgroundColor,\n borderRadius: '8px',\n boxShadow: theme.dropdownShadow,\n overflow: 'hidden',\n maxHeight: '320px',\n overflowY: 'auto',\n zIndex: 1005,\n }}\n >\n {filteredCommands.map((cmd, index) => (\n <div\n key={cmd.id}\n ref={el => { itemRefs.current[index] = el; }}\n data-testid=\"command-autocomplete-item\"\n onClick={() => onSelect(cmd)}\n onMouseEnter={() => onHighlightChange(index)}\n style={{\n padding: '10px 14px',\n background: index === highlightedIndex ? theme.hoverBackground : 'transparent',\n cursor: 'pointer',\n borderBottom: index < filteredCommands.length - 1 ? `1px solid ${theme.hoverBackground}` : 'none',\n transition: 'background 0.1s',\n display: 'flex',\n alignItems: 'flex-start',\n gap: '8px',\n }}\n >\n <div style={{ flex: 1, minWidth: 0 }}>\n <div\n style={{\n fontWeight: 600,\n fontSize: '14px',\n color: theme.primaryColor,\n }}\n >\n /{cmd.name}\n </div>\n <div\n style={{\n marginTop: '4px',\n fontSize: '13px',\n color: theme.secondaryTextColor,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n }}\n >\n {cmd.text.length > 60 ? cmd.text.substring(0, 60) + '...' : cmd.text}\n </div>\n </div>\n {onDelete && (\n <button\n data-testid=\"command-delete-button\"\n onClick={(e) => {\n e.stopPropagation();\n onDelete(cmd);\n }}\n style={{\n padding: '4px',\n background: 'transparent',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n color: theme.placeholderTextColor,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n marginTop: '2px',\n transition: 'all 0.15s',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.color = theme.dangerColor;\n e.currentTarget.style.background = theme.errorBackground;\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.color = theme.placeholderTextColor;\n e.currentTarget.style.background = 'transparent';\n }}\n title={strings.commands.deleteCommand}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M2 4H14M6.5 7V11M9.5 7V11M3 4L4 13C4 13.5304 4.21071 14.0391 4.58579 14.4142C4.96086 14.7893 5.46957 15 6 15H10C10.5304 15 11.0391 14.7893 11.4142 14.4142C11.7893 14.0391 12 13.5304 12 13L13 4M5.5 4V2.5C5.5 2.23478 5.60536 1.98043 5.79289 1.79289C5.98043 1.60536 6.23478 1.5 6.5 1.5H9.5C9.76522 1.5 10.0196 1.60536 10.2071 1.79289C10.3946 1.98043 10.5 2.23478 10.5 2.5V4\" />\n </svg>\n </button>\n )}\n </div>\n ))}\n </div>\n );\n}\n\n/**\n * Returns the number of filtered commands for keyboard navigation bounds.\n */\nexport function getFilteredCommandsCount(\n commands: SavedCommand[],\n searchPrefix: string\n): number {\n return Math.min(\n commands.filter(c => c.name.toLowerCase().startsWith(searchPrefix.toLowerCase())).length,\n MAX_VISIBLE_ITEMS\n );\n}\n","import React, { useState, useRef, useCallback, useEffect, useMemo } from 'react';\nimport type { FileAttachment, FileUploadConfig } from '../fileUpload/types';\nimport { DEFAULT_MAX_FILE_SIZE } from '../fileUpload/types';\nimport { v4 as uuidv4 } from 'uuid';\nimport { useTheme, useStrings } from '../theme';\n\n/**\n * Props for the drop zone container element.\n */\nexport interface DropZoneProps {\n onDragEnter: (e: React.DragEvent) => void;\n onDragOver: (e: React.DragEvent) => void;\n onDragLeave: (e: React.DragEvent) => void;\n onDrop: (e: React.DragEvent) => void;\n}\n\n/**\n * Generates a preview for image files.\n */\nasync function generateImagePreview(file: File): Promise<string | undefined> {\n if (!file.type.startsWith('image/')) {\n return undefined;\n }\n\n return new Promise((resolve) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result === 'string') {\n resolve(reader.result);\n } else {\n resolve(undefined);\n }\n };\n reader.onerror = () => resolve(undefined);\n reader.readAsDataURL(file);\n });\n}\n\n/**\n * Checks if a MIME type matches accepted types.\n */\nfunction isTypeAccepted(mimeType: string, acceptedTypes?: string[]): boolean {\n if (!acceptedTypes || acceptedTypes.length === 0) {\n return true;\n }\n\n return acceptedTypes.some(pattern => {\n if (pattern.endsWith('/*')) {\n // Wildcard pattern like 'image/*'\n const prefix = pattern.slice(0, -1);\n return mimeType.startsWith(prefix);\n }\n return mimeType === pattern;\n });\n}\n\nexport interface UseFileUploadOptions {\n /** Configuration for file uploads. If undefined, file upload is disabled. */\n config?: FileUploadConfig;\n /** Whether file operations should be disabled (e.g., during loading) */\n disabled?: boolean;\n /** Dependency that resets attachments when changed (e.g., currentChatId) */\n resetDependency?: unknown;\n}\n\nexport interface UseFileUploadReturn {\n /** Current file attachments */\n attachments: FileAttachment[];\n /** Whether a drag operation is in progress over the drop zone */\n isDragging: boolean;\n /** Current file error message, if any */\n fileError: string | null;\n /** Whether file upload is enabled */\n enabled: boolean;\n /** Maximum file size in bytes */\n maxFileSize: number;\n /** Accepted MIME types */\n acceptedTypes?: string[];\n /** Ref to attach to hidden file input */\n fileInputRef: React.MutableRefObject<HTMLInputElement | null>;\n /** Validates and adds files to attachments */\n handleFiles: (files: FileList | File[]) => Promise<void>;\n /** Removes a file attachment by ID */\n removeAttachment: (id: string) => void;\n /** Clears all attachments */\n clearAttachments: () => void;\n /** Opens the file picker dialog */\n openFilePicker: () => void;\n /** Handler for file input change event */\n handleFileInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n /** Handler for dragenter event (use with dragover/dragleave/drop) */\n handleDragEnter: (e: React.DragEvent) => void;\n /** Handler for dragover event */\n handleDragOver: (e: React.DragEvent) => void;\n /** Handler for dragleave event */\n handleDragLeave: (e: React.DragEvent) => void;\n /** Handler for drop event */\n handleDrop: (e: React.DragEvent) => void;\n /** Props to spread on the drop zone container element */\n getDropZoneProps: () => DropZoneProps;\n /** Overlay component to render inside the drop zone container (shows when dragging) */\n DropZoneOverlay: React.ReactNode;\n}\n\n/**\n * Hook for managing file uploads with drag-and-drop support.\n *\n * Features:\n * - File validation (size, type)\n * - Image preview generation\n * - Drag and drop handling\n * - Auto-clearing error messages\n * - Reset on dependency change (e.g., chat switch)\n *\n * @example\n * ```typescript\n * const {\n * attachments,\n * isDragging,\n * fileError,\n * fileInputRef,\n * handleDragOver,\n * handleDragLeave,\n * handleDrop,\n * openFilePicker,\n * handleFileInputChange,\n * removeAttachment,\n * } = useFileUpload({\n * config: fileUploadConfig,\n * disabled: loading,\n * resetDependency: currentChatId,\n * });\n * ```\n */\nexport function useFileUpload({\n config,\n disabled = false,\n resetDependency,\n}: UseFileUploadOptions): UseFileUploadReturn {\n const strings = useStrings();\n const theme = useTheme();\n\n const [attachments, setAttachments] = useState<FileAttachment[]>([]);\n const [isDragging, setIsDragging] = useState(false);\n const [fileError, setFileError] = useState<string | null>(null);\n const fileInputRef = useRef<HTMLInputElement | null>(null);\n // Counter to track nested drag enter/leave events (prevents flickering)\n const dragCounterRef = useRef(0);\n\n const enabled = config !== undefined;\n const maxFileSize = config?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;\n const acceptedTypes = config?.acceptedTypes;\n\n // Clear file error after 3 seconds\n useEffect(() => {\n if (fileError) {\n const timer = setTimeout(() => setFileError(null), 3000);\n return () => clearTimeout(timer);\n }\n }, [fileError]);\n\n // Clear attachments when resetDependency changes\n useEffect(() => {\n setAttachments([]);\n setFileError(null);\n }, [resetDependency]);\n\n /**\n * Validates and adds files to attachments.\n */\n const handleFiles = useCallback(async (files: FileList | File[]) => {\n const fileArray = Array.from(files);\n\n for (const file of fileArray) {\n // Check file size\n if (file.size > maxFileSize) {\n const errorMsg = strings.fileUpload.fileSizeError\n .replace('{filename}', file.name)\n .replace('{maxSize}', String(Math.round(maxFileSize / (1024 * 1024))));\n setFileError(errorMsg);\n continue;\n }\n\n // Check file type\n if (!isTypeAccepted(file.type, acceptedTypes)) {\n const errorMsg = strings.fileUpload.fileTypeError.replace('{type}', file.type);\n setFileError(errorMsg);\n continue;\n }\n\n // Generate preview for images\n const preview = await generateImagePreview(file);\n\n // Add to attachments\n setAttachments(prev => [\n ...prev,\n {\n id: uuidv4(),\n file,\n preview,\n },\n ]);\n }\n }, [maxFileSize, acceptedTypes, strings]);\n\n /**\n * Removes a file attachment by ID.\n */\n const removeAttachment = useCallback((id: string) => {\n setAttachments(prev => prev.filter(a => a.id !== id));\n }, []);\n\n /**\n * Clears all attachments.\n */\n const clearAttachments = useCallback(() => {\n setAttachments([]);\n }, []);\n\n /**\n * Opens the file picker dialog.\n */\n const openFilePicker = useCallback(() => {\n fileInputRef.current?.click();\n }, []);\n\n /**\n * Handles file input change event.\n */\n const handleFileInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {\n const files = e.target.files;\n if (files && files.length > 0) {\n handleFiles(files);\n }\n // Reset input so the same file can be selected again\n e.target.value = '';\n }, [handleFiles]);\n\n // Drag and drop handlers using counter to prevent flickering\n // The counter approach: increment on dragenter, decrement on dragleave\n // Only show drop zone when counter > 0, reset to 0 on drop\n const handleDragEnter = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (enabled && !disabled) {\n dragCounterRef.current++;\n if (dragCounterRef.current === 1) {\n setIsDragging(true);\n }\n }\n }, [enabled, disabled]);\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n }, []);\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current--;\n if (dragCounterRef.current === 0) {\n setIsDragging(false);\n }\n }, []);\n\n const handleDrop = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current = 0;\n setIsDragging(false);\n\n if (!enabled || disabled) return;\n\n const files = e.dataTransfer.files;\n if (files.length > 0) {\n handleFiles(files);\n }\n }, [enabled, disabled, handleFiles]);\n\n /**\n * Returns props to spread on the drop zone container element.\n */\n const getDropZoneProps = useCallback((): DropZoneProps => ({\n onDragEnter: handleDragEnter,\n onDragOver: handleDragOver,\n onDragLeave: handleDragLeave,\n onDrop: handleDrop,\n }), [handleDragEnter, handleDragOver, handleDragLeave, handleDrop]);\n\n /**\n * Overlay component that renders when dragging files over the drop zone.\n * Should be rendered inside the drop zone container (which needs position: relative or similar).\n */\n const DropZoneOverlay = useMemo(() => {\n if (!isDragging || !enabled) return null;\n\n return (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n background: theme.primaryColorTranslucent,\n border: `3px dashed ${theme.primaryColor}`,\n borderRadius: 'inherit',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 1010,\n pointerEvents: 'none',\n }}\n >\n <div\n style={{\n background: theme.backgroundColor,\n padding: '16px 24px',\n borderRadius: '12px',\n boxShadow: theme.buttonShadow,\n }}\n >\n <span style={{ color: theme.primaryColor, fontWeight: 600, fontSize: '16px' }}>\n {strings.fileUpload.dropFilesHere}\n </span>\n </div>\n </div>\n );\n }, [isDragging, enabled, theme, strings]);\n\n return {\n attachments,\n isDragging,\n fileError,\n enabled,\n maxFileSize,\n acceptedTypes,\n fileInputRef,\n handleFiles,\n removeAttachment,\n clearAttachments,\n openFilePicker,\n handleFileInputChange,\n handleDragEnter,\n handleDragOver,\n handleDragLeave,\n handleDrop,\n getDropZoneProps,\n DropZoneOverlay,\n };\n}\n","/**\n * Default maximum file size (10MB)\n */\nexport const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;\n\n/**\n * Persisted file metadata (lightweight, for storage).\n * Only metadata is stored - not the actual file data.\n */\nexport interface PersistedFileMetadata {\n /** Original file name */\n name: string;\n /** File size in bytes */\n size: number;\n /** MIME type of the file */\n mimeType: string;\n}\n\n/**\n * Runtime file attachment (local File reference until send).\n * The File object is kept in memory until the message is sent,\n * at which point it's converted to a URL via the FileUploadBackend.\n */\nexport interface FileAttachment {\n /** Unique identifier for this attachment */\n id: string;\n /** The local File object */\n file: File;\n /** Data URL for image thumbnails (generated on attach for preview) */\n preview?: string;\n}\n\n/**\n * Abstract file upload backend interface.\n * Converts File objects to URLs at send time.\n *\n * Implementations:\n * - EmbedFileUploadBackend: Converts to base64 data URL (built-in)\n * - S3FileUploadBackend: Uploads to S3 and returns public URL (future)\n */\nexport interface FileUploadBackend {\n /**\n * Prepare file for sending to AI.\n * Called at send time - converts File to URL.\n *\n * @param file - The File object to prepare\n * @returns Promise resolving to a URL string\n * - For embed: base64 data URL\n * - For S3: public URL after upload\n */\n prepareForSend(file: File): Promise<string>;\n}\n\n/**\n * Configuration for file uploads in UseAIProvider.\n */\nexport interface FileUploadConfig {\n /**\n * Backend for converting files to URLs at send time.\n * Defaults to EmbedFileUploadBackend if not specified.\n */\n backend?: FileUploadBackend;\n /**\n * Maximum file size in bytes.\n * @default 10485760 (10MB)\n */\n maxFileSize?: number;\n /**\n * Accepted MIME types.\n * Supports patterns like 'image/*' or specific types like 'application/pdf'.\n * If undefined, all types are accepted.\n */\n acceptedTypes?: string[];\n}\n","import React, { useState, useCallback, useMemo } from 'react';\n\n/**\n * Return value from useDropdownState hook.\n */\nexport interface UseDropdownStateReturn {\n /** Whether the dropdown is currently open */\n isOpen: boolean;\n /** Opens the dropdown */\n open: () => void;\n /** Closes the dropdown */\n close: () => void;\n /** Toggles the dropdown open/closed */\n toggle: () => void;\n /**\n * Backdrop element that closes dropdown when clicked.\n * Render this as a sibling to the dropdown content (both inside a container).\n * Returns null when dropdown is closed.\n */\n Backdrop: React.ReactNode;\n}\n\nexport interface UseDropdownStateOptions {\n /** z-index for the backdrop (default: 1002) */\n backdropZIndex?: number;\n /** Initial open state (default: false) */\n initialOpen?: boolean;\n}\n\n/**\n * Hook for managing dropdown open/close state with click-outside handling.\n *\n * Provides a reusable pattern for dropdowns that:\n * - Track open/close state\n * - Close when clicking outside (via backdrop)\n *\n * @example\n * ```typescript\n * const dropdown = useDropdownState();\n *\n * return (\n * <div style={{ position: 'relative' }}>\n * <button onClick={dropdown.toggle}>Toggle</button>\n *\n * {dropdown.isOpen && (\n * <div className=\"dropdown-content\">\n * Dropdown content here\n * </div>\n * )}\n *\n * {dropdown.Backdrop}\n * </div>\n * );\n * ```\n */\nexport function useDropdownState(options: UseDropdownStateOptions = {}): UseDropdownStateReturn {\n const { backdropZIndex = 1002, initialOpen = false } = options;\n\n const [isOpen, setIsOpen] = useState(initialOpen);\n\n const open = useCallback(() => {\n setIsOpen(true);\n }, []);\n\n const close = useCallback(() => {\n setIsOpen(false);\n }, []);\n\n const toggle = useCallback(() => {\n setIsOpen(prev => !prev);\n }, []);\n\n const Backdrop = useMemo(() => {\n if (!isOpen) return null;\n\n return (\n <div\n onClick={close}\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n zIndex: backdropZIndex,\n }}\n />\n );\n }, [isOpen, close, backdropZIndex]);\n\n return {\n isOpen,\n open,\n close,\n toggle,\n Backdrop,\n };\n}\n","import React from 'react';\nimport { useTheme } from '../theme';\n\n/**\n * Props for the floating chat wrapper.\n */\ninterface UseAIFloatingChatWrapperProps {\n isOpen: boolean;\n onClose: () => void;\n children: React.ReactNode;\n}\n\n/**\n * Wrapper that adds floating chrome (backdrop, positioning, animations).\n * Wrap UseAIChatPanel with this for a floating chat experience.\n */\nexport function UseAIFloatingChatWrapper({\n isOpen,\n onClose,\n children,\n}: UseAIFloatingChatWrapperProps) {\n const theme = useTheme();\n\n if (!isOpen) return null;\n\n return (\n <>\n {/* Backdrop */}\n <div\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n background: theme.backdropColor,\n zIndex: 999,\n animation: 'fadeIn 0.2s',\n }}\n onClick={onClose}\n />\n\n {/* Floating panel */}\n <div\n style={{\n position: 'fixed',\n bottom: '24px',\n right: '24px',\n width: '380px',\n height: '600px',\n maxHeight: 'calc(100vh - 48px)',\n borderRadius: '16px',\n boxShadow: theme.panelShadow,\n zIndex: 1001,\n animation: 'slideIn 0.3s ease-out',\n overflow: 'hidden',\n }}\n >\n {children}\n </div>\n\n <style>{`\n @keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes slideIn {\n from {\n transform: translateY(20px);\n opacity: 0;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n }\n `}</style>\n </>\n );\n}\n\ninterface CloseButtonProps {\n onClick: () => void;\n}\n\n/**\n * Close button component for the floating chat header.\n */\nexport function CloseButton({ onClick }: CloseButtonProps) {\n const theme = useTheme();\n\n return (\n <button\n data-testid=\"chat-close-button\"\n className=\"chat-close-button\"\n onClick={onClick}\n style={{\n background: 'transparent',\n border: 'none',\n borderRadius: '6px',\n padding: '6px 8px',\n cursor: 'pointer',\n color: theme.secondaryTextColor,\n fontSize: '20px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n transition: 'all 0.2s',\n lineHeight: 1,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = theme.hoverBackground;\n e.currentTarget.style.color = theme.textColor;\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.background = 'transparent';\n e.currentTarget.style.color = theme.secondaryTextColor;\n }}\n >\n ×\n </button>\n );\n}\n","import React, { createContext, useContext } from 'react';\nimport { UseAIChatPanel } from './UseAIChatPanel';\nimport { UseAIFloatingChatWrapper, CloseButton } from './UseAIFloatingChatWrapper';\nimport type { Message } from './UseAIChatPanel';\nimport type { AgentInfo } from '../types';\nimport type { FileUploadConfig, FileAttachment } from '../fileUpload/types';\nimport type { SavedCommand } from '../commands/types';\nimport type { Chat } from '../providers/chatRepository/types';\n\n/**\n * Internal context value for chat UI state.\n * This is populated by UseAIProvider and consumed by UseAIChat.\n */\nexport interface ChatUIContextValue {\n /** Whether connected to the server */\n connected: boolean;\n /** Whether the AI is processing */\n loading: boolean;\n /** Send a message with optional file attachments */\n sendMessage: (message: string, attachments?: FileAttachment[]) => void;\n /** Current messages in the conversation */\n messages: Message[];\n /** Currently streaming text from assistant (real-time updates) */\n streamingText: string;\n /** Aggregated suggestions from all useAI hooks */\n suggestions: string[];\n /** File upload configuration */\n fileUploadConfig: FileUploadConfig | undefined;\n /** Chat history management */\n history: {\n /** The current chat ID */\n currentId: string | null;\n /** Creates a new chat and switches to it */\n create: () => Promise<string | void>;\n /** Loads an existing chat by ID */\n load: (chatId: string) => Promise<void>;\n /** Deletes a chat by ID */\n delete: (chatId: string) => Promise<void>;\n /** Lists all available chats */\n list: () => Promise<Array<Omit<Chat, 'messages'>>>;\n };\n /** Agent selection */\n agents: {\n /** List of available agents from the server */\n available: AgentInfo[];\n /** The default agent ID from the server */\n default: string | null;\n /** The currently selected agent ID (null means use server default) */\n selected: string | null;\n /** Sets the agent to use for requests */\n set: (agentId: string | null) => void;\n };\n /** Command management */\n commands: {\n /** List of saved slash commands */\n list: SavedCommand[];\n /** Saves a new command */\n save: (name: string, text: string) => Promise<string>;\n /** Renames an existing command */\n rename: (id: string, newName: string) => Promise<void>;\n /** Deletes a command by ID */\n delete: (id: string) => Promise<void>;\n };\n /** UI state for floating chat */\n ui: {\n /** Whether the floating chat is open */\n isOpen: boolean;\n /** Set the chat open state */\n setOpen: (open: boolean) => void;\n };\n}\n\n/**\n * Internal context for chat UI state.\n * @internal\n */\nexport const __UseAIChatContext = createContext<ChatUIContextValue | null>(null);\n\n/**\n * Hook to access chat UI context.\n * @internal\n */\nfunction useChatUIContext(): ChatUIContextValue {\n const context = useContext(__UseAIChatContext);\n if (!context) {\n throw new Error(\n 'UseAIChat must be used within a UseAIProvider. ' +\n 'Make sure UseAIChat is a descendant of UseAIProvider.'\n );\n }\n return context;\n}\n\n/**\n * Props for UseAIChat component.\n */\nexport interface UseAIChatProps {\n /**\n * When true, renders as a floating panel with backdrop.\n * When false (default), renders inline filling its container.\n */\n floating?: boolean;\n}\n\n/**\n * Standalone chat component that can be placed anywhere within UseAIProvider.\n *\n * Use this when you want to control where the chat UI is rendered,\n * such as embedding it in a sidebar or specific container.\n *\n * @example\n * ```tsx\n * // Embedded in a sidebar\n * <UseAIProvider serverUrl=\"ws://localhost:8081\" renderChat={false}>\n * <div style={{ display: 'flex', height: '100vh' }}>\n * <MainContent style={{ flex: 1 }} />\n * <aside style={{ width: 380 }}>\n * <UseAIChat />\n * </aside>\n * </div>\n * </UseAIProvider>\n *\n * // Floating mode (manually controlled)\n * <UseAIProvider serverUrl=\"ws://localhost:8081\" renderChat={false}>\n * <App />\n * <UseAIChat floating />\n * </UseAIProvider>\n * ```\n */\nexport function UseAIChat({ floating = false }: UseAIChatProps) {\n const ctx = useChatUIContext();\n\n const chatPanelProps = {\n onSendMessage: ctx.sendMessage,\n messages: ctx.messages,\n loading: ctx.loading,\n connected: ctx.connected,\n streamingText: ctx.streamingText,\n currentChatId: ctx.history.currentId,\n onNewChat: ctx.history.create,\n onLoadChat: ctx.history.load,\n onDeleteChat: ctx.history.delete,\n onListChats: ctx.history.list,\n suggestions: ctx.suggestions,\n availableAgents: ctx.agents.available,\n defaultAgent: ctx.agents.default,\n selectedAgent: ctx.agents.selected,\n onAgentChange: ctx.agents.set,\n fileUploadConfig: ctx.fileUploadConfig,\n commands: ctx.commands.list,\n onSaveCommand: ctx.commands.save,\n onRenameCommand: ctx.commands.rename,\n onDeleteCommand: ctx.commands.delete,\n };\n\n if (floating) {\n return (\n <UseAIFloatingChatWrapper\n isOpen={ctx.ui.isOpen}\n onClose={() => ctx.ui.setOpen(false)}\n >\n <UseAIChatPanel\n {...chatPanelProps}\n closeButton={<CloseButton onClick={() => ctx.ui.setOpen(false)} />}\n />\n </UseAIFloatingChatWrapper>\n );\n }\n\n return <UseAIChatPanel {...chatPanelProps} />;\n}\n","import { io, Socket } from 'socket.io-client';\nimport { EventType } from '@meetsmore-oss/use-ai-core';\nimport type {\n ToolDefinition,\n Message,\n RunAgentInput,\n AGUIEvent,\n TextMessageStartEvent,\n TextMessageContentEvent,\n ToolCallStartEvent,\n ToolCallArgsEvent,\n ToolCallEndEvent,\n UseAIClientMessage,\n ToolResultMessage,\n McpHeadersMap,\n AgentInfo,\n MultimodalContent,\n} from './types';\nimport { v4 as uuidv4 } from 'uuid';\n\n/**\n * Handler for AG-UI events from the server.\n */\nexport type AGUIEventHandler = (event: AGUIEvent) => void;\n\n/**\n * Simplified message handler for text responses.\n */\nexport type MessageHandler = (content: string) => void;\n\n/**\n * Tool call handler that receives the tool name, arguments, and a callback to send the result.\n */\nexport type ToolCallHandler = (\n toolCallId: string,\n toolName: string,\n args: Record<string, unknown>\n) => void;\n\n/**\n * Tool call structure for assistant messages\n */\ntype MessageToolCall = {\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n};\n\n/**\n * Assistant message with optional tool calls\n */\ntype AssistantMessageWithTools = Message & {\n role: 'assistant';\n toolCalls?: MessageToolCall[];\n};\n\n/**\n * Tool result message\n */\ntype ToolResultMessageData = Message & {\n role: 'tool';\n toolCallId: string;\n};\n\n/**\n * Socket.IO client for communicating with the UseAI server.\n * Uses the AG-UI protocol (https://docs.ag-ui.com/), so will be compatible with other AG-UI compliant servers.\n *\n * Handles:\n * - Connection management and automatic reconnection\n * - Sending RunAgentInput messages to server\n * - Parsing AG-UI event streams from server\n * - Tool execution coordination\n *\n * You probably don't need to use this directly, instead use {@link UseAIProvider}.\n */\nexport class UseAIClient {\n private socket: Socket | null = null;\n private eventHandlers: Map<string, AGUIEventHandler> = new Map();\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private reconnectDelay = 1000;\n\n // Session state\n private _threadId: string | null = null;\n private _tools: ToolDefinition[] = [];\n private _messages: Message[] = [];\n private _state: unknown = null;\n\n // MCP headers provider\n private mcpHeadersProvider?: () => McpHeadersMap | Promise<McpHeadersMap>;\n\n // Agent selection\n private _availableAgents: AgentInfo[] = [];\n private _defaultAgent: string | null = null;\n private _selectedAgent: string | null = null;\n private agentsChangeHandlers: Set<(agents: AgentInfo[], defaultAgent: string | null) => void> = new Set();\n\n // Connection state handlers\n private connectionStateHandlers: Set<(connected: boolean) => void> = new Set();\n\n // Text message assembly\n private _currentMessageId: string | null = null;\n private _currentMessageContent: string = '';\n\n // Assistant message assembly (for tracking full conversation history)\n private _currentAssistantMessage: { id: string; role: 'assistant'; content: string } | null = null;\n private _currentAssistantToolCalls: Array<{ id: string; type: 'function'; function: { name: string; arguments: string } }> = [];\n\n // Tool call assembly\n private currentToolCalls: Map<string, {\n name: string;\n args: string;\n }> = new Map();\n\n /**\n * Creates a new UseAI client instance.\n *\n * @param serverUrl - The WebSocket URL of the UseAI server\n */\n constructor(private serverUrl: string) {}\n\n /**\n * Establishes a Socket.IO connection to the server.\n * Connection state changes are notified via onConnectionStateChange().\n * Socket.IO handles reconnection automatically.\n */\n connect(): void {\n this.socket = io(this.serverUrl, {\n transports: ['polling', 'websocket'],\n reconnection: true,\n reconnectionAttempts: this.maxReconnectAttempts,\n reconnectionDelay: this.reconnectDelay,\n withCredentials: true,\n });\n\n this.socket.on('connect', () => {\n console.log('[UseAI] Connected to server');\n console.log('[UseAI] Transport:', this.socket?.io?.engine?.transport?.name);\n this.reconnectAttempts = 0;\n\n // Listen for transport upgrades (only if engine is available)\n const engine = this.socket?.io?.engine;\n if (engine) {\n engine.on('upgrade', (transport: { name: string }) => {\n console.log('[UseAI] Upgraded to transport:', transport.name);\n });\n\n engine.on('upgradeError', (err: { message: string }) => {\n console.warn('[UseAI] Upgrade error:', err.message);\n });\n }\n\n // Notify connection state handlers\n this.connectionStateHandlers.forEach(handler => handler(true));\n });\n\n this.socket.on('event', (aguiEvent: AGUIEvent) => {\n try {\n console.log('[Client] Received event:', aguiEvent.type);\n this.handleEvent(aguiEvent);\n } catch (error) {\n console.error('[UseAI] Error handling event:', error);\n }\n });\n\n // Listen for available agents from server\n this.socket.on('agents', (data: { agents: AgentInfo[]; defaultAgent: string }) => {\n console.log('[Client] Received available agents:', data);\n this._availableAgents = data.agents;\n this._defaultAgent = data.defaultAgent;\n // Notify listeners\n this.agentsChangeHandlers.forEach(handler => handler(data.agents, data.defaultAgent));\n });\n\n this.socket.on('connect_error', (error) => {\n // Use warn instead of error to avoid triggering Next.js error overlay\n console.warn('[UseAI] Connection error:', error.message);\n });\n\n this.socket.on('disconnect', (reason) => {\n console.log('[UseAI] Disconnected:', reason);\n // Notify connection state handlers\n this.connectionStateHandlers.forEach(handler => handler(false));\n });\n }\n\n\n private handleEvent(event: AGUIEvent) {\n // Track assistant message lifecycle for conversation history\n if (event.type === EventType.RUN_STARTED) {\n // Start of a new assistant response - initialize message\n this._currentAssistantMessage = {\n id: uuidv4(),\n role: 'assistant' as const,\n content: '',\n };\n this._currentAssistantToolCalls = [];\n }\n\n // Handle text message streaming\n if (event.type === EventType.TEXT_MESSAGE_START) {\n const e = event as TextMessageStartEvent;\n this._currentMessageId = e.messageId;\n this._currentMessageContent = '';\n } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {\n const e = event as TextMessageContentEvent;\n this._currentMessageContent += e.delta;\n } else if (event.type === EventType.TEXT_MESSAGE_END) {\n // Message complete - store content in assistant message\n if (this._currentAssistantMessage) {\n this._currentAssistantMessage.content = this._currentMessageContent;\n }\n this._currentMessageId = null;\n }\n\n // Handle tool call streaming\n else if (event.type === EventType.TOOL_CALL_START) {\n const e = event as ToolCallStartEvent;\n this.currentToolCalls.set(e.toolCallId, {\n name: e.toolCallName,\n args: '',\n });\n } else if (event.type === EventType.TOOL_CALL_ARGS) {\n const e = event as ToolCallArgsEvent;\n const toolCall = this.currentToolCalls.get(e.toolCallId);\n if (toolCall) {\n toolCall.args += e.delta;\n }\n } else if (event.type === EventType.TOOL_CALL_END) {\n // Tool call args complete - add to assistant message\n const e = event as ToolCallEndEvent;\n const toolCall = this.currentToolCalls.get(e.toolCallId);\n if (toolCall) {\n this._currentAssistantToolCalls.push({\n id: e.toolCallId,\n type: 'function',\n function: {\n name: toolCall.name,\n arguments: toolCall.args,\n },\n });\n }\n }\n\n // Handle run completion - finalize assistant message\n else if (event.type === EventType.RUN_FINISHED) {\n // Add completed assistant message to conversation history\n if (this._currentAssistantMessage) {\n const assistantMessage: AssistantMessageWithTools = {\n id: this._currentAssistantMessage.id!,\n role: 'assistant',\n content: this._currentAssistantMessage.content || '',\n };\n\n // Add tool calls if any\n if (this._currentAssistantToolCalls.length > 0) {\n assistantMessage.toolCalls = this._currentAssistantToolCalls;\n }\n\n this._messages.push(assistantMessage);\n\n // Reset for next message\n this._currentAssistantMessage = null;\n this._currentAssistantToolCalls = [];\n }\n }\n\n // Notify all registered handlers\n this.eventHandlers.forEach((handler) => handler(event));\n }\n\n /**\n * Registers available tools and optional state with the server.\n * This updates the session state for future agent runs.\n *\n * @param tools - Array of tool definitions to register\n * @param state - Optional state object to provide to the AI.\n */\n registerTools(tools: ToolDefinition[], state?: unknown) {\n this._tools = tools;\n // Only update state if explicitly provided to avoid overwriting state set by updateState\n if (state !== undefined) {\n this._state = state;\n }\n }\n\n /**\n * Updates the state without re-registering tools.\n * Call this before sendPrompt to ensure the AI sees the latest UI state.\n *\n * @param state - The current state object to provide to the AI\n */\n updateState(state: unknown) {\n this._state = state;\n }\n\n /**\n * Sets the MCP headers provider.\n * The provider will be called each time a message is sent to get fresh headers.\n *\n * @param provider - Function that returns MCP headers configuration\n */\n setMcpHeadersProvider(provider: () => McpHeadersMap | Promise<McpHeadersMap>) {\n this.mcpHeadersProvider = provider;\n }\n\n /**\n * Sends a user prompt to the AI.\n *\n * @param prompt - The user's prompt/question (text part)\n * @param multimodalContent - Optional multimodal content (text, images, files)\n */\n async sendPrompt(prompt: string, multimodalContent?: MultimodalContent[]) {\n // Build message content - use multimodal if provided, otherwise just the text\n // AG-UI Message type expects content to be string | ContentPart[]\n // For multimodal content, we pass the array; for text-only, we pass the string\n type MessageContent = string | Array<{ type: string; [key: string]: unknown }>;\n let messageContent: MessageContent = prompt;\n\n if (multimodalContent && multimodalContent.length > 0) {\n // Convert our MultimodalContent to AG-UI ContentPart format\n messageContent = multimodalContent.map(part => {\n if (part.type === 'text') {\n return { type: 'text', text: part.text };\n } else if (part.type === 'image') {\n return { type: 'image', url: part.url };\n } else {\n return {\n type: 'file',\n url: part.url,\n mimeType: part.mimeType,\n };\n }\n });\n }\n\n // Add user message to conversation\n const userMessage: Message = {\n id: uuidv4(),\n role: 'user',\n content: messageContent as string, // Type cast needed for Message type compatibility\n };\n this._messages.push(userMessage);\n\n // Get MCP headers if provider is set\n let mcpHeaders: McpHeadersMap | undefined;\n if (this.mcpHeadersProvider) {\n try {\n mcpHeaders = await this.mcpHeadersProvider();\n } catch (error) {\n console.error('[UseAIClient] Failed to get MCP headers:', error);\n // Continue without headers rather than blocking the request\n }\n }\n\n // Create RunAgentInput\n const runInput: RunAgentInput = {\n threadId: this.threadId, // Use getter to ensure non-null\n runId: uuidv4(),\n messages: this._messages,\n tools: this._tools.map(t => ({\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n })),\n state: this._state,\n context: [],\n forwardedProps: {\n ...(mcpHeaders ? { mcpHeaders } : {}),\n ...(this._selectedAgent ? { agent: this._selectedAgent } : {}),\n },\n };\n\n this.send({\n type: 'run_agent',\n data: runInput,\n });\n }\n\n /**\n * Sends the result of a tool execution back to the server.\n *\n * @param toolCallId - The ID of the tool call being responded to\n * @param result - The result returned by the tool execution\n * @param state - Optional updated state to send back to the AI\n */\n sendToolResponse(toolCallId: string, result: unknown, state?: unknown) {\n // Update session state if provided\n if (state !== undefined) {\n this._state = state;\n }\n\n const toolResultMessage: ToolResultMessage = {\n type: 'tool_result',\n data: {\n messageId: uuidv4(),\n toolCallId,\n content: JSON.stringify(result),\n role: 'tool',\n },\n };\n\n // Track tool result in conversation history\n const toolResultMsg: ToolResultMessageData = {\n id: toolResultMessage.data.messageId,\n role: 'tool',\n content: toolResultMessage.data.content,\n toolCallId,\n };\n this._messages.push(toolResultMsg);\n\n this.send(toolResultMessage);\n }\n\n /**\n * Retrieves accumulated tool call data for a specific tool call ID.\n * Used to get the complete tool name and arguments after they've been streamed\n * across multiple TOOL_CALL_ARGS events.\n *\n * @param toolCallId - The ID of the tool call\n * @returns Object with tool name and accumulated arguments, or undefined if not found\n */\n getToolCallData(toolCallId: string): { name: string; args: string } | undefined {\n return this.currentToolCalls.get(toolCallId);\n }\n\n /**\n * Registers an AG-UI event handler for receiving server events.\n *\n * @param id - Unique identifier for this handler\n * @param handler - Callback function to handle incoming AG-UI events\n * @returns Cleanup function to unregister the handler\n */\n onEvent(id: string, handler: AGUIEventHandler) {\n this.eventHandlers.set(id, handler);\n return () => {\n this.eventHandlers.delete(id);\n };\n }\n\n /**\n * Helper method to listen for text message content.\n * Aggregates TEXT_MESSAGE_CONTENT events and calls handler with complete messages.\n *\n * @param handler - Callback function to handle complete text messages\n * @returns Cleanup function\n */\n onTextMessage(handler: MessageHandler): () => void {\n return this.onEvent('text-message-handler', (event) => {\n if (event.type === EventType.TEXT_MESSAGE_END && this._currentMessageContent) {\n handler(this._currentMessageContent);\n }\n });\n }\n\n /**\n * Helper method to listen for tool call requests.\n * Aggregates TOOL_CALL_* events and calls handler with complete tool calls.\n *\n * @param handler - Callback function to handle tool calls\n * @returns Cleanup function\n */\n onToolCall(handler: ToolCallHandler): () => void {\n return this.onEvent('tool-call-handler', (event) => {\n if (event.type === EventType.TOOL_CALL_END) {\n const e = event as ToolCallEndEvent;\n const toolCall = this.currentToolCalls.get(e.toolCallId);\n if (toolCall) {\n try {\n const args = JSON.parse(toolCall.args);\n handler(e.toolCallId, toolCall.name, args);\n this.currentToolCalls.delete(e.toolCallId);\n } catch (error) {\n console.error('Error parsing tool call args:', error);\n }\n }\n }\n });\n }\n\n /**\n * Gets the current accumulated message content (useful during streaming).\n */\n get currentMessageContent(): string {\n return this._currentMessageContent;\n }\n\n /**\n * Gets the current thread ID for this session.\n * Generates a new one if not set.\n */\n get threadId(): string {\n if (!this._threadId) {\n this._threadId = uuidv4();\n }\n return this._threadId;\n }\n\n /**\n * Gets the current conversation messages.\n */\n get messages(): Message[] {\n return this._messages;\n }\n\n /**\n * Gets the current state.\n */\n get state(): unknown {\n return this._state;\n }\n\n /**\n * Gets the list of available agents from the server.\n */\n get availableAgents(): AgentInfo[] {\n return this._availableAgents;\n }\n\n /**\n * Gets the default agent ID from the server.\n */\n get defaultAgent(): string | null {\n return this._defaultAgent;\n }\n\n /**\n * Gets the currently selected agent ID.\n * Returns null if using server default.\n */\n get selectedAgent(): string | null {\n return this._selectedAgent;\n }\n\n /**\n * Gets the effective agent ID (selected or default).\n */\n get currentAgent(): string | null {\n return this._selectedAgent ?? this._defaultAgent;\n }\n\n /**\n * Sets the agent to use for requests.\n * Pass null to use the server default.\n *\n * @param agentId - The agent ID to use, or null for server default\n */\n setAgent(agentId: string | null) {\n this._selectedAgent = agentId;\n console.log('[Client] Agent set to:', agentId ?? 'server default');\n }\n\n /**\n * Subscribes to agent changes (when server sends available agents).\n *\n * @param handler - Callback function receiving agents list and default agent\n * @returns Cleanup function to unsubscribe\n */\n onAgentsChange(handler: (agents: AgentInfo[], defaultAgent: string | null) => void): () => void {\n this.agentsChangeHandlers.add(handler);\n // Immediately call with current values if available\n if (this._availableAgents.length > 0) {\n handler(this._availableAgents, this._defaultAgent);\n }\n return () => {\n this.agentsChangeHandlers.delete(handler);\n };\n }\n\n /**\n * Subscribes to connection state changes.\n * This is called on both initial connection and reconnection.\n *\n * @param handler - Callback function receiving connection state (true = connected, false = disconnected)\n * @returns Cleanup function to unsubscribe\n */\n onConnectionStateChange(handler: (connected: boolean) => void): () => void {\n this.connectionStateHandlers.add(handler);\n // Immediately call with current state\n handler(this.isConnected());\n return () => {\n this.connectionStateHandlers.delete(handler);\n };\n }\n\n /**\n * Sets the thread ID for this session.\n * When the thread ID changes, conversation state is cleared to prevent history bleeding.\n * Use this when switching between different chat conversations.\n *\n * @param threadId - The thread/chat ID to use (typically the chatId)\n */\n setThreadId(threadId: string) {\n if (this._threadId !== threadId) {\n console.log('[Client] ThreadId changed, clearing conversation state', {\n oldThreadId: this._threadId,\n newThreadId: threadId,\n });\n\n // Clear conversation state when switching threads\n this._messages = [];\n this._currentMessageContent = '';\n this._currentMessageId = null;\n this.currentToolCalls.clear();\n this._currentAssistantMessage = null;\n this._currentAssistantToolCalls = [];\n }\n this._threadId = threadId;\n }\n\n /**\n * Loads messages into the conversation history (for resuming from storage).\n * @param messages - Array of messages to load\n */\n loadMessages(messages: Message[]) {\n this._messages = messages;\n }\n\n /**\n * Clears the conversation history and resets the thread.\n */\n clearConversation() {\n this._messages = [];\n this._threadId = null;\n this._currentMessageContent = '';\n this._currentMessageId = null;\n this.currentToolCalls.clear();\n this._currentAssistantMessage = null;\n this._currentAssistantToolCalls = [];\n }\n\n send(message: UseAIClientMessage) {\n if (this.socket && this.socket.connected) {\n this.socket.emit('message', message);\n } else {\n console.error('Socket.IO is not connected');\n }\n }\n\n /**\n * Closes the Socket.IO connection to the server.\n */\n disconnect() {\n if (this.socket) {\n this.socket.disconnect();\n this.socket = null;\n }\n }\n\n /**\n * Checks if the client is currently connected to the server.\n *\n * @returns true if connected, false otherwise\n */\n isConnected(): boolean {\n return this.socket !== null && this.socket.connected;\n }\n}\n","import { z } from 'zod';\nimport type { ToolDefinition } from '@meetsmore-oss/use-ai-core';\n\n/**\n * Options for configuring tool behavior.\n */\nexport interface ToolOptions {\n /** Whether the tool asks the AI for explicit user confirmation before execution */\n confirmationRequired?: boolean;\n}\n\n/**\n * JSON Schema representation type (simplified)\n */\ninterface JSONSchema {\n type?: string;\n properties?: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean | Record<string, unknown>;\n [key: string]: unknown;\n}\n\n/**\n * A tool definition with validation schema and execution function.\n * Created by the `defineTool` function and used to define tools that the AI can call.\n *\n * @template T - The Zod schema type for validating tool input\n */\nexport interface DefinedTool<T extends z.ZodType> {\n /** Human-readable description of what the tool does */\n description: string;\n /** JSON Schema representation of the input schema */\n _jsonSchema: JSONSchema;\n /** Zod schema for validating input */\n _zodSchema: T;\n /** The function to execute when the tool is called */\n fn: (input: z.infer<T>) => unknown | Promise<unknown>;\n /** Configuration options for the tool */\n _options: ToolOptions;\n /** Converts this tool to a ToolDefinition for registration with the server */\n _toToolDefinition: (name: string) => ToolDefinition;\n /** Validates input and executes the tool function */\n _execute: (input: unknown) => Promise<unknown>;\n}\n\n/**\n * Defines a tool with no input parameters that can be called by the AI.\n *\n * @template TReturn - The return type of the tool function\n * @param description - Human-readable description of what the tool does\n * @param fn - The function to execute when the tool is called\n * @param options - Optional configuration for the tool\n * @returns A DefinedTool that can be registered with useAI\n *\n * @example\n * ```typescript\n * const getCurrentTime = defineTool(\n * 'Get the current time',\n * () => new Date().toISOString()\n * );\n * ```\n */\nexport function defineTool<TReturn>(\n description: string,\n fn: () => TReturn | Promise<TReturn>,\n options?: ToolOptions\n): DefinedTool<z.ZodObject<{}>>;\n\n/**\n * Defines a tool with typed input parameters that can be called by the AI.\n *\n * @template TSchema - The Zod schema type for validating input\n * @param description - Human-readable description of what the tool does\n * @param schema - Zod schema defining the tool's input parameters\n * @param fn - The function to execute when the tool is called\n * @param options - Optional configuration for the tool\n * @returns A DefinedTool that can be registered with useAI\n *\n * @example\n * ```typescript\n * import { defineTool } from '@meetsmore-oss/use-ai-client';\n * import { z } from 'zod';\n *\n * const addTodo = defineTool(\n * 'Add a new todo item',\n * z.object({ text: z.string() }),\n * (input) => {\n * todos.push({ id: Date.now(), text: input.text, completed: false });\n * return { success: true };\n * }\n * );\n * ```\n */\nexport function defineTool<TSchema extends z.ZodType>(\n description: string,\n schema: TSchema,\n fn: (input: z.infer<TSchema>) => unknown | Promise<unknown>,\n options?: ToolOptions\n): DefinedTool<TSchema>;\n\n/**\n * @internal\n * Implementation of defineTool that handles both overloads\n */\nexport function defineTool<T extends z.ZodType>(\n description: string,\n schemaOrFn: T | (() => unknown),\n fnOrOptions?: ((input: z.infer<T>) => unknown | Promise<unknown>) | ToolOptions,\n options?: ToolOptions\n): DefinedTool<T> {\n const isNoParamFunction = typeof schemaOrFn === 'function';\n const schema = (isNoParamFunction ? z.object({}) : schemaOrFn) as T;\n\n let actualFn: (input: z.infer<T>) => unknown | Promise<unknown>;\n let actualOptions: ToolOptions;\n\n if (isNoParamFunction) {\n actualFn = schemaOrFn as () => unknown | Promise<unknown>;\n actualOptions = (fnOrOptions as ToolOptions) || {};\n } else {\n actualFn = fnOrOptions as (input: z.infer<T>) => unknown | Promise<unknown>;\n actualOptions = options || {};\n }\n\n const jsonSchema = z.toJSONSchema(schema) as JSONSchema;\n\n return {\n description,\n _jsonSchema: jsonSchema,\n _zodSchema: schema,\n fn: actualFn,\n _options: actualOptions,\n\n _toToolDefinition(name: string): ToolDefinition {\n const parameters: {\n type: 'object';\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean | Record<string, unknown>;\n } = {\n type: 'object',\n properties: (this._jsonSchema.properties || {}) as Record<string, unknown>,\n };\n\n if (this._jsonSchema.required && this._jsonSchema.required.length > 0) {\n parameters.required = this._jsonSchema.required;\n }\n\n if (this._jsonSchema.additionalProperties !== undefined) {\n parameters.additionalProperties = this._jsonSchema.additionalProperties as boolean | Record<string, unknown>;\n }\n\n const toolDef: ToolDefinition = {\n name,\n description,\n parameters,\n };\n\n if (this._options.confirmationRequired) {\n toolDef.confirmationRequired = true;\n }\n\n return toolDef;\n },\n\n async _execute(input: unknown) {\n const validated = this._zodSchema.parse(input);\n return await actualFn(validated);\n },\n };\n}\n\n/**\n * A collection of named tools.\n * Used to register multiple tools with the useAI hook.\n */\nexport type ToolsDefinition = Record<string, DefinedTool<z.ZodType>>;\n\n/**\n * Converts a ToolsDefinition to an array of ToolDefinition objects.\n *\n * @param tools - The tools to convert\n * @returns Array of tool definitions suitable for server registration\n * @internal\n */\nexport function convertToolsToDefinitions(tools: ToolsDefinition): ToolDefinition[] {\n return Object.entries(tools).map(([name, tool]) => tool._toToolDefinition(name));\n}\n\n/**\n * Executes a defined tool by name with the provided input.\n *\n * @param tools - The collection of available tools\n * @param toolName - The name of the tool to execute\n * @param input - The input parameters for the tool\n * @returns The result of executing the tool\n * @throws Error if the tool is not found\n * @internal\n */\nexport async function executeDefinedTool(\n tools: ToolsDefinition,\n toolName: string,\n input: unknown\n): Promise<unknown> {\n const tool = tools[toolName];\n if (!tool) {\n throw new Error(`Tool \"${toolName}\" not found`);\n }\n return await tool._execute(input);\n}\n","import type {\n Chat,\n ChatRepository,\n CreateChatOptions,\n ListChatsOptions,\n} from './types';\nimport { generateChatId } from './types';\n\nconst STORAGE_KEY_PREFIX = 'use-ai:chat:';\nconst STORAGE_INDEX_KEY = 'use-ai:chat-index';\nconst MAX_CHATS = 20;\n\n/**\n * LocalStorage-based implementation of ChatRepository.\n * Stores chats in browser `localStorage`.\n *\n * Storage structure:\n * - `use-ai:chat-index`: Array of chat IDs\n * - `use-ai:chat:{id}`: Individual chat data\n *\n * Storage limit: Only the most recent 20 chats are kept by default.\n * When creating a new chat, the oldest chat (by updatedAt) is automatically deleted if the limit is reached.\n *\n * @example\n * ```typescript\n * // Use default 20-chat limit\n * const repository = new LocalStorageChatRepository();\n *\n * // Customize max chats limit\n * const repository = new LocalStorageChatRepository(localStorage, 50);\n * ```\n */\nexport class LocalStorageChatRepository implements ChatRepository {\n private storage: Storage;\n private maxChats: number;\n\n /**\n * Creates a new LocalStorageChatRepository.\n *\n * @param storage - Storage implementation to use (defaults to browser `localStorage`)\n * @param maxChats - Maximum number of chats to keep (defaults to 20). Oldest chats are automatically deleted when this limit is exceeded.\n */\n constructor(storage: Storage = localStorage, maxChats: number = MAX_CHATS) {\n this.storage = storage;\n this.maxChats = maxChats;\n }\n\n async createChat(options?: CreateChatOptions): Promise<string> {\n const id = generateChatId();\n const now = new Date();\n\n const chat: Chat = {\n id,\n title: options?.title,\n messages: [],\n createdAt: now,\n updatedAt: now,\n };\n\n // Enforce max chats limit by deleting oldest chat if needed\n await this.enforceMaxChatsLimit();\n\n await this.saveChat(chat);\n await this.addToIndex(id);\n\n return id;\n }\n\n async loadChat(id: string): Promise<Chat | null> {\n try {\n const key = this.getChatKey(id);\n const data = this.storage.getItem(key);\n\n if (!data) {\n return null;\n }\n\n const chat = JSON.parse(data) as Chat;\n\n // Deserialize dates\n chat.createdAt = new Date(chat.createdAt);\n chat.updatedAt = new Date(chat.updatedAt);\n chat.messages = chat.messages.map((msg) => ({\n ...msg,\n createdAt: new Date(msg.createdAt),\n }));\n\n return chat;\n } catch (error) {\n console.error(`Failed to load chat ${id}:`, error);\n return null;\n }\n }\n\n async saveChat(chat: Chat): Promise<void> {\n try {\n const key = this.getChatKey(chat.id);\n const data = JSON.stringify({\n ...chat,\n updatedAt: new Date(),\n });\n\n this.storage.setItem(key, data);\n } catch (error) {\n console.error(`Failed to save chat ${chat.id}:`, error);\n throw new Error(`Failed to save chat: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n async deleteChat(id: string): Promise<void> {\n try {\n const key = this.getChatKey(id);\n this.storage.removeItem(key);\n await this.removeFromIndex(id);\n } catch (error) {\n console.error(`Failed to delete chat ${id}:`, error);\n throw new Error(`Failed to delete chat: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n async listChats(\n options?: ListChatsOptions\n ): Promise<Array<Omit<Chat, 'messages'>>> {\n try {\n const ids = await this.getIndex();\n const chats: Array<Omit<Chat, 'messages'>> = [];\n\n for (const id of ids) {\n const chat = await this.loadChat(id);\n if (chat) {\n const { messages, ...metadata } = chat;\n chats.push(metadata);\n }\n }\n\n // Sort by updatedAt descending (most recent first)\n chats.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());\n\n // Apply pagination\n const { limit, offset = 0 } = options ?? {};\n const start = offset;\n const end = limit ? offset + limit : undefined;\n\n return chats.slice(start, end);\n } catch (error) {\n console.error('Failed to list chats:', error);\n return [];\n }\n }\n\n async deleteAll(): Promise<void> {\n try {\n const ids = await this.getIndex();\n\n for (const id of ids) {\n const key = this.getChatKey(id);\n this.storage.removeItem(key);\n }\n\n this.storage.removeItem(STORAGE_INDEX_KEY);\n } catch (error) {\n console.error('Failed to clear all chats:', error);\n throw new Error(`Failed to clear all chats: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n private getChatKey(id: string): string {\n return `${STORAGE_KEY_PREFIX}${id}`;\n }\n\n private async getIndex(): Promise<string[]> {\n try {\n const data = this.storage.getItem(STORAGE_INDEX_KEY);\n return data ? JSON.parse(data) : [];\n } catch (error) {\n console.error('Failed to load chat index:', error);\n return [];\n }\n }\n\n private async addToIndex(id: string): Promise<void> {\n const index = await this.getIndex();\n if (!index.includes(id)) {\n index.push(id);\n this.storage.setItem(STORAGE_INDEX_KEY, JSON.stringify(index));\n }\n }\n\n private async removeFromIndex(id: string): Promise<void> {\n const index = await this.getIndex();\n const filtered = index.filter((chatId) => chatId !== id);\n this.storage.setItem(STORAGE_INDEX_KEY, JSON.stringify(filtered));\n }\n\n private async enforceMaxChatsLimit(): Promise<void> {\n const chats = await this.listChats();\n\n if (chats.length >= this.maxChats) {\n // Sort by updatedAt ascending to find oldest\n const sortedChats = [...chats].sort(\n (a, b) => a.updatedAt.getTime() - b.updatedAt.getTime()\n );\n\n // Delete the oldest chat(s) to make room\n const numToDelete = chats.length - this.maxChats + 1;\n for (let i = 0; i < numToDelete; i++) {\n await this.deleteChat(sortedChats[i].id);\n }\n }\n }\n}\n","import type { FileUploadBackend } from './types';\n\n/**\n * File upload backend that embeds files as base64 data URLs.\n *\n * This is the default backend for file uploads. It converts files\n * to base64 data URLs at send time, which are then embedded directly\n * in the message sent to the AI.\n *\n * Pros:\n * - No external storage required\n * - Simple setup\n *\n * Cons:\n * - Increases message size (base64 is ~33% larger than binary)\n * - Files are not persistent across sessions\n * - Not suitable for very large files\n *\n * @example\n * ```typescript\n * import { UseAIProvider, EmbedFileUploadBackend } from '@meetsmore-oss/use-ai-client';\n *\n * <UseAIProvider\n * serverUrl=\"wss://...\"\n * fileUploadConfig={{\n * backend: new EmbedFileUploadBackend(),\n * maxFileSize: 10 * 1024 * 1024, // 10MB\n * }}\n * >\n * <App />\n * </UseAIProvider>\n * ```\n */\nexport class EmbedFileUploadBackend implements FileUploadBackend {\n /**\n * Converts a File to a base64 data URL.\n *\n * @param file - The File object to convert\n * @returns Promise resolving to a base64 data URL (e.g., \"data:image/png;base64,...\")\n * @throws Error if file reading fails\n */\n async prepareForSend(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n\n reader.onload = () => {\n if (typeof reader.result === 'string') {\n resolve(reader.result);\n } else {\n reject(new Error('Failed to read file as data URL'));\n }\n };\n\n reader.onerror = () => {\n reject(new Error(`Failed to read file: ${file.name}`));\n };\n\n reader.readAsDataURL(file);\n });\n }\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { ChatRepository, Chat, PersistedMessageContent } from '../providers/chatRepository/types';\nimport type { Message } from '../components/UseAIChatPanel';\nimport type { UseAIClient } from '../client';\nimport type { Message as AGUIMessage } from '../types';\n\n// Constants\nconst CHAT_TITLE_MAX_LENGTH = 50;\n\n/**\n * Generates a chat title from a message, truncating if necessary.\n */\nfunction generateChatTitle(message: string): string {\n return message.length > CHAT_TITLE_MAX_LENGTH\n ? message.substring(0, CHAT_TITLE_MAX_LENGTH) + '...'\n : message;\n}\n\n/**\n * Extracts text content from persisted message content.\n */\nfunction getTextFromContent(content: PersistedMessageContent): string {\n if (typeof content === 'string') {\n return content;\n }\n return content\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map(part => part.text)\n .join('\\n');\n}\n\n/**\n * Transforms storage messages to UI message format.\n */\nfunction transformMessagesToUI(\n storageMessages: Array<{\n id: string;\n role: string;\n content: PersistedMessageContent;\n createdAt: Date;\n displayMode?: 'default' | 'error';\n }>\n): Message[] {\n return storageMessages.map((msg) => ({\n id: msg.id,\n role: msg.role as 'user' | 'assistant',\n content: msg.content,\n timestamp: msg.createdAt,\n displayMode: msg.displayMode,\n }));\n}\n\n/**\n * Transforms UI messages to AG-UI message format for loading into client.\n */\nfunction transformMessagesToClientFormat(uiMessages: Message[]): AGUIMessage[] {\n return uiMessages.map((msg) => {\n const textContent = getTextFromContent(msg.content);\n return {\n id: msg.id,\n role: msg.role,\n content: textContent,\n };\n });\n}\n\nexport interface UseChatManagementOptions {\n /** Chat repository for persistence */\n repository: ChatRepository;\n /** Reference to the UseAIClient (can be null during initialization) */\n clientRef: React.MutableRefObject<UseAIClient | null>;\n}\n\nexport interface UseChatManagementReturn {\n /** Current active chat ID where AI responses are saved */\n currentChatId: string | null;\n /** Chat loaded for viewing but not yet active for AI responses */\n pendingChatId: string | null;\n /** Current messages in the chat */\n messages: Message[];\n /** The displayed chat ID (pending or current) */\n displayedChatId: string | null;\n /** Creates a new chat and switches to it */\n createNewChat: () => Promise<string>;\n /** Loads an existing chat by ID */\n loadChat: (chatId: string) => Promise<void>;\n /** Deletes a chat by ID */\n deleteChat: (chatId: string) => Promise<void>;\n /** Lists all available chats */\n listChats: () => Promise<Array<Omit<Chat, 'messages'>>>;\n /** Clears the current chat messages */\n clearCurrentChat: () => Promise<void>;\n /** Activates the pending chat (called when user sends first message) */\n activatePendingChat: () => string | null;\n /** Saves a user message to storage and reloads messages */\n saveUserMessage: (chatId: string, content: PersistedMessageContent) => Promise<boolean>;\n /** Saves an AI response to storage and optionally reloads messages */\n saveAIResponse: (content: string, displayMode?: 'default' | 'error') => Promise<void>;\n /** Reloads messages from storage for the given chat ID */\n reloadMessages: (chatId: string) => Promise<void>;\n /** Snapshot refs for use in event handlers */\n currentChatIdSnapshot: React.MutableRefObject<string | null>;\n pendingChatIdSnapshot: React.MutableRefObject<string | null>;\n}\n\n/**\n * Hook for managing chat lifecycle operations.\n *\n * Features:\n * - Creates, loads, deletes chats\n * - Manages pending/active chat state machine\n * - Saves user messages and AI responses\n * - Auto-generates chat titles\n * - Initializes with most recent chat or creates new one\n *\n * @example\n * ```typescript\n * const {\n * currentChatId,\n * pendingChatId,\n * messages,\n * createNewChat,\n * loadChat,\n * deleteChat,\n * listChats,\n * clearCurrentChat,\n * activatePendingChat,\n * saveUserMessage,\n * saveAIResponse,\n * } = useChatManagement({\n * repository: chatRepository,\n * clientRef,\n * });\n * ```\n */\nexport function useChatManagement({\n repository,\n clientRef,\n}: UseChatManagementOptions): UseChatManagementReturn {\n /**\n * Current active chat where AI responses are saved.\n * This is the \"source of truth\" for where new AI messages get persisted.\n */\n const [currentChatId, setCurrentChatId] = useState<string | null>(null);\n\n /**\n * Chat loaded for viewing but not yet active for AI responses.\n * Becomes currentChatId when user sends their first message.\n * This prevents race conditions when AI is still responding to the previous chat.\n */\n const [pendingChatId, setPendingChatId] = useState<string | null>(null);\n\n const [messages, setMessages] = useState<Message[]>([]);\n\n /**\n * Snapshot refs to capture latest chat IDs in event handler closures.\n * Event handlers are created once during mount and don't see updated state values.\n * These refs are kept in sync with state via useEffect to provide access to current values.\n */\n const currentChatIdSnapshot = useRef<string | null>(null);\n const pendingChatIdSnapshot = useRef<string | null>(null);\n\n // Keep snapshot refs in sync with latest chat IDs\n useEffect(() => {\n currentChatIdSnapshot.current = currentChatId;\n }, [currentChatId]);\n\n useEffect(() => {\n pendingChatIdSnapshot.current = pendingChatId;\n }, [pendingChatId]);\n\n /**\n * Loads messages from storage for a given chat ID.\n */\n const loadChatMessages = useCallback(async (chatId: string): Promise<Message[]> => {\n try {\n const chat = await repository.loadChat(chatId);\n\n if (chat) {\n const loadedMessages = transformMessagesToUI(chat.messages);\n console.log('[ChatManagement] Loaded', loadedMessages.length, 'messages from storage for chat:', chatId);\n return loadedMessages;\n } else {\n console.log('[ChatManagement] Chat not found in storage:', chatId);\n return [];\n }\n } catch (error) {\n console.error('[ChatManagement] Failed to load chat messages:', error);\n return [];\n }\n }, [repository]);\n\n /**\n * Reloads messages from storage and updates state.\n */\n const reloadMessages = useCallback(async (chatId: string) => {\n const loadedMessages = await loadChatMessages(chatId);\n setMessages(loadedMessages);\n }, [loadChatMessages]);\n\n /**\n * Creates a new chat.\n */\n const createNewChat = useCallback(async (): Promise<string> => {\n console.log('[ChatManagement] createNewChat called - currentChatId:', currentChatId, 'pendingChatId:', pendingChatId, 'messages.length:', messages.length);\n\n // If we already have a pending blank chat, don't create another one\n if (pendingChatId && messages.length === 0) {\n console.log('[ChatManagement] Pending chat is already blank, not creating new chat');\n return pendingChatId;\n }\n\n // If current chat is already blank (and no pending chat), don't create a new one\n if (currentChatId && !pendingChatId && messages.length === 0) {\n console.log('[ChatManagement] Current chat is already blank, not creating new chat');\n return currentChatId;\n }\n\n console.log('[ChatManagement] Creating new chat...');\n const chatId = await repository.createChat();\n\n // Set as pending - don't switch currentChatId until user sends a message\n setPendingChatId(chatId);\n setMessages([]); // Clear messages for the new blank chat\n\n // Set threadId to new chatId to ensure clean conversation state\n if (clientRef.current) {\n clientRef.current.setThreadId(chatId);\n console.log('[ChatManagement] Set threadId to new chatId:', chatId);\n }\n\n console.log('[ChatManagement] Created pending chat:', chatId, '(will activate on first message)');\n return chatId;\n }, [currentChatId, pendingChatId, messages, repository, clientRef]);\n\n /**\n * Loads an existing chat by ID.\n */\n const loadChat = useCallback(async (chatId: string): Promise<void> => {\n // Set as pending chat - don't activate until user sends a message\n // This prevents race condition if AI is still responding to current chat\n setPendingChatId(chatId);\n\n // Load messages from storage for display\n await reloadMessages(chatId);\n\n // Set threadId to chatId to ensure server recognizes this as a different conversation\n // This clears conversation state on the client and signals the server to clear history\n if (clientRef.current) {\n clientRef.current.setThreadId(chatId);\n console.log('[ChatManagement] Set threadId to chatId:', chatId);\n }\n\n console.log('[ChatManagement] Loaded pending chat:', chatId, '(will activate on first message)');\n }, [reloadMessages, clientRef]);\n\n /**\n * Deletes a chat by ID.\n */\n const deleteChat = useCallback(async (chatId: string): Promise<void> => {\n await repository.deleteChat(chatId);\n\n // Clear current chat if it's the one being deleted\n if (currentChatId === chatId) {\n setCurrentChatId(null);\n setMessages([]);\n }\n\n // Clear pending chat if it's the one being deleted\n if (pendingChatId === chatId) {\n setPendingChatId(null);\n setMessages([]);\n }\n\n console.log('[ChatManagement] Deleted chat:', chatId);\n }, [currentChatId, pendingChatId, repository]);\n\n /**\n * Lists all available chats.\n */\n const listChats = useCallback(async (): Promise<Array<Omit<Chat, 'messages'>>> => {\n return await repository.listChats();\n }, [repository]);\n\n /**\n * Clears the current chat messages.\n */\n const clearCurrentChat = useCallback(async (): Promise<void> => {\n setMessages([]);\n\n if (currentChatId) {\n const chat = await repository.loadChat(currentChatId);\n if (chat) {\n chat.messages = [];\n await repository.saveChat(chat);\n console.log('[ChatManagement] Cleared current chat:', currentChatId);\n }\n }\n }, [currentChatId, repository]);\n\n /**\n * Activates the pending chat (called when user sends first message).\n * Returns the activated chat ID, or null if no pending chat.\n */\n const activatePendingChat = useCallback((): string | null => {\n if (!pendingChatId) return null;\n\n console.log('[ChatManagement] Activating pending chat:', pendingChatId);\n\n // Load messages into client if they exist\n if (clientRef.current && messages.length > 0) {\n clientRef.current.loadMessages(transformMessagesToClientFormat(messages));\n console.log('[ChatManagement] Loaded', messages.length, 'existing messages into client');\n }\n\n setCurrentChatId(pendingChatId);\n setPendingChatId(null);\n\n return pendingChatId;\n }, [pendingChatId, messages, clientRef]);\n\n /**\n * Saves a user message to storage.\n */\n const saveUserMessage = useCallback(async (\n chatId: string,\n content: PersistedMessageContent\n ): Promise<boolean> => {\n try {\n const chat = await repository.loadChat(chatId);\n\n if (!chat) {\n console.error('[ChatManagement] Chat not found:', chatId);\n return false;\n }\n\n const { generateMessageId } = await import('../providers/chatRepository/types');\n chat.messages.push({\n id: generateMessageId(),\n role: 'user',\n content,\n createdAt: new Date(),\n });\n\n // Auto-generate title from text content\n if (!chat.title) {\n const text = getTextFromContent(content);\n if (text) {\n chat.title = generateChatTitle(text);\n }\n }\n\n await repository.saveChat(chat);\n console.log('[ChatManagement] Saved user message to storage');\n\n // Reload messages to show the new message\n await reloadMessages(chatId);\n return true;\n } catch (error) {\n console.error('[ChatManagement] Failed to save user message:', error);\n return false;\n }\n }, [repository, reloadMessages]);\n\n /**\n * Saves an AI response to storage and updates UI.\n */\n const saveAIResponse = useCallback(async (\n content: string,\n displayMode?: 'default' | 'error'\n ): Promise<void> => {\n const currentChatIdValue = currentChatIdSnapshot.current;\n const pendingChatIdValue = pendingChatIdSnapshot.current;\n const displayedChatId = pendingChatIdValue || currentChatIdValue;\n\n if (!currentChatIdValue) {\n console.warn('[ChatManagement] No current chat ID, cannot save AI response');\n return;\n }\n\n try {\n const chat = await repository.loadChat(currentChatIdValue);\n\n if (!chat) {\n console.error('[ChatManagement] Chat not found:', currentChatIdValue);\n return;\n }\n\n const { generateMessageId } = await import('../providers/chatRepository/types');\n chat.messages.push({\n id: generateMessageId(),\n role: 'assistant',\n content,\n createdAt: new Date(),\n displayMode,\n });\n\n // Auto-generate title from first user message if not set\n if (!chat.title) {\n const firstUserMessage = chat.messages.find(msg => msg.role === 'user');\n if (firstUserMessage) {\n const textContent = getTextFromContent(firstUserMessage.content);\n if (textContent) {\n chat.title = generateChatTitle(textContent);\n }\n }\n }\n\n await repository.saveChat(chat);\n console.log('[ChatManagement] Saved AI response to storage for chatId:', currentChatIdValue);\n\n // Reload UI if user is viewing this chat\n if (displayedChatId === currentChatIdValue) {\n await reloadMessages(currentChatIdValue);\n }\n } catch (error) {\n console.error('[ChatManagement] Failed to save AI response:', error);\n }\n }, [repository, reloadMessages]);\n\n // Initialize: load most recent chat or create new one on mount\n const initializedRef = useRef(false);\n useEffect(() => {\n // Only initialize if we don't have any chat (neither current nor pending)\n if (currentChatId === null && pendingChatId === null && !initializedRef.current) {\n initializedRef.current = true;\n\n (async () => {\n try {\n const chats = await repository.listChats({ limit: 1 });\n\n if (chats.length > 0) {\n // Load the most recent chat and activate it immediately (no AI response in progress at startup)\n const mostRecentChatId = chats[0].id;\n console.log('[ChatManagement] Loading most recent chat on mount:', mostRecentChatId);\n const loadedMessages = await loadChatMessages(mostRecentChatId);\n\n setCurrentChatId(mostRecentChatId);\n setMessages(loadedMessages);\n\n // Load into client for conversation history and set threadId\n if (clientRef.current) {\n clientRef.current.setThreadId(mostRecentChatId);\n clientRef.current.loadMessages(transformMessagesToClientFormat(loadedMessages));\n console.log('[ChatManagement] Set threadId and loaded messages for chat:', mostRecentChatId);\n }\n\n console.log('[ChatManagement] Loaded and activated chat on mount:', mostRecentChatId);\n } else {\n // No chats exist, create a new one (will be pending until first message)\n console.log('[ChatManagement] No existing chats, creating new one');\n await createNewChat();\n }\n } catch (err) {\n console.error('[ChatManagement] Failed to initialize chat:', err);\n initializedRef.current = false; // Reset on error so it can retry\n }\n })();\n }\n }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef]);\n\n // The displayed chat ID is the pending chat (if any) or the current active chat\n const displayedChatId = pendingChatId || currentChatId;\n\n return {\n currentChatId,\n pendingChatId,\n messages,\n displayedChatId,\n createNewChat,\n loadChat,\n deleteChat,\n listChats,\n clearCurrentChat,\n activatePendingChat,\n saveUserMessage,\n saveAIResponse,\n reloadMessages,\n currentChatIdSnapshot,\n pendingChatIdSnapshot,\n };\n}\n","import { useState, useCallback, useEffect, useMemo } from 'react';\nimport type { AgentInfo } from '../types';\nimport type { UseAIClient } from '../client';\n\nexport interface UseAgentSelectionOptions {\n /** Reference to the UseAIClient (can be null during initialization) */\n clientRef: React.MutableRefObject<UseAIClient | null>;\n /** Whether the client is connected (triggers subscription when true) */\n connected: boolean;\n /**\n * Optional list of agent IDs to show in the UI.\n * When provided, only agents with these IDs will be shown (if they exist on the server).\n * If the list is empty or no agents match, falls back to the default agent.\n */\n visibleAgentIds?: AgentInfo['id'][];\n}\n\nexport interface UseAgentSelectionReturn {\n /** List of available agents from the server */\n availableAgents: AgentInfo[];\n /** The default agent ID from the server */\n defaultAgent: string | null;\n /** The currently selected agent ID (null means use server default) */\n selectedAgent: string | null;\n /** Sets the agent to use for requests (null to use server default) */\n setAgent: (agentId: string | null) => void;\n}\n\n/**\n * Filters server agents based on the provided visibleAgentIds (list of IDs).\n * - If visibleAgentIds is undefined, returns all server agents.\n * - If visibleAgentIds is empty array or no matches found, logs warning and falls back to default agent.\n * - Otherwise, returns matched agents in config order.\n */\nfunction filterAgents(\n serverAgents: AgentInfo[],\n defaultAgentId: string | null,\n visibleAgentIds?: string[]\n): AgentInfo[] {\n // Helper to get default agent fallback\n const getDefaultAgentFallback = (): AgentInfo[] => {\n const defaultAgentInfo = serverAgents.find(a => a.id === defaultAgentId);\n return defaultAgentInfo ? [defaultAgentInfo] : serverAgents;\n };\n\n // visibleAgentIds is undefined - return all server agents\n if (visibleAgentIds === undefined) {\n return serverAgents;\n }\n\n // Empty array - warn and fallback to default agent\n if (visibleAgentIds.length === 0) {\n console.warn('[AgentSelection] visibleAgentIds is empty array, falling back to default agent');\n return getDefaultAgentFallback();\n }\n\n // Create a map of server agents for quick lookup\n const serverAgentMap = new Map(serverAgents.map(a => [a.id, a]));\n\n // Filter based on config IDs, preserving config order\n const matchedAgents = visibleAgentIds\n .filter(id => serverAgentMap.has(id))\n .map(id => serverAgentMap.get(id)!);\n\n // No matches found - warn and fallback to default agent\n if (matchedAgents.length === 0) {\n console.warn('[AgentSelection] No agents in visibleAgentIds match server agents, falling back to default agent');\n return getDefaultAgentFallback();\n }\n\n return matchedAgents;\n}\n\n/**\n * Hook for managing agent selection state.\n *\n * Features:\n * - Subscribes to agent changes from the server\n * - Tracks available agents, default agent, and user selection\n * - Syncs selection state with the WebSocket client\n * - Filters agents based on visibleAgentIds (list of IDs)\n *\n * @example\n * ```typescript\n * const {\n * availableAgents,\n * defaultAgent,\n * selectedAgent,\n * setAgent,\n * } = useAgentSelection({ clientRef, connected });\n *\n * // Select a specific agent\n * setAgent('claude-3-5-sonnet');\n *\n * // Reset to server default\n * setAgent(null);\n * ```\n *\n * @example\n * ```typescript\n * // With visibleAgentIds to filter agents by ID\n * const {\n * availableAgents, // Only includes agents with these IDs\n * } = useAgentSelection({\n * clientRef,\n * connected,\n * visibleAgentIds: ['claude-sonnet', 'claude-opus']\n * });\n * ```\n */\nexport function useAgentSelection({\n clientRef,\n connected,\n visibleAgentIds,\n}: UseAgentSelectionOptions): UseAgentSelectionReturn {\n const [serverAgents, setServerAgents] = useState<AgentInfo[]>([]);\n const [defaultAgent, setDefaultAgent] = useState<string | null>(null);\n const [selectedAgent, setSelectedAgent] = useState<string | null>(null);\n\n // Filter agents based on config\n const availableAgents = useMemo(\n () => filterAgents(serverAgents, defaultAgent, visibleAgentIds),\n [serverAgents, defaultAgent, visibleAgentIds]\n );\n\n /**\n * Sets the agent to use for requests.\n * Pass null to reset to server default.\n */\n const setAgent = useCallback((agentId: string | null) => {\n setSelectedAgent(agentId);\n if (clientRef.current) {\n clientRef.current.setAgent(agentId);\n }\n console.log('[AgentSelection] Agent set to:', agentId ?? 'server default');\n }, [clientRef]);\n\n // Subscribe to agent changes from the server\n useEffect(() => {\n const client = clientRef.current;\n if (!client || !connected) return;\n\n const unsubscribe = client.onAgentsChange((agents, defaultAgentId) => {\n console.log('[AgentSelection] Received agents:', agents, 'default:', defaultAgentId);\n setServerAgents(agents);\n setDefaultAgent(defaultAgentId);\n });\n\n return unsubscribe;\n }, [clientRef, connected]);\n\n // Auto-select the appropriate agent when visibleAgentIds is provided\n // Priority: 1) defaultAgent if it's in availableAgents, 2) first available agent\n useEffect(() => {\n if (selectedAgent === null && availableAgents.length > 0 && !availableAgents.some(a => a.id === defaultAgent)) {\n // Default agent is not available, select the first configured agent\n const firstAgentId = availableAgents[0].id;\n setAgent(firstAgentId);\n }\n }, [availableAgents, selectedAgent, defaultAgent, setAgent]);\n\n return {\n availableAgents,\n defaultAgent,\n selectedAgent,\n setAgent,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { CommandRepository, SavedCommand } from '../commands/types';\nimport { LocalStorageCommandRepository } from '../commands/LocalStorageCommandRepository';\n\nexport interface UseCommandManagementOptions {\n /** Custom command repository. Defaults to LocalStorageCommandRepository. */\n repository?: CommandRepository;\n}\n\nexport interface UseCommandManagementReturn {\n /** List of saved slash commands */\n commands: SavedCommand[];\n /** Refreshes the commands list from storage */\n refreshCommands: () => Promise<void>;\n /** Saves a new command */\n saveCommand: (name: string, text: string) => Promise<string>;\n /** Renames an existing command */\n renameCommand: (id: string, newName: string) => Promise<void>;\n /** Deletes a command by ID */\n deleteCommand: (id: string) => Promise<void>;\n}\n\n/**\n * Hook for managing slash commands persistence.\n *\n * Features:\n * - CRUD operations for slash commands\n * - Auto-loads commands on mount\n * - Uses LocalStorageCommandRepository by default\n *\n * @example\n * ```typescript\n * const {\n * commands,\n * refreshCommands,\n * saveCommand,\n * renameCommand,\n * deleteCommand,\n * } = useCommandManagement();\n *\n * // Save a new command\n * await saveCommand('greet', 'Hello, how can I help you today?');\n *\n * // Rename a command\n * await renameCommand(commandId, 'greeting');\n *\n * // Delete a command\n * await deleteCommand(commandId);\n * ```\n */\nexport function useCommandManagement({\n repository,\n}: UseCommandManagementOptions = {}): UseCommandManagementReturn {\n const repositoryRef = useRef<CommandRepository>(\n repository || new LocalStorageCommandRepository()\n );\n\n const [commands, setCommands] = useState<SavedCommand[]>([]);\n\n const refreshCommands = useCallback(async () => {\n try {\n const cmdList = await repositoryRef.current.listCommands();\n setCommands(cmdList);\n console.log('[CommandManagement] Loaded', cmdList.length, 'commands');\n } catch (err) {\n console.error('[CommandManagement] Failed to load commands:', err);\n }\n }, []);\n\n const saveCommand = useCallback(async (name: string, text: string): Promise<string> => {\n const id = await repositoryRef.current.createCommand({ name, text });\n await refreshCommands();\n console.log('[CommandManagement] Saved command:', name);\n return id;\n }, [refreshCommands]);\n\n const renameCommand = useCallback(async (id: string, newName: string): Promise<void> => {\n const command = await repositoryRef.current.loadCommand(id);\n if (!command) throw new Error(`Command ${id} not found`);\n command.name = newName.trim();\n await repositoryRef.current.updateCommand(command);\n await refreshCommands();\n console.log('[CommandManagement] Renamed command:', id, 'to', newName);\n }, [refreshCommands]);\n\n const deleteCommand = useCallback(async (id: string): Promise<void> => {\n await repositoryRef.current.deleteCommand(id);\n await refreshCommands();\n console.log('[CommandManagement] Deleted command:', id);\n }, [refreshCommands]);\n\n // Load commands on mount\n useEffect(() => {\n refreshCommands();\n }, [refreshCommands]);\n\n return {\n commands,\n refreshCommands,\n saveCommand,\n renameCommand,\n deleteCommand,\n };\n}\n","import type {\n CommandRepository,\n SavedCommand,\n CreateCommandOptions,\n ListCommandsOptions,\n} from './types';\nimport { generateCommandId } from './types';\n\nconst STORAGE_KEY_PREFIX = 'use-ai:command:';\nconst STORAGE_INDEX_KEY = 'use-ai:command-index';\nconst DEFAULT_MAX_COMMANDS = 50;\n\n/**\n * LocalStorage-based implementation of CommandRepository.\n * Stores commands in browser localStorage with an index for efficient listing.\n */\nexport class LocalStorageCommandRepository implements CommandRepository {\n private storage: Storage;\n private maxCommands: number;\n\n constructor(storage: Storage = localStorage, maxCommands: number = DEFAULT_MAX_COMMANDS) {\n this.storage = storage;\n this.maxCommands = maxCommands;\n }\n\n async createCommand(options: CreateCommandOptions): Promise<string> {\n const name = options.name.trim();\n\n // Check for duplicate name\n const existing = await this.loadCommandByName(name);\n if (existing) {\n throw new Error(`Command \"${name}\" already exists`);\n }\n\n const id = generateCommandId();\n const command: SavedCommand = {\n id,\n name,\n text: options.text,\n createdAt: new Date(),\n };\n\n await this.enforceMaxCommandsLimit();\n this.saveCommandToStorage(command);\n await this.addToIndex(id);\n return id;\n }\n\n async loadCommand(id: string): Promise<SavedCommand | null> {\n try {\n const data = this.storage.getItem(`${STORAGE_KEY_PREFIX}${id}`);\n if (!data) return null;\n return this.deserializeCommand(data);\n } catch {\n return null;\n }\n }\n\n async loadCommandByName(name: string): Promise<SavedCommand | null> {\n const trimmedName = name.trim();\n const commands = await this.listCommands();\n return commands.find(c => c.name === trimmedName) || null;\n }\n\n async updateCommand(command: SavedCommand): Promise<void> {\n this.saveCommandToStorage(command);\n }\n\n async deleteCommand(id: string): Promise<void> {\n this.storage.removeItem(`${STORAGE_KEY_PREFIX}${id}`);\n await this.removeFromIndex(id);\n }\n\n async listCommands(options?: ListCommandsOptions): Promise<SavedCommand[]> {\n const ids = this.getIndex();\n const commands: SavedCommand[] = [];\n\n for (const id of ids) {\n const cmd = await this.loadCommand(id);\n if (cmd) {\n // Filter by prefix if specified\n if (!options?.namePrefix || cmd.name.startsWith(options.namePrefix.toLowerCase())) {\n commands.push(cmd);\n }\n }\n }\n\n // Sort by lastUsedAt (most recent first), then by name\n commands.sort((a, b) => {\n if (a.lastUsedAt && b.lastUsedAt) {\n return b.lastUsedAt.getTime() - a.lastUsedAt.getTime();\n }\n if (a.lastUsedAt) return -1;\n if (b.lastUsedAt) return 1;\n return a.name.localeCompare(b.name);\n });\n\n return options?.limit ? commands.slice(0, options.limit) : commands;\n }\n\n async deleteAll(): Promise<void> {\n const ids = this.getIndex();\n for (const id of ids) {\n this.storage.removeItem(`${STORAGE_KEY_PREFIX}${id}`);\n }\n this.storage.removeItem(STORAGE_INDEX_KEY);\n }\n\n private saveCommandToStorage(command: SavedCommand): void {\n this.storage.setItem(\n `${STORAGE_KEY_PREFIX}${command.id}`,\n JSON.stringify(command)\n );\n }\n\n private deserializeCommand(data: string): SavedCommand {\n const parsed = JSON.parse(data) as SavedCommand;\n // Convert date strings back to Date objects\n parsed.createdAt = new Date(parsed.createdAt);\n if (parsed.lastUsedAt) {\n parsed.lastUsedAt = new Date(parsed.lastUsedAt);\n }\n return parsed;\n }\n\n private getIndex(): string[] {\n const data = this.storage.getItem(STORAGE_INDEX_KEY);\n return data ? JSON.parse(data) : [];\n }\n\n private async addToIndex(id: string): Promise<void> {\n const index = this.getIndex();\n if (!index.includes(id)) {\n index.push(id);\n this.storage.setItem(STORAGE_INDEX_KEY, JSON.stringify(index));\n }\n }\n\n private async removeFromIndex(id: string): Promise<void> {\n const index = this.getIndex();\n this.storage.setItem(\n STORAGE_INDEX_KEY,\n JSON.stringify(index.filter(i => i !== id))\n );\n }\n\n private async enforceMaxCommandsLimit(): Promise<void> {\n const commands = await this.listCommands();\n if (commands.length >= this.maxCommands) {\n // Sort by createdAt to find oldest\n const sorted = [...commands].sort(\n (a, b) => a.createdAt.getTime() - b.createdAt.getTime()\n );\n const numToDelete = commands.length - this.maxCommands + 1;\n for (let i = 0; i < numToDelete; i++) {\n await this.deleteCommand(sorted[i].id);\n }\n }\n }\n}\n","import { useState, useCallback, useRef, useMemo } from 'react';\nimport type { ToolsDefinition } from '../defineTool';\n\nexport interface RegisterToolsOptions {\n /** Mark component as invisible (no visual state, skip prompt wait) */\n invisible?: boolean;\n}\n\nexport interface UseToolRegistryReturn {\n /** Registers tools for a specific component */\n registerTools: (id: string, tools: ToolsDefinition, options?: RegisterToolsOptions) => void;\n /** Unregisters tools for a specific component */\n unregisterTools: (id: string) => void;\n /** Checks if a component is marked as invisible */\n isInvisible: (id: string) => boolean;\n /** All tools aggregated from registered components */\n aggregatedTools: ToolsDefinition;\n /** Whether any tools are registered */\n hasTools: boolean;\n /** Ref to current aggregated tools (for use in closures) */\n aggregatedToolsRef: React.MutableRefObject<ToolsDefinition>;\n /** Ref mapping tool names to component IDs */\n toolOwnershipRef: React.MutableRefObject<Map<string, string>>;\n}\n\n/**\n * Hook for managing tool registration and aggregation.\n *\n * Only handles tools - prompt management is handled separately.\n */\nexport function useToolRegistry(): UseToolRegistryReturn {\n const toolRegistryRef = useRef<Map<string, ToolsDefinition>>(new Map());\n const [toolRegistryVersion, setToolRegistryVersion] = useState(0);\n const toolOwnershipRef = useRef<Map<string, string>>(new Map());\n const invisibleRef = useRef<Set<string>>(new Set());\n\n const registerTools = useCallback((\n id: string,\n tools: ToolsDefinition,\n options?: RegisterToolsOptions\n ) => {\n const existingTools = toolRegistryRef.current.get(id);\n\n // Always update the ref to capture latest closures\n toolRegistryRef.current.set(id, tools);\n\n // Only increment version if tool names changed (added/removed tools)\n if (existingTools) {\n const existingKeys = Object.keys(existingTools).sort().join(',');\n const newKeys = Object.keys(tools).sort().join(',');\n if (existingKeys !== newKeys) {\n setToolRegistryVersion(v => v + 1);\n }\n } else {\n // First registration\n setToolRegistryVersion(v => v + 1);\n }\n\n Object.keys(tools).forEach(toolName => {\n toolOwnershipRef.current.set(toolName, id);\n });\n\n // Track invisible status\n if (options?.invisible) {\n invisibleRef.current.add(id);\n } else {\n invisibleRef.current.delete(id);\n }\n }, []);\n\n const unregisterTools = useCallback((id: string) => {\n const tools = toolRegistryRef.current.get(id);\n if (tools) {\n Object.keys(tools).forEach(toolName => {\n toolOwnershipRef.current.delete(toolName);\n });\n }\n\n toolRegistryRef.current.delete(id);\n setToolRegistryVersion(v => v + 1);\n invisibleRef.current.delete(id);\n }, []);\n\n const isInvisible = useCallback((id: string) => {\n return invisibleRef.current.has(id);\n }, []);\n\n const aggregatedTools = useMemo(() => {\n const tools: ToolsDefinition = {};\n toolRegistryRef.current.forEach((toolSet) => {\n Object.assign(tools, toolSet);\n });\n return tools;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [toolRegistryVersion]);\n\n const hasTools = toolRegistryRef.current.size > 0;\n\n // Keep a ref to aggregated tools for use in closures\n const aggregatedToolsRef = useRef(aggregatedTools);\n aggregatedToolsRef.current = aggregatedTools;\n\n return {\n registerTools,\n unregisterTools,\n isInvisible,\n aggregatedTools,\n hasTools,\n aggregatedToolsRef,\n toolOwnershipRef,\n };\n}\n","import { useState, useCallback, useRef, useMemo, useEffect } from 'react';\nimport type { UseAIClient } from '../client';\n\nexport interface UsePromptStateOptions {\n /** System prompt to include in state */\n systemPrompt?: string;\n /** Reference to the UseAIClient for state updates */\n clientRef: React.MutableRefObject<UseAIClient | null>;\n /** Whether the client is connected to the server */\n connected: boolean;\n}\n\nexport interface UsePromptStateReturn {\n /** Updates the prompt and suggestions for a specific component */\n updatePrompt: (id: string, prompt?: string, suggestions?: string[]) => void;\n /** Registers a waiter function for a component */\n registerWaiter: (id: string, waiter: () => Promise<void>) => void;\n /** Unregisters a waiter function */\n unregisterWaiter: (id: string) => void;\n /** Gets the waiter function for a component */\n getWaiter: (id: string) => (() => Promise<void>) | undefined;\n /** All suggestions aggregated from registered components */\n aggregatedSuggestions: string[];\n /** Ref mapping component IDs to prompts */\n promptsRef: React.MutableRefObject<Map<string, string>>;\n}\n\n/**\n * Hook for managing prompt state across multiple useAI hooks.\n *\n * Handles:\n * - Storing prompts and suggestions per component\n * - Updating client state when prompts change\n * - Managing waiter functions for prompt change notifications\n * - Aggregating suggestions from all components\n */\nexport function usePromptState({\n systemPrompt,\n clientRef,\n connected,\n}: UsePromptStateOptions): UsePromptStateReturn {\n const promptsRef = useRef<Map<string, string>>(new Map());\n const suggestionsRef = useRef<Map<string, string[]>>(new Map());\n const waitersRef = useRef<Map<string, () => Promise<void>>>(new Map());\n const [suggestionsVersion, setSuggestionsVersion] = useState(0);\n\n // Build state from all prompts\n const buildStateFromPrompts = useCallback(() => {\n const promptParts: string[] = [];\n if (systemPrompt) {\n promptParts.push(systemPrompt);\n }\n for (const [, prompt] of promptsRef.current.entries()) {\n if (prompt) {\n promptParts.push(prompt);\n }\n }\n return promptParts.length > 0 ? { context: promptParts.join('\\n\\n---\\n\\n') } : null;\n }, [systemPrompt]);\n\n // Sync system prompt to client when connected\n // This ensures the system prompt is sent even when no useAI hooks are present\n useEffect(() => {\n if (connected && clientRef.current && systemPrompt) {\n clientRef.current.updateState(buildStateFromPrompts());\n }\n }, [connected, clientRef, systemPrompt, buildStateFromPrompts]);\n\n const updatePrompt = useCallback((id: string, prompt?: string, suggestions?: string[]) => {\n if (prompt) {\n promptsRef.current.set(id, prompt);\n } else {\n promptsRef.current.delete(id);\n }\n\n const hadSuggestions = suggestionsRef.current.has(id);\n if (suggestions && suggestions.length > 0) {\n suggestionsRef.current.set(id, suggestions);\n if (!hadSuggestions) setSuggestionsVersion(v => v + 1);\n } else {\n suggestionsRef.current.delete(id);\n if (hadSuggestions) setSuggestionsVersion(v => v + 1);\n }\n\n // Update client state immediately when prompts change\n // `connected` in deps ensures this callback reference changes when connection is established,\n // triggering useAI's effect to re-run and sync prompts to the client\n if (clientRef.current) {\n clientRef.current.updateState(buildStateFromPrompts());\n }\n }, [buildStateFromPrompts, clientRef, connected]);\n\n const registerWaiter = useCallback((id: string, waiter: () => Promise<void>) => {\n waitersRef.current.set(id, waiter);\n }, []);\n\n const unregisterWaiter = useCallback((id: string) => {\n waitersRef.current.delete(id);\n }, []);\n\n const getWaiter = useCallback((id: string) => {\n return waitersRef.current.get(id);\n }, []);\n\n const aggregatedSuggestions = useMemo(() => {\n const allSuggestions: string[] = [];\n suggestionsRef.current.forEach((suggestions) => {\n allSuggestions.push(...suggestions);\n });\n return allSuggestions;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [suggestionsVersion]);\n\n return {\n updatePrompt,\n registerWaiter,\n unregisterWaiter,\n getWaiter,\n aggregatedSuggestions,\n promptsRef,\n };\n}\n","import { useRef } from 'react';\nimport type { ToolsDefinition, DefinedTool } from '../defineTool';\nimport type { z } from 'zod';\n\n/**\n * Creates stable tool references that maintain fresh closures.\n *\n * This hook solves the \"render loop\" problem that occurs when users define tools\n * inline without memoization. It ensures that:\n *\n * 1. Tool object references remain stable as long as tool names don't change\n * 2. Handler calls are proxied through refs to always use the latest closure\n * 3. Metadata (description, schema, confirmationRequired) updates in-place\n *\n * @param tools - The tools definition from the user (potentially unstable references)\n * @returns Stabilized tools definition that won't cause effect re-runs\n *\n * @example\n * ```typescript\n * // Previously problematic - caused render loops\n * useAI({\n * tools: {\n * updateState: defineTool('Update state', z.object({ value: z.string() }),\n * (input) => setState(input.value) // Closure recreated every render\n * ),\n * },\n * });\n *\n * // Now works correctly - useStableTools handles stabilization internally\n * ```\n */\nexport function useStableTools(tools: ToolsDefinition | undefined): ToolsDefinition | undefined {\n // Ref to store latest tools (updated every render for fresh closures)\n const latestToolsRef = useRef<ToolsDefinition>({});\n\n // Ref to store stable wrapper objects\n const stableToolsRef = useRef<ToolsDefinition>({});\n\n // Track tool names for change detection\n const prevToolNamesRef = useRef<string>('');\n\n if (!tools) {\n latestToolsRef.current = {};\n return undefined;\n }\n\n // Always update latest ref (for fresh closures)\n latestToolsRef.current = tools;\n\n const currentToolNames = Object.keys(tools).sort().join(',');\n\n if (currentToolNames !== prevToolNamesRef.current) {\n // Tool names changed - rebuild stable wrappers\n prevToolNamesRef.current = currentToolNames;\n stableToolsRef.current = {};\n\n for (const [name, tool] of Object.entries(tools)) {\n stableToolsRef.current[name] = createStableToolWrapper(\n name,\n tool,\n latestToolsRef\n );\n }\n } else {\n // Tool names unchanged - update metadata in-place without creating new objects\n for (const [name, tool] of Object.entries(tools)) {\n const stable = stableToolsRef.current[name];\n if (stable) {\n stable.description = tool.description;\n stable._jsonSchema = tool._jsonSchema;\n stable._zodSchema = tool._zodSchema;\n stable._options = tool._options;\n }\n }\n }\n\n return stableToolsRef.current;\n}\n\n/**\n * Creates a stable wrapper for a tool that proxies handler calls through refs.\n *\n * The wrapper has a stable identity but always calls the latest handler.\n */\nfunction createStableToolWrapper(\n name: string,\n tool: DefinedTool<z.ZodType>,\n latestToolsRef: React.MutableRefObject<ToolsDefinition>\n): DefinedTool<z.ZodType> {\n // Create a stable handler that proxies to the latest version\n const stableHandler = (input: unknown) => {\n const currentTool = latestToolsRef.current[name];\n if (!currentTool) {\n throw new Error(`Tool \"${name}\" no longer exists`);\n }\n return currentTool.fn(input);\n };\n\n // Create the stable _execute function\n const stableExecute = async (input: unknown) => {\n const currentTool = latestToolsRef.current[name];\n if (!currentTool) {\n throw new Error(`Tool \"${name}\" no longer exists`);\n }\n return await currentTool._execute(input);\n };\n\n return {\n description: tool.description,\n _jsonSchema: tool._jsonSchema,\n _zodSchema: tool._zodSchema,\n fn: stableHandler,\n _options: tool._options,\n _toToolDefinition: tool._toToolDefinition.bind(tool),\n _execute: stableExecute,\n };\n}\n","/**\n * TODO: We would prefer to have this in a separate package, but it creates bundling problems with shared react contexts.\n */\n\nimport { useState, useCallback, useRef, useEffect } from 'react';\nimport { useAIContext } from './providers/useAIProvider';\nimport { type ToolsDefinition, executeDefinedTool, convertToolsToDefinitions } from './defineTool';\nimport type { AGUIEvent, RunErrorEvent, TextMessageContentEvent, ToolCallEndEvent, WorkflowStatus } from '@meetsmore-oss/use-ai-core';\nimport { EventType } from '@meetsmore-oss/use-ai-core';\nimport type { RunWorkflowMessage } from '@meetsmore-oss/use-ai-core';\nimport { v4 as uuidv4 } from 'uuid';\n\n// Re-export WorkflowStatus for convenience\nexport type { WorkflowStatus } from '@meetsmore-oss/use-ai-core';\n\n/**\n * Progress update from a workflow execution.\n */\nexport interface WorkflowProgress {\n /** Current status of the workflow */\n status: WorkflowStatus;\n /** Text output from the workflow (if any) */\n text?: string;\n /** Error message (if status is 'error') */\n error?: string;\n /** Tool calls made during workflow execution */\n toolCalls?: Array<{\n toolName: string;\n args: Record<string, unknown>;\n result?: unknown;\n }>;\n}\n\n/**\n * Configuration for triggering a workflow.\n */\nexport interface TriggerWorkflowOptions {\n /** Input data for the workflow */\n inputs: Record<string, any>;\n /** Optional tools that the workflow can call */\n tools?: ToolsDefinition;\n /** Optional callback for progress updates */\n onProgress?: (progress: WorkflowProgress) => void;\n /** Optional callback when workflow completes successfully */\n onComplete?: (result: any) => void;\n /** Optional callback when workflow encounters an error */\n onError?: (error: Error) => void;\n}\n\n/**\n * Result from the useAIWorkflow hook.\n */\nexport interface UseAIWorkflowResult {\n /** Triggers a workflow execution */\n trigger: (options: TriggerWorkflowOptions) => Promise<void>;\n /** Current status of the workflow */\n status: WorkflowStatus;\n /** Accumulated text output from the workflow */\n text: string | null;\n /** Error if workflow failed */\n error: Error | null;\n /** Whether the client is connected to the server */\n connected: boolean;\n}\n\n/**\n * React hook for triggering headless workflows.\n *\n * Workflows are different from chat-based agents:\n * - No conversation history (stateless)\n * - No chat UI involvement\n * - Can use external platforms (Dify, Flowise, etc.)\n * - Still supports tool calls to frontend\n *\n * Use this for button-triggered operations like:\n * - File upload processing\n * - Data transformations\n * - Multi-step background tasks\n * - External workflow integrations\n *\n * @param runner - The runner to use (e.g., 'dify', 'flowise', 'claude')\n * @param workflowId - The workflow identifier\n *\n * @example\n * ```typescript\n * import { useAIWorkflow } from '@meetsmore-oss/use-ai-plugin-workflows-client';\n * import { defineTool, z } from '@meetsmore-oss/use-ai-client';\n *\n * function PDFUploadButton() {\n * const { trigger, status, text } = useAIWorkflow('dify', 'pdf-processor');\n *\n * const insertText = defineTool(\n * 'Insert text into the document',\n * z.object({ text: z.string() }),\n * (input) => {\n * document.body.appendChild(document.createTextNode(input.text));\n * return { success: true };\n * }\n * );\n *\n * const handleUpload = async (file: File) => {\n * const pdfData = await file.arrayBuffer();\n *\n * await trigger({\n * inputs: { file: pdfData },\n * tools: { insertText },\n * onProgress: (progress) => console.log('Progress:', progress),\n * onComplete: (result) => console.log('Completed:', result),\n * onError: (error) => console.error('Error:', error),\n * });\n * };\n *\n * return (\n * <div>\n * <input type=\"file\" onChange={(e) => handleUpload(e.target.files[0])} />\n * {status === 'running' && <p>Processing...</p>}\n * {status === 'completed' && <p>Done! {text}</p>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useAIWorkflow(runner: string, workflowId: string): UseAIWorkflowResult {\n const { connected, client } = useAIContext();\n const [status, setStatus] = useState<WorkflowStatus>('idle');\n const [text, setText] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const currentWorkflowRef = useRef<{\n runId: string;\n threadId: string;\n tools: ToolsDefinition;\n onProgress?: (progress: WorkflowProgress) => void;\n onComplete?: (result: any) => void;\n onError?: (error: Error) => void;\n accumulatedText: string;\n toolCalls: Array<{\n toolName: string;\n args: Record<string, unknown>;\n result?: unknown;\n }>;\n } | null>(null);\n\n const eventListenerIdRef = useRef<string>(`useAIWorkflow-${Math.random().toString(36).substr(2, 9)}`);\n\n const handleWorkflowEvent = useCallback(async (event: AGUIEvent) => {\n const currentWorkflow = currentWorkflowRef.current;\n if (!currentWorkflow) return;\n\n // Only process events for the current workflow\n if (event.type === EventType.RUN_STARTED) {\n const runEvent = event;\n if (runEvent.runId !== currentWorkflow.runId) return;\n }\n\n switch (event.type) {\n case EventType.TEXT_MESSAGE_CONTENT: {\n const textEvent = event as TextMessageContentEvent;\n currentWorkflow.accumulatedText += textEvent.delta;\n setText(currentWorkflow.accumulatedText);\n\n currentWorkflow.onProgress?.({\n status: 'running',\n text: currentWorkflow.accumulatedText,\n toolCalls: currentWorkflow.toolCalls,\n });\n break;\n }\n\n case EventType.TOOL_CALL_END: {\n if (!client) break;\n\n const toolCallEvent = event as ToolCallEndEvent;\n const toolCallId = toolCallEvent.toolCallId;\n\n // Get the accumulated tool call data from the client\n const toolCallData = client.getToolCallData(toolCallId);\n if (!toolCallData) {\n console.error(`[useAIWorkflow] Tool call ${toolCallId} not found`);\n break;\n }\n\n const toolName = toolCallData.name;\n const toolArgs = JSON.parse(toolCallData.args);\n\n console.log(`[useAIWorkflow] Executing tool: ${toolName}`, toolArgs);\n console.log(`[useAIWorkflow] Available tools:`, Object.keys(currentWorkflow.tools));\n\n try {\n // Execute the tool\n const result = await executeDefinedTool(currentWorkflow.tools, toolName, toolArgs);\n\n // Track tool call\n currentWorkflow.toolCalls.push({\n toolName,\n args: toolArgs,\n result,\n });\n\n currentWorkflow.onProgress?.({\n status: 'running',\n text: currentWorkflow.accumulatedText,\n toolCalls: currentWorkflow.toolCalls,\n });\n\n // Send result back to server\n client.sendToolResponse(toolCallId, result);\n } catch (err) {\n console.error('[useAIWorkflow] Tool execution error:', err);\n client.sendToolResponse(toolCallId, {\n error: err instanceof Error ? err.message : 'Unknown error',\n });\n }\n break;\n }\n\n case EventType.RUN_FINISHED: {\n setStatus('completed');\n\n const result = {\n text: currentWorkflow.accumulatedText,\n toolCalls: currentWorkflow.toolCalls,\n };\n\n currentWorkflow.onProgress?.({\n status: 'completed',\n text: currentWorkflow.accumulatedText,\n toolCalls: currentWorkflow.toolCalls,\n });\n\n currentWorkflow.onComplete?.(result);\n\n // Clear workflow ref\n currentWorkflowRef.current = null;\n break;\n }\n\n case EventType.RUN_ERROR: {\n const errorEvent = event as RunErrorEvent;\n const err = new Error(errorEvent.message);\n setError(err);\n setStatus('error');\n\n currentWorkflow.onProgress?.({\n status: 'error',\n error: errorEvent.message,\n text: currentWorkflow.accumulatedText,\n toolCalls: currentWorkflow.toolCalls,\n });\n\n currentWorkflow.onError?.(err);\n\n // Clear workflow ref\n currentWorkflowRef.current = null;\n break;\n }\n }\n }, [client]);\n\n // Register event listener once\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onEvent(eventListenerIdRef.current, handleWorkflowEvent);\n\n return () => {\n unsubscribe();\n };\n }, [client, handleWorkflowEvent]);\n\n const trigger = useCallback(async (options: TriggerWorkflowOptions) => {\n if (!client?.isConnected()) {\n const err = new Error('Not connected to server');\n setError(err);\n options.onError?.(err);\n return;\n }\n\n // Prevent concurrent workflows\n if (currentWorkflowRef.current !== null) {\n const err = new Error('A workflow is already running. Wait for it to complete before triggering a new one.');\n setError(err);\n setStatus('error');\n options.onError?.(err);\n return;\n }\n\n // Reset state\n setStatus('running');\n setError(null);\n setText(null);\n\n const runId = uuidv4();\n const threadId = uuidv4();\n\n // Store workflow context\n currentWorkflowRef.current = {\n runId,\n threadId,\n tools: options.tools || {},\n onProgress: options.onProgress,\n onComplete: options.onComplete,\n onError: options.onError,\n accumulatedText: '',\n toolCalls: [],\n };\n\n // Convert tools to ToolDefinition format\n const toolDefinitions = options.tools ? convertToolsToDefinitions(options.tools) : [];\n\n // Send run_workflow message\n const message: RunWorkflowMessage = {\n type: 'run_workflow',\n data: {\n runner,\n workflowId,\n inputs: options.inputs,\n tools: toolDefinitions,\n runId,\n threadId,\n },\n };\n\n console.log('[useAIWorkflow] Sending run_workflow message:', message);\n\n // Send via socket\n client.send(message);\n\n options.onProgress?.({\n status: 'running',\n });\n }, [client, handleWorkflowEvent, runner, workflowId]);\n\n return {\n trigger,\n status,\n text,\n error,\n connected,\n };\n}\n","export { useAI } from './useAI';\nexport { useAIWorkflow } from './useAIWorkflow';\nexport { UseAIProvider, useAIContext } from './providers/useAIProvider';\nexport { UseAIClient } from './client';\nexport { defineTool, executeDefinedTool, convertToolsToDefinitions } from './defineTool';\nexport { z } from 'zod';\n\n// Theme and strings\nexport {\n defaultStrings,\n defaultTheme,\n useTheme,\n useStrings,\n} from './theme';\nexport type { UseAIStrings, UseAITheme } from './theme';\n\n// Chat UI components\nexport { UseAIChatPanel } from './components/UseAIChatPanel';\nexport type {\n Message,\n UseAIChatPanelStrings,\n UseAIChatPanelTheme,\n UseAIChatPanelProps,\n} from './components/UseAIChatPanel';\nexport { UseAIFloatingChatWrapper, CloseButton } from './components/UseAIFloatingChatWrapper';\nexport { UseAIFloatingButton } from './components/UseAIFloatingButton';\nexport { UseAIChat } from './components/UseAIChat';\nexport type { UseAIChatProps } from './components/UseAIChat';\n\nexport type { UseAIOptions, UseAIResult } from './useAI';\nexport type { UseAIWorkflowResult, TriggerWorkflowOptions, WorkflowProgress } from './useAIWorkflow';\nexport type { UseAIConfig, ToolDefinition, AgentInfo } from './types';\nexport type {\n UseAIContextValue,\n ChatContextValue,\n AgentContextValue,\n CommandContextValue,\n ToolRegistryContextValue,\n PromptsContextValue,\n FloatingButtonProps,\n ChatPanelProps,\n UseAIProviderProps,\n} from './providers/useAIProvider';\nexport type { DefinedTool, ToolsDefinition, ToolOptions } from './defineTool';\n\n// Chat persistence\nexport { LocalStorageChatRepository } from './providers/chatRepository/LocalStorageChatRepository';\nexport { generateChatId, generateMessageId } from './providers/chatRepository/types';\nexport type {\n ChatRepository,\n Chat,\n PersistedMessage,\n PersistedMessageContent,\n PersistedContentPart,\n PersistedTextContent,\n PersistedFileContent,\n CreateChatOptions,\n ListChatsOptions,\n} from './providers/chatRepository/types';\n\n// File upload\nexport { EmbedFileUploadBackend } from './fileUpload/EmbedFileUploadBackend';\nexport { DEFAULT_MAX_FILE_SIZE } from './fileUpload/types';\nexport { useFileUpload } from './hooks/useFileUpload';\nexport type {\n FileUploadBackend,\n FileUploadConfig,\n FileAttachment,\n PersistedFileMetadata,\n} from './fileUpload/types';\nexport type {\n UseFileUploadOptions,\n UseFileUploadReturn,\n DropZoneProps,\n} from './hooks/useFileUpload';\n\n// Slash commands\nexport { LocalStorageCommandRepository } from './commands/LocalStorageCommandRepository';\nexport { generateCommandId, validateCommandName } from './commands/types';\nexport { useSlashCommands } from './hooks/useSlashCommands';\nexport type {\n CommandRepository,\n SavedCommand,\n CreateCommandOptions,\n ListCommandsOptions,\n} from './commands/types';\nexport type {\n UseSlashCommandsOptions,\n UseSlashCommandsReturn,\n InlineSaveProps,\n} from './hooks/useSlashCommands';\n\n// Chat management\nexport { useChatManagement } from './hooks/useChatManagement';\nexport type {\n UseChatManagementOptions,\n UseChatManagementReturn,\n} from './hooks/useChatManagement';\n\n// Agent selection\nexport { useAgentSelection } from './hooks/useAgentSelection';\nexport type {\n UseAgentSelectionOptions,\n UseAgentSelectionReturn,\n} from './hooks/useAgentSelection';\n\n// Command management\nexport { useCommandManagement } from './hooks/useCommandManagement';\nexport type {\n UseCommandManagementOptions,\n UseCommandManagementReturn,\n} from './hooks/useCommandManagement';\n\n// Tool registry\nexport { useToolRegistry } from './hooks/useToolRegistry';\nexport type {\n RegisterToolsOptions,\n UseToolRegistryReturn,\n} from './hooks/useToolRegistry';\n\n// Prompt state\nexport { usePromptState } from './hooks/usePromptState';\nexport type {\n UsePromptStateOptions,\n UsePromptStateReturn,\n} from './hooks/usePromptState';\n\n// Tool stabilization\nexport { useStableTools } from './hooks/useStableTools';\n\n// UI utilities\nexport { useDropdownState } from './hooks/useDropdownState';\nexport type {\n UseDropdownStateOptions,\n UseDropdownStateReturn,\n} from './hooks/useDropdownState';\n"],"mappings":"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","names":["useState","useEffect","useRef","useCallback","useMemo","createContext","useContext","useState","useEffect","useCallback","useRef","createContext","useContext","useState","useRef","useEffect","jsx","jsx","jsxs","useRef","useEffect","jsx","jsxs","jsx","jsxs","MAX_VISIBLE_ITEMS","useRef","useEffect","useState","useRef","useCallback","useEffect","jsx","useState","useRef","useEffect","useCallback","useState","useCallback","useMemo","jsx","jsx","jsxs","useState","useRef","useEffect","Fragment","jsx","jsxs","createContext","useContext","jsx","createContext","useContext","EventType","uuidv4","useState","useCallback","useRef","useEffect","generateMessageId","displayedChatId","useState","useCallback","useEffect","useMemo","useState","useCallback","useRef","useEffect","STORAGE_KEY_PREFIX","STORAGE_INDEX_KEY","useRef","useState","useCallback","useEffect","useState","useCallback","useRef","useMemo","useState","useCallback","useRef","useMemo","useEffect","Fragment","jsx","jsxs","createContext","useState","useRef","useEffect","useCallback","useContext","useRef","useState","useRef","useMemo","useEffect","useCallback","error","useState","useCallback","useRef","useEffect","EventType","uuidv4","useState","useRef","useCallback","useEffect","z"]}
|