@meetsmore-oss/use-ai-client 1.9.0 → 1.9.2
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 +3973 -1254
- package/dist/bundled.js.map +1 -1
- package/dist/index.d.ts +547 -539
- package/dist/index.js +191 -73
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-STF3H6F5.js +0 -13
- package/dist/chunk-STF3H6F5.js.map +0 -1
- package/dist/chunk-UM4UCU4W.js +0 -46
- package/dist/chunk-UM4UCU4W.js.map +0 -1
- package/dist/types-GWPQMSYT.js +0 -9
- package/dist/types-GWPQMSYT.js.map +0 -1
- package/dist/types-RJZTRF3U.js +0 -9
- package/dist/types-RJZTRF3U.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/useAI.ts","../src/providers/useAIProvider.tsx","../src/theme/strings.ts","../src/theme/theme.ts","../src/components/UseAIFloatingButton.tsx","../src/components/UseAIChatPanel.tsx","../src/components/MarkdownContent.tsx","../src/components/Spinner.tsx","../src/components/ProgressBar.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/fileUpload/mimeTypeMatcher.ts","../src/fileUpload/EmbedFileUploadBackend.ts","../src/fileUpload/processAttachments.ts","../src/hooks/useDropdownState.tsx","../src/components/ToolApprovalDialog.tsx","../src/components/UseAIFloatingChatWrapper.tsx","../src/components/UseAIChat.tsx","../src/client.ts","../src/defineTool.ts","../src/providers/chatRepository/LocalStorageChatRepository.ts","../src/hooks/useChatManagement.ts","../src/hooks/useAgentSelection.ts","../src/hooks/useCommandManagement.ts","../src/commands/LocalStorageCommandRepository.ts","../src/hooks/useToolSystem.ts","../src/hooks/usePromptState.ts","../src/hooks/useFeedback.ts","../src/hooks/useServerEvents.ts","../src/types.ts","../src/hooks/useMessageQueue.ts","../src/hooks/useStableTools.ts","../src/useAIWorkflow.ts","../src/index.ts"],"sourcesContent":["import { useState, useEffect, useLayoutEffect, 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, signalReady, registerWaiter, unregisterWaiter } = tools;\n const { update: updatePrompt } = 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 using useLayoutEffect for deterministic timing\n // This runs synchronously after DOM mutations, allowing waitForToolsToStabilize\n // to reliably detect when all components have finished registering.\n useLayoutEffect(() => {\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 // Signal that this component has finished registering its tools\n // This is used by waitForToolsToStabilize to know when all components are ready\n signalReady(hookId.current);\n }\n\n return () => {\n if (stableTools) {\n unregisterTools(hookId.current);\n }\n };\n }, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools, signalReady]);\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, AgentInfo, UseAIForwardedProps } 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, type ToolsDefinition } from '../defineTool';\nimport type { ChatRepository, Chat, ChatMetadata, CreateChatOptions, PersistedMessageContent, PersistedContentPart } from './chatRepository/types';\nimport { LocalStorageChatRepository } from './chatRepository/LocalStorageChatRepository';\nimport type { FileAttachment, FileUploadConfig, FileProcessingState } from '../fileUpload/types';\nimport { processAttachments } from '../fileUpload/processAttachments';\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 { useToolSystem, type RegisterToolsOptions } from '../hooks/useToolSystem';\nimport { usePromptState } from '../hooks/usePromptState';\nimport { useFeedback } from '../hooks/useFeedback';\nimport { useServerEvents } from '../hooks/useServerEvents';\nimport { useMessageQueue, type SendMessageOptions } from '../hooks/useMessageQueue';\nimport { ThemeContext, StringsContext, defaultTheme, defaultStrings } from '../theme';\nimport type { UseAITheme, UseAIStrings } from '../theme';\n\n// ── Context Types ───────────────────────────────────────────────────────────\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: (options?: CreateChatOptions) => 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 * Programmatically send a message to the chat.\n * Throws on failure (e.g., not connected).\n */\n sendMessage: (message: string, options?: SendMessageOptions) => Promise<void>;\n /** Get the current chat object. Metadata is frozen to prevent accidental mutation. */\n get: () => Promise<Chat | null>;\n /**\n * Update metadata for the current chat.\n * @param metadata Metadata to set/merge\n * @param overwrite If true, replaces all metadata instead of merging (default: false)\n */\n updateMetadata: (metadata: ChatMetadata, overwrite?: boolean) => 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 system context (from useToolSystem hook).\n */\nexport interface ToolRegistryContextValue {\n /** Registers tools for a specific component */\n register: (id: string, tools: ToolsDefinition, options?: RegisterToolsOptions) => void;\n /** Unregisters tools for a specific component */\n unregister: (id: string) => void;\n /** Signals that a component has completed registration in useLayoutEffect */\n signalReady: (id: 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 * 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}\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 system (registry, waiters, execution) */\n tools: ToolRegistryContextValue;\n /** Prompt management */\n prompts: PromptsContextValue;\n /** Chat management */\n chat: ChatContextValue;\n /** Agent selection */\n agents: AgentContextValue;\n /** Command management */\n commands: CommandContextValue;\n}\n\n/**\n * React context for UseAI provider state.\n * @internal Exported only for testing purposes. Use {@link useAIContext} 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 signalReady: () => {},\n registerWaiter: () => {},\n unregisterWaiter: () => {},\n },\n prompts: {\n update: () => {},\n },\n chat: {\n currentId: null,\n create: async () => '',\n load: async () => {},\n delete: async () => {},\n list: async () => [],\n clear: async () => {},\n sendMessage: async () => {},\n get: async () => null,\n updateMetadata: 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// ── Component Props ─────────────────────────────────────────────────────────\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 * Provider function for forwarded props (telemetry metadata, MCP headers, etc.).\n * Called before each message is sent. Can be sync or async.\n * Props from this provider are merged with message-level forwardedProps,\n * with message-level taking precedence.\n *\n * @example\n * ```tsx\n * <UseAIProvider\n * serverUrl=\"wss://your-server.com\"\n * forwardedPropsProvider={() => ({\n * mcpHeaders: {\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 * telemetryMetadata: {\n * userId: currentUser.id,\n * tenantId: tenant.id,\n * },\n * })}\n * >\n * <App />\n * </UseAIProvider>\n * ```\n */\n forwardedPropsProvider?: () => UseAIForwardedProps | Promise<UseAIForwardedProps>;\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 * Callback when the chat open state should change.\n * Called by programmatic actions like `sendMessage({ openChat: true })`.\n * Useful when `renderChat=false` and you control the chat panel's visibility externally.\n *\n * @example\n * ```tsx\n * const [sidebarOpen, setSidebarOpen] = useState(false);\n *\n * <UseAIProvider\n * serverUrl=\"ws://localhost:8081\"\n * renderChat={false}\n * onOpenChange={(isOpen) => {\n * // Sync with external sidebar state\n * setSidebarOpen(isOpen);\n * }}\n * >\n * <Sidebar isOpen={sidebarOpen} onClose={() => setSidebarOpen(false)}>\n * <UseAIChat />\n * </Sidebar>\n * </UseAIProvider>\n * ```\n */\n onOpenChange?: (isOpen: boolean) => void;\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// ── Provider Component ──────────────────────────────────────────────────────\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 forwardedPropsProvider,\n fileUploadConfig: fileUploadConfigProp,\n commandRepository,\n renderChat = true,\n theme: customTheme,\n strings: customStrings,\n visibleAgentIds,\n onOpenChange,\n}: UseAIProviderProps) {\n const fileUploadConfig = fileUploadConfigProp === false\n ? undefined\n : (fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG);\n\n const theme = { ...defaultTheme, ...customTheme };\n const strings = { ...defaultStrings, ...customStrings };\n\n // ── Core State ──────────────────────────────────────────────────────────\n\n const [connected, setConnected] = useState(false);\n const [isChatOpen, setIsChatOpen] = useState(false);\n const [messages, setMessages] = useState<Message[]>([]);\n const [fileProcessingState, setFileProcessingState] = useState<FileProcessingState | null>(null);\n\n const handleSetChatOpen = useCallback((open: boolean) => {\n setIsChatOpen(open);\n onOpenChange?.(open);\n }, [onOpenChange]);\n\n const clientRef = useRef<UseAIClient | null>(null);\n const repositoryRef = useRef<ChatRepository>(\n chatRepository || new LocalStorageChatRepository()\n );\n\n // ── Hooks ───────────────────────────────────────────────────────────────\n\n const promptState = usePromptState({\n systemPrompt,\n clientRef,\n connected,\n });\n\n const toolSystem = useToolSystem({\n clientRef,\n buildState: promptState.buildStateFromPrompts,\n });\n\n const chatManagement = useChatManagement({\n repository: repositoryRef.current,\n clientRef,\n messages,\n setMessages,\n connected,\n });\n\n const serverEvents = useServerEvents({\n toolSystem,\n saveAIResponse: chatManagement.saveAIResponse,\n strings,\n });\n\n const feedback = useFeedback({\n clientRef,\n repository: repositoryRef.current,\n getDisplayedChatId: () => chatManagement.displayedChatId,\n setMessages,\n });\n\n const {\n availableAgents,\n defaultAgent,\n selectedAgent,\n setAgent,\n } = useAgentSelection({ clientRef, connected, visibleAgentIds });\n\n const {\n commands,\n refreshCommands,\n saveCommand,\n renameCommand,\n deleteCommand,\n } = useCommandManagement({ repository: commandRepository });\n\n // ── Client Lifecycle ────────────────────────────────────────────────────\n\n // Ref to always call the latest handleServerEvent from the stable subscription\n const handleServerEventRef = useRef(serverEvents.handleServerEvent);\n handleServerEventRef.current = serverEvents.handleServerEvent;\n\n useEffect(() => {\n console.log('[UseAIProvider] Initializing client with serverUrl:', serverUrl);\n const client = new UseAIClient(serverUrl);\n\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 await handleServerEventRef.current(client, event);\n });\n\n clientRef.current = client;\n\n return () => {\n unsubscribeConnection();\n unsubscribe();\n client.disconnect();\n };\n }, [serverUrl]);\n\n // ── Tool Registration Sync ──────────────────────────────────────────────\n\n const lastRegisteredToolsRef = useRef<string>('');\n\n useEffect(() => {\n const client = clientRef.current;\n if (!client || !client.isConnected() || !toolSystem.hasTools) return;\n\n const toolKeys = Object.keys(toolSystem.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(toolSystem.aggregatedTools);\n console.log(`[Provider] Registering ${toolDefinitions.length} tools`);\n client.registerTools(toolDefinitions);\n } catch (err) {\n console.error('Failed to register tools:', err);\n }\n }, [toolSystem.hasTools, toolSystem.aggregatedTools, connected]);\n\n // ── Message Sending ─────────────────────────────────────────────────────\n\n const handleSendMessage = useCallback(async (message: string, attachments?: FileAttachment[], messageForwardedProps?: UseAIForwardedProps) => {\n if (!clientRef.current) return;\n\n serverEvents.clearStreamingText();\n\n const activatedChatId = chatManagement.activatePendingChat();\n const activeChatId = activatedChatId || chatManagement.currentChatId;\n\n serverEvents.streamingChatIdRef.current = activeChatId;\n\n let persistedContent: PersistedMessageContent = message;\n let multimodalContent: MultimodalContent[] | undefined;\n\n if (attachments && attachments.length > 0) {\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 if (activeChatId) {\n await chatManagement.saveUserMessage(activeChatId, persistedContent);\n }\n\n serverEvents.setLoading(true);\n\n try {\n const fileContent = await processAttachments(attachments, {\n getCurrentChat: chatManagement.getCurrentChat,\n backend: fileUploadConfig?.backend,\n transformers: fileUploadConfig?.transformers,\n onFileProgress: (_fileId, state) => {\n setFileProcessingState(state);\n },\n });\n\n multimodalContent = [];\n if (message.trim()) {\n multimodalContent.push({ type: 'text', text: message });\n }\n multimodalContent.push(...fileContent);\n } catch (error) {\n serverEvents.setLoading(false);\n throw error;\n } finally {\n setFileProcessingState(null);\n }\n } else {\n if (activeChatId) {\n await chatManagement.saveUserMessage(activeChatId, persistedContent);\n }\n serverEvents.setLoading(true);\n }\n\n const providerResult = forwardedPropsProvider ? forwardedPropsProvider() : {};\n const providerProps = providerResult instanceof Promise ? await providerResult : providerResult;\n\n const mergedForwardedProps = {\n ...providerProps,\n ...messageForwardedProps,\n };\n\n await clientRef.current.sendPrompt(\n message,\n multimodalContent,\n Object.keys(mergedForwardedProps).length > 0\n ? mergedForwardedProps\n : undefined\n );\n }, [chatManagement, serverEvents, fileUploadConfig, forwardedPropsProvider]);\n\n // ── Message Queue (programmatic sendMessage) ────────────────────────────\n\n const messageQueue = useMessageQueue({\n sendFn: handleSendMessage,\n createNewChat: chatManagement.createNewChat,\n setOpen: handleSetChatOpen,\n connected,\n loading: serverEvents.loading,\n hasPendingApproval: toolSystem.pendingApprovals.length > 0,\n });\n\n // ── Context Values ──────────────────────────────────────────────────────\n\n const value: UseAIContextValue = {\n serverUrl,\n connected,\n client: clientRef.current,\n tools: {\n register: toolSystem.registerTools,\n unregister: toolSystem.unregisterTools,\n signalReady: toolSystem.signalReady,\n registerWaiter: toolSystem.registerWaiter,\n unregisterWaiter: toolSystem.unregisterWaiter,\n },\n prompts: {\n update: promptState.updatePrompt,\n },\n chat: {\n currentId: chatManagement.currentChatId,\n create: chatManagement.createNewChat,\n load: chatManagement.loadChat,\n delete: chatManagement.deleteChat,\n list: chatManagement.listChats,\n clear: chatManagement.clearCurrentChat,\n sendMessage: messageQueue.sendMessage,\n get: chatManagement.getCurrentChat,\n updateMetadata: chatManagement.updateMetadata,\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 // ── Chat UI ─────────────────────────────────────────────────────────────\n\n const effectiveStreamingText = serverEvents.streamingChatIdRef.current === chatManagement.displayedChatId\n ? serverEvents.streamingText : '';\n\n const chatUIContextValue: ChatUIContextValue = {\n connected,\n loading: serverEvents.loading,\n sendMessage: handleSendMessage,\n messages,\n streamingText: effectiveStreamingText,\n suggestions: promptState.aggregatedSuggestions,\n fileUploadConfig,\n fileProcessing: fileProcessingState,\n history: {\n currentId: chatManagement.displayedChatId,\n create: chatManagement.createNewChat,\n load: chatManagement.loadChat,\n delete: chatManagement.deleteChat,\n list: chatManagement.listChats,\n get: chatManagement.getCurrentChat,\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: handleSetChatOpen,\n },\n tools: {\n executing: serverEvents.executingTool,\n pending: {\n tools: toolSystem.pendingApprovals,\n approveAll: toolSystem.approveAll,\n rejectAll: toolSystem.rejectAll,\n },\n },\n feedback: {\n enabled: feedback.enabled,\n submit: feedback.submitFeedback,\n },\n };\n\n const isUIDisabled = CustomButton === null || CustomChat === null;\n const ButtonComponent = isUIDisabled ? null : (CustomButton || UseAIFloatingButton);\n const hasCustomChat = CustomChat !== undefined && CustomChat !== null;\n\n const chatPanelProps = {\n onSendMessage: handleSendMessage,\n messages,\n loading: serverEvents.loading,\n connected,\n streamingText: effectiveStreamingText,\n currentChatId: chatManagement.displayedChatId,\n onNewChat: chatManagement.createNewChat,\n onLoadChat: chatManagement.loadChat,\n onDeleteChat: chatManagement.deleteChat,\n onListChats: chatManagement.listChats,\n suggestions: promptState.aggregatedSuggestions,\n availableAgents,\n defaultAgent,\n selectedAgent,\n onAgentChange: setAgent,\n fileUploadConfig,\n fileProcessing: fileProcessingState,\n commands,\n onSaveCommand: saveCommand,\n onRenameCommand: renameCommand,\n onDeleteCommand: deleteCommand,\n executingTool: serverEvents.executingTool,\n feedbackEnabled: feedback.enabled,\n onFeedback: feedback.submitFeedback,\n pendingApprovals: toolSystem.pendingApprovals,\n onApproveToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.approveAll : undefined,\n onRejectToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.rejectAll : undefined,\n };\n\n const renderDefaultChat = () => {\n if (isUIDisabled) return null;\n return (\n <UseAIFloatingChatWrapper isOpen={isChatOpen} onClose={() => handleSetChatOpen(false)}>\n <UseAIChatPanel\n {...chatPanelProps}\n closeButton={<CloseButton onClick={() => handleSetChatOpen(false)} />}\n />\n </UseAIFloatingChatWrapper>\n );\n };\n\n const renderCustomChat = () => {\n if (!CustomChat) return null;\n return (\n <CustomChat\n isOpen={isChatOpen}\n onClose={() => handleSetChatOpen(false)}\n onSendMessage={handleSendMessage}\n messages={messages}\n loading={serverEvents.loading}\n connected={connected}\n suggestions={promptState.aggregatedSuggestions}\n availableAgents={availableAgents}\n defaultAgent={defaultAgent}\n selectedAgent={selectedAgent}\n onAgentChange={setAgent}\n />\n );\n };\n\n const renderBuiltInChat = () => {\n if (!renderChat) return null;\n return (\n <>\n {ButtonComponent && (\n <ButtonComponent\n onClick={() => handleSetChatOpen(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// ── Context Hook ────────────────────────────────────────────────────────────\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 */\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","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 /** Loading indicator text */\n thinking: 'Thinking',\n /** File processing indicator text (shown during file transformation like OCR) */\n processingFile: 'Processing file...',\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 // Tool execution status\n toolExecution: {\n /** Fallback messages when no tool title is provided (one randomly selected) */\n fallbackMessages: ['Working', 'Processing', 'Thinking'],\n },\n\n // Tool approval dialog\n toolApproval: {\n /** Title shown in the approval dialog */\n title: 'Confirmation Required',\n /** Message shown in the approval dialog. {toolName} is replaced with tool name. */\n message: '\"{toolName}\" is waiting for your approval.',\n /** Message shown when multiple tools are awaiting approval. {count} is replaced with number. */\n batchMessage: '{count} actions are waiting for your approval.',\n /** Label for approve button */\n approve: 'Allow',\n /** Label for approve all button (batch mode) */\n approveAll: 'Allow All',\n /** Label for reject button */\n reject: 'Deny',\n /** Label for reject all button (batch mode) */\n rejectAll: 'Deny All',\n /** Label for showing tool arguments */\n showDetails: 'Show details',\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, FeedbackValue, ToolAnnotations } from '../types';\nimport type { FileAttachment, FileUploadConfig, FileProcessingState } 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';\nimport { ToolApprovalDialog } from './ToolApprovalDialog';\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 /** Langfuse trace ID for feedback tracking (only for assistant messages) */\n traceId?: string;\n /** User feedback on this message (only for assistant messages) */\n feedback?: FeedbackValue;\n}\n\n/**\n * Props for the FeedbackButton component.\n */\ninterface FeedbackButtonProps {\n /** The type of feedback this button represents */\n type: 'upvote' | 'downvote';\n /** Whether this feedback type is currently selected */\n isSelected: boolean;\n /** Callback when clicked */\n onClick: () => void;\n /** Color when selected */\n selectedColor: string;\n /** Color when not selected */\n unselectedColor: string;\n}\n\n/**\n * Thumbs up/down feedback button with pop animation.\n */\nfunction FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedColor }: FeedbackButtonProps) {\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const handleClick = () => {\n // Pop animation only on select, not de-select\n if (!isSelected && buttonRef.current) {\n buttonRef.current.style.transform = 'scale(1.3)';\n setTimeout(() => {\n if (buttonRef.current) {\n buttonRef.current.style.transform = 'scale(1)';\n }\n }, 150);\n }\n onClick();\n };\n\n const thumbsUpPath = \"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\";\n const thumbsDownPath = \"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\";\n\n return (\n <button\n ref={buttonRef}\n data-testid={`feedback-${type}`}\n onClick={handleClick}\n title={type === 'upvote' ? 'Good response' : 'Poor response'}\n style={{\n background: 'transparent',\n border: 'none',\n padding: '4px',\n cursor: 'pointer',\n color: isSelected ? selectedColor : unselectedColor,\n opacity: isSelected ? 1 : 0.5,\n transition: 'all 0.15s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '4px',\n transform: 'scale(1)',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (!isSelected) {\n e.currentTarget.style.opacity = '0.8';\n e.currentTarget.style.color = selectedColor;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (!isSelected) {\n e.currentTarget.style.opacity = '0.5';\n e.currentTarget.style.color = unselectedColor;\n }\n }}\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill={isSelected ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d={type === 'upvote' ? thumbsUpPath : thumbsDownPath} />\n </svg>\n </button>\n );\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 /** Gets the current chat */\n onGetChat?: () => Promise<Chat | null>;\n suggestions?: string[];\n availableAgents?: AgentInfo[];\n defaultAgent?: string | null;\n selectedAgent?: string | null;\n onAgentChange?: (agentId: string | null) => void;\n fileUploadConfig?: FileUploadConfig;\n /** File processing state for send-time transformations (e.g., OCR) */\n fileProcessing?: FileProcessingState | null;\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 /** Currently executing tool info for status display */\n executingTool?: { displayText: string } | null;\n /** Whether feedback buttons are enabled (requires Langfuse on server) */\n feedbackEnabled?: boolean;\n /** Callback when user submits feedback on a message */\n onFeedback?: (messageId: string, traceId: string, feedback: FeedbackValue) => void;\n /** Pending tool approvals awaiting user confirmation */\n pendingApprovals?: Array<{\n toolCallId: string;\n toolCallName: string;\n toolCallArgs: Record<string, unknown>;\n annotations?: ToolAnnotations;\n }>;\n /** Callback to approve all pending tool calls */\n onApproveToolCall?: () => void;\n /** Callback to reject all pending tool calls */\n onRejectToolCall?: (reason?: string) => void;\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 onGetChat,\n suggestions,\n availableAgents,\n defaultAgent,\n selectedAgent,\n onAgentChange,\n fileUploadConfig,\n fileProcessing,\n commands = [],\n onSaveCommand,\n onRenameCommand,\n onDeleteCommand,\n closeButton,\n executingTool,\n feedbackEnabled,\n onFeedback,\n pendingApprovals = [],\n onApproveToolCall,\n onRejectToolCall,\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 const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Message hover state for save button\n const [hoveredMessageId, setHoveredMessageId] = useState<string | null>(null);\n\n // File upload hook - includes processing state for transformation progress\n const {\n attachments,\n fileError,\n enabled: fileUploadEnabled,\n acceptedTypes,\n processingState: fileProcessingState,\n fileInputRef,\n removeAttachment,\n clearAttachments,\n openFilePicker,\n handleFileInputChange,\n getDropZoneProps,\n DropZoneOverlay,\n } = useFileUpload({\n getCurrentChat: onGetChat ?? (async () => null),\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 const maxTextareaHeight = 160;\n\n // Auto-resize textarea based on content\n useEffect(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n // Reset to single row to measure actual content height\n textarea.style.height = 'auto';\n\n // Calculate new height based on scrollHeight (clamped to max)\n const newHeight = Math.min(textarea.scrollHeight, maxTextareaHeight);\n textarea.style.height = `${newHeight}px`;\n }, [input]);\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 {/* Feedback buttons - only for assistant messages with traceId */}\n {message.role === 'assistant' && message.traceId && feedbackEnabled && onFeedback && (\n <div\n data-testid=\"feedback-buttons\"\n style={{\n display: 'flex',\n gap: '4px',\n marginTop: '4px',\n padding: '0 4px',\n }}\n >\n <FeedbackButton\n type=\"upvote\"\n isSelected={message.feedback === 'upvote'}\n onClick={() => {\n const newFeedback = message.feedback === 'upvote' ? null : 'upvote';\n onFeedback(message.id, message.traceId!, newFeedback);\n }}\n selectedColor={theme.primaryColor}\n unselectedColor={theme.secondaryTextColor}\n />\n <FeedbackButton\n type=\"downvote\"\n isSelected={message.feedback === 'downvote'}\n onClick={() => {\n const newFeedback = message.feedback === 'downvote' ? null : 'downvote';\n onFeedback(message.id, message.traceId!, newFeedback);\n }}\n selectedColor={theme.errorTextColor}\n unselectedColor={theme.secondaryTextColor}\n />\n </div>\n )}\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 ) : fileProcessing && fileProcessing.status === 'processing' ? (\n <div>\n <span style={{ opacity: 0.6 }}>{strings.input.processingFile}</span>\n {fileProcessing.progress != null && (\n <>\n <span style={{ opacity: 0.6, marginLeft: '4px' }}>\n {Math.round(fileProcessing.progress)}%\n </span>\n <div style={{\n marginTop: '6px',\n height: '4px',\n borderRadius: '2px',\n background: theme.borderColor,\n overflow: 'hidden',\n }}>\n <div style={{\n height: '100%',\n width: `${fileProcessing.progress}%`,\n borderRadius: '2px',\n background: theme.primaryColor,\n transition: 'width 0.3s ease',\n }} />\n </div>\n </>\n )}\n {fileProcessing.progress == null && (\n <span className=\"dots\" style={{ marginLeft: '4px' }}>...</span>\n )}\n </div>\n ) : (\n <span className=\"dots\" style={{ opacity: 0.6 }}>...</span>\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 processingState={fileProcessingState.get(attachment.id)}\n />\n ))}\n </div>\n )}\n\n {/* Hidden file input - always rendered */}\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n data-testid=\"file-input\"\n style={{ display: 'none' }}\n onChange={handleFileInputChange}\n accept={acceptedTypes?.join(',')}\n />\n\n {/* Tool approval dialog - replaces input when pending */}\n {pendingApprovals.length > 0 && onApproveToolCall && onRejectToolCall ? (\n <ToolApprovalDialog\n toolCallName={pendingApprovals[0].toolCallName}\n toolCallArgs={pendingApprovals[0].toolCallArgs}\n annotations={pendingApprovals[0].annotations}\n toolCount={pendingApprovals.length}\n pendingTools={pendingApprovals}\n onApprove={onApproveToolCall}\n onReject={onRejectToolCall}\n theme={theme}\n strings={strings}\n />\n ) : (\n /* Input container - single border around everything */\n <div\n style={{\n border: `1px solid ${theme.borderColor}`,\n borderRadius: '12px',\n background: theme.backgroundColor,\n overflow: 'hidden',\n position: 'relative',\n }}\n >\n {/* Command Autocomplete */}\n {slashCommands.AutocompleteComponent}\n\n {/* Textarea area */}\n <textarea\n ref={textareaRef}\n data-testid=\"chat-input\"\n className=\"chat-input\"\n value={input}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n placeholder={\n !connected\n ? strings.input.connectingPlaceholder\n : loading\n ? `${executingTool?.displayText ?? strings.input.thinking}...`\n : strings.input.placeholder\n }\n disabled={!connected || loading || pendingApprovals.length > 0}\n rows={1}\n style={{\n width: '100%',\n padding: '10px 14px 6px',\n border: 'none',\n fontSize: '14px',\n lineHeight: '1.4',\n resize: 'none',\n maxHeight: `${maxTextareaHeight}px`,\n fontFamily: 'inherit',\n outline: 'none',\n background: 'transparent',\n overflowY: 'auto',\n boxSizing: 'border-box',\n }}\n />\n\n {/* Bottom toolbar - fixed */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '4px 8px',\n }}\n >\n {/* Left side - file picker */}\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n {fileUploadEnabled && (\n <button\n data-testid=\"file-picker-button\"\n onClick={openFilePicker}\n disabled={!connected || loading || pendingApprovals.length > 0}\n style={{\n padding: '4px',\n background: 'transparent',\n border: `1px solid ${theme.borderColor}`,\n borderRadius: '50%',\n cursor: connected && !loading && pendingApprovals.length === 0 ? 'pointer' : 'not-allowed',\n color: theme.secondaryTextColor,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '28px',\n height: '28px',\n transition: 'all 0.15s',\n opacity: connected && !loading && pendingApprovals.length === 0 ? 1 : 0.5,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (connected && !loading && pendingApprovals.length === 0) {\n e.currentTarget.style.color = theme.primaryColor;\n e.currentTarget.style.borderColor = theme.primaryColor;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.color = theme.secondaryTextColor;\n e.currentTarget.style.borderColor = theme.borderColor;\n }}\n title={strings.fileUpload.attachFiles}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n </button>\n )}\n </div>\n\n {/* Right side - send button */}\n <button\n data-testid=\"chat-send-button\"\n className=\"chat-send-button\"\n onClick={handleSend}\n disabled={!connected || loading || pendingApprovals.length > 0 || (!input.trim() && attachments.length === 0)}\n style={{\n padding: '6px',\n background: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0)\n ? theme.primaryGradient\n : theme.buttonDisabledBackground,\n color: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0) ? 'white' : theme.secondaryTextColor,\n border: 'none',\n borderRadius: '50%',\n cursor: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0) ? 'pointer' : 'not-allowed',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '32px',\n height: '32px',\n transition: 'all 0.2s',\n }}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"5\" />\n <polyline points=\"5 12 12 5 19 12\" />\n </svg>\n </button>\n </div>\n </div>\n )}\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';\n\n/**\n * Props for the Spinner component.\n */\nexport interface SpinnerProps {\n /** Size of the spinner in pixels (default: 16) */\n size?: number;\n /** Color of the spinner (default: currentColor) */\n color?: string;\n /** Color of the track behind the spinner (default: same as color with 0.25 opacity) */\n trackColor?: string;\n /** Stroke width (default: 2) */\n strokeWidth?: number;\n}\n\n/**\n * A circular spinner component for indeterminate progress.\n * Matches the visual style of CircularProgress.\n */\nexport function Spinner({\n size = 16,\n color = 'currentColor',\n trackColor,\n strokeWidth = 2,\n}: SpinnerProps) {\n const radius = 10;\n const circumference = 2 * Math.PI * radius;\n\n return (\n <svg\n data-testid=\"spinner\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n style={{\n animation: 'use-ai-spin 1s linear infinite',\n }}\n >\n <style>\n {`\n @keyframes use-ai-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `}\n </style>\n {/* Background track */}\n <circle\n cx=\"12\"\n cy=\"12\"\n r={radius}\n fill=\"none\"\n stroke={trackColor || color}\n strokeWidth={strokeWidth}\n opacity={trackColor ? 1 : 0.25}\n />\n {/* Spinning arc (25% of circle) */}\n <circle\n cx=\"12\"\n cy=\"12\"\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n strokeDashoffset={circumference * 0.75}\n style={{\n transformOrigin: 'center',\n }}\n />\n </svg>\n );\n}\n","import React from 'react';\n\n/**\n * Props for the CircularProgress component.\n */\nexport interface ProgressBarProps {\n /** Progress value from 0 to 100 */\n progress: number;\n /** Size of the progress indicator in pixels (default: 16) */\n size?: number;\n /** Color of the progress fill (default: currentColor) */\n color?: string;\n /** Color of the track behind the progress (default: same as color with 0.25 opacity) */\n trackColor?: string;\n /** Stroke width (default: 2) */\n strokeWidth?: number;\n}\n\n/**\n * A circular progress indicator component for determinate progress (0-100%).\n * Matches the visual style of Spinner.\n */\nexport function ProgressBar({\n progress,\n size = 16,\n color = 'currentColor',\n trackColor,\n strokeWidth = 2,\n}: ProgressBarProps) {\n const clampedProgress = Math.min(100, Math.max(0, progress));\n const radius = 10;\n const circumference = 2 * Math.PI * radius;\n const strokeDashoffset = circumference * (1 - clampedProgress / 100);\n\n return (\n <svg\n data-testid=\"progress-bar\"\n role=\"progressbar\"\n aria-valuenow={clampedProgress}\n aria-valuemin={0}\n aria-valuemax={100}\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n style={{\n transform: 'rotate(-90deg)',\n }}\n >\n {/* Background track */}\n <circle\n cx=\"12\"\n cy=\"12\"\n r={radius}\n fill=\"none\"\n stroke={trackColor || color}\n strokeWidth={strokeWidth}\n opacity={trackColor ? 1 : 0.25}\n />\n {/* Progress arc */}\n <circle\n cx=\"12\"\n cy=\"12\"\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n strokeDashoffset={strokeDashoffset}\n style={{\n transition: 'stroke-dashoffset 0.2s ease',\n }}\n />\n </svg>\n );\n}\n","import React from 'react';\nimport type { FileAttachment, FileProcessingState } from '../fileUpload/types';\nimport { useTheme } from '../theme';\nimport { Spinner } from './Spinner';\nimport { ProgressBar } from './ProgressBar';\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 /** Processing state for this file (for transformation progress) */\n processingState?: FileProcessingState;\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 * - Shows processing overlay with spinner or progress bar\n */\nexport function FileChip({ attachment, onRemove, disabled, processingState }: FileChipProps) {\n const theme = useTheme();\n const { file, preview } = attachment;\n const isImage = file.type.startsWith('image/');\n const isProcessing = processingState?.status === 'processing';\n const hasError = processingState?.status === 'error';\n const progress = processingState?.progress;\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 position: 'relative',\n opacity: isProcessing ? 0.7 : 1,\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 || isProcessing}\n style={{\n background: 'transparent',\n border: 'none',\n padding: '2px 4px',\n cursor: disabled || isProcessing ? 'not-allowed' : 'pointer',\n color: theme.placeholderTextColor,\n fontSize: '16px',\n lineHeight: 1,\n borderRadius: '4px',\n transition: 'all 0.15s',\n opacity: disabled || isProcessing ? 0.5 : 1,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (!disabled && !isProcessing) {\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\n {/* Processing overlay */}\n {isProcessing && (\n <div\n data-testid=\"file-chip-processing\"\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'rgba(255, 255, 255, 0.7)',\n borderRadius: 'inherit',\n }}\n >\n {progress !== undefined ? (\n <ProgressBar progress={progress} size={16} color={theme.secondaryTextColor} />\n ) : (\n <Spinner size={16} color={theme.secondaryTextColor} />\n )}\n </div>\n )}\n\n {/* Error indicator */}\n {hasError && (\n <div\n data-testid=\"file-chip-error\"\n style={{\n position: 'absolute',\n bottom: '-2px',\n right: '-2px',\n width: '12px',\n height: '12px',\n borderRadius: '50%',\n background: '#ef4444',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '8px',\n color: 'white',\n fontWeight: 'bold',\n }}\n >\n !\n </div>\n )}\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, FileProcessingState, FileTransformerContext, FileTransformerMap } from '../fileUpload/types';\nimport type { Chat } from '../providers/chatRepository/types';\nimport { DEFAULT_MAX_FILE_SIZE } from '../fileUpload/types';\nimport { findTransformerPattern } from '../fileUpload/mimeTypeMatcher';\nimport { getTransformedContent } from '../fileUpload/processAttachments';\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 /** Function to get the current chat (for transformer context) */\n getCurrentChat: () => Promise<Chat | null>;\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 /** Processing state for each file (by attachment ID) */\n processingState: Map<string, FileProcessingState>;\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 getCurrentChat,\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 [processingState, setProcessingState] = useState<Map<string, FileProcessingState>>(new Map());\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 const transformers = config?.transformers;\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 setProcessingState(new Map());\n }, [resetDependency]);\n\n /**\n * Runs a transformer for a file attachment.\n * Updates processing state and attachment with transformed content.\n * Uses centralized cache to avoid re-transforming the same file.\n */\n const runTransformer = useCallback(async (\n attachmentId: string,\n file: File,\n transformerKey: string\n ) => {\n const transformer = transformers?.[transformerKey];\n if (!transformer) return;\n\n // Set initial processing state\n setProcessingState(prev => new Map(prev).set(attachmentId, { status: 'processing' }));\n\n try {\n // Get current chat for context\n const chat = await getCurrentChat();\n const context: FileTransformerContext = { chat };\n\n const [transformedContent] = await getTransformedContent([file], transformer, context, (progress) => {\n setProcessingState(prev => new Map(prev).set(attachmentId, {\n status: 'processing',\n progress,\n }));\n });\n\n // Update attachment with transformed content\n setAttachments(prev => prev.map(a =>\n a.id === attachmentId ? { ...a, transformedContent } : a\n ));\n\n // Mark as done\n setProcessingState(prev => new Map(prev).set(attachmentId, { status: 'done' }));\n } catch (error) {\n console.error(`[useFileUpload] Transformation failed for ${file.name}:`, error);\n setProcessingState(prev => new Map(prev).set(attachmentId, { status: 'error' }));\n }\n }, [getCurrentChat, transformers]);\n\n /**\n * Validates and adds files to attachments.\n * If a transformer matches the file type, transformation starts immediately.\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 // Create attachment\n const attachmentId = uuidv4();\n const attachment: FileAttachment = {\n id: attachmentId,\n file,\n preview,\n };\n\n // Add to attachments\n setAttachments(prev => [...prev, attachment]);\n\n // Check for transformer and start transformation immediately\n const transformerKey = findTransformerPattern(file.type, transformers);\n if (transformerKey) {\n // Run transformer in background (don't await - let it update state as it progresses)\n runTransformer(attachmentId, file, transformerKey);\n }\n }\n }, [maxFileSize, acceptedTypes, strings, transformers, runTransformer]);\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 setProcessingState(prev => {\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n /**\n * Clears all attachments.\n */\n const clearAttachments = useCallback(() => {\n setAttachments([]);\n setProcessingState(new Map());\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 processingState,\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","import type { Chat } from '../providers/chatRepository/types';\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 * Transformed content (if a transformer matched this file's MIME type).\n * Populated asynchronously after attachment - check processingState for status.\n */\n transformedContent?: 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 * Context provided to file transformers.\n */\nexport interface FileTransformerContext {\n /** The current chat (includes metadata) */\n chat: Chat | null;\n}\n\n/**\n * A transformer that converts files into string representations for the AI.\n *\n * Receives all files that were matched to this transformer instance\n * and returns one string per file in the same order.\n *\n * @example\n * ```typescript\n * const pdfTransformer: FileTransformer = {\n * transform: async (files, context) =>\n * Promise.all(files.map(f => extractText(f))),\n * };\n * ```\n */\nexport interface FileTransformer {\n /**\n * Transform files into string representations for the AI.\n *\n * @param files - The files to transform (all matched to this transformer instance)\n * @param context - Context including the current chat and its metadata\n * @param onProgress - Optional callback for reporting progress (0-100).\n * If called, UI shows progress bar; otherwise shows spinner.\n * @returns One string per input file, in the same order\n * @throws If transformation fails\n */\n transform(\n files: File[],\n context: FileTransformerContext,\n onProgress?: (progress: number) => void\n ): Promise<string[]>;\n}\n\n/**\n * Map of MIME type patterns to transformers.\n *\n * Keys are MIME type patterns:\n * - Exact match: 'application/pdf'\n * - Partial wildcard: 'image/*'\n * - Global wildcard: '*' or '*\\/*'\n *\n * When multiple patterns match, the most specific one wins:\n * 1. Exact match (e.g., 'application/pdf')\n * 2. Partial wildcard (e.g., 'image/*')\n * 3. Global wildcard ('*' or '*\\/*')\n */\nexport type FileTransformerMap = Record<string, FileTransformer>;\n\n/**\n * Status of file processing during send.\n */\nexport type FileProcessingStatus = 'idle' | 'processing' | 'done' | 'error';\n\n/**\n * Processing state for a file attachment.\n */\nexport interface FileProcessingState {\n status: FileProcessingStatus;\n /** Progress 0-100, or undefined for indeterminate (spinner) */\n progress?: number;\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 * Map of MIME type patterns to transformers.\n * Files matching a transformer pattern will be converted to text\n * before being sent to the AI.\n */\n transformers?: FileTransformerMap;\n}\n","import type { FileTransformerMap } from './types';\n\n/**\n * Check if a MIME type matches a pattern.\n * Supports exact match and wildcard patterns ending with '*'.\n *\n * Examples:\n * - 'application/pdf' matches 'application/pdf' (exact)\n * - 'image/png' matches 'image/*' (partial wildcard)\n * - 'text/plain' matches '*' (global wildcard)\n * - 'text/plain' matches '*\\/*' (global wildcard)\n */\nexport function matchesMimeType(mimeType: string, pattern: string): boolean {\n // Exact match\n if (!pattern.includes('*')) {\n return mimeType === pattern;\n }\n\n // Wildcard match: convert pattern to regex\n // 'image/*' -> /^image\\/.*$/\n // '*' -> /^.*$/\n // '*/*' -> /^.*\\/.*$/\n const regexPattern = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // Escape regex special chars (except *)\n .replace(/\\*/g, '.*'); // Convert * to .*\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(mimeType);\n}\n\n/**\n * Find the most specific transformer pattern key for a MIME type.\n * Returns the pattern string (e.g., 'application/pdf', 'image/*') or undefined.\n *\n * Specificity rules:\n * 1. Exact match (no wildcard) always wins\n * 2. Among wildcard patterns, longer pattern = more specific\n *\n * Example for 'image/png':\n * - 'image/png' (exact, wins)\n * - 'image/*' (length 7, second)\n * - '*' (length 1, last)\n */\nexport function findTransformerPattern(\n mimeType: string,\n transformers: FileTransformerMap | undefined\n): string | undefined {\n if (!transformers) {\n return undefined;\n }\n\n let bestKey: string | undefined;\n let bestIsExact = false;\n let bestLength = -1;\n\n for (const pattern of Object.keys(transformers)) {\n if (!matchesMimeType(mimeType, pattern)) {\n continue;\n }\n\n const isExact = !pattern.includes('*');\n\n // Exact match always wins over wildcard\n if (isExact && !bestIsExact) {\n bestKey = pattern;\n bestIsExact = true;\n bestLength = pattern.length;\n continue;\n }\n\n // If both are exact or both are wildcard, longer pattern wins\n if (isExact === bestIsExact && pattern.length > bestLength) {\n bestKey = pattern;\n bestLength = pattern.length;\n }\n }\n\n return bestKey;\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 type { MultimodalContent } from '@meetsmore-oss/use-ai-core';\nimport type {\n FileAttachment,\n FileUploadBackend,\n FileTransformerMap,\n FileTransformer,\n FileTransformerContext,\n FileProcessingState,\n} from './types';\nimport type { Chat } from '../providers/chatRepository/types';\nimport { findTransformerPattern } from './mimeTypeMatcher';\nimport { EmbedFileUploadBackend } from './EmbedFileUploadBackend';\n\n/**\n * Group items by a key derived from each item.\n * Based on the Map.groupBy proposal polyfill.\n */\nfunction groupBy<K, V>(items: Iterable<V>, keyFn: (item: V) => K): Map<K, V[]> {\n const map = new Map<K, V[]>();\n for (const item of items) {\n const key = keyFn(item);\n const list = map.get(key);\n list ? list.push(item) : map.set(key, [item]);\n }\n return map;\n}\n\n/**\n * Configuration for processing file attachments.\n */\nexport interface ProcessAttachmentsConfig {\n /** Function to get the current chat (for transformer context) */\n getCurrentChat: () => Promise<Chat | null>;\n /** Backend for converting files to URLs (default: EmbedFileUploadBackend) */\n backend?: FileUploadBackend;\n /** Map of MIME type patterns to transformers */\n transformers?: FileTransformerMap;\n /** Called when a file's processing state changes */\n onFileProgress?: (fileId: string, state: FileProcessingState) => void;\n}\n\n/**\n * In-memory cache for transformed file content.\n * Keyed by a SHA-256 hash of the file group identity.\n */\nconst transformationCache = new Map<string, string[]>();\n\n/**\n * Generate a cache key fragment for a single file based on its identity.\n */\nfunction getFileCacheKey(file: File): string {\n return `${file.name}:${file.size}:${file.lastModified}`;\n}\n\n/**\n * Hash a group of files into a fixed-length cache key.\n * Each file is identified by name, size, and lastModified, then the\n * combined string is hashed with SHA-256 to keep key size constant\n * regardless of how many files are in the group.\n *\n * Order matters: [A, B] and [B, A] produce different cache keys because\n * transformer results are positional (results[i] corresponds to files[i]).\n *\n * @example\n * // Input: [\"report.pdf:1024:1700000000\", \"scan.pdf:2048:1700000001\"]\n * // Hashed: \"a3f1...b7c2\" (64-char hex string)\n */\nasync function hashGroupCacheKey(files: File[]): Promise<string> {\n const raw = files.map(getFileCacheKey).join(', ');\n const data = new TextEncoder().encode(raw);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Get transformed content for files, using cache when possible.\n * Results are cached per group (same set of files in the same order = cache hit).\n *\n * @param files - The files to transform (must all belong to the same transformer)\n * @param transformer - The transformer to use\n * @param context - Context for the transformer (including current chat)\n * @param onProgress - Optional progress callback (0-100)\n * @returns Transformer results (one or more strings, depending on the transformer)\n */\nexport async function getTransformedContent(\n files: File[],\n transformer: FileTransformer,\n context: FileTransformerContext,\n onProgress?: (progress: number) => void\n): Promise<string[]> {\n if (files.length === 0) {\n return [];\n }\n\n const cacheKey = await hashGroupCacheKey(files);\n const cached = transformationCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const results = await transformer.transform(files, context, onProgress);\n\n transformationCache.set(cacheKey, results);\n\n return results;\n}\n\n/**\n * Convert a single attachment to a multimodal content part.\n */\nasync function toContentPart(\n attachment: FileAttachment,\n backend: FileUploadBackend\n): Promise<MultimodalContent> {\n // Pre-transformed content (transformation at attach time)\n if (attachment.transformedContent !== undefined) {\n return {\n type: 'transformed_file',\n text: attachment.transformedContent,\n originalFile: {\n name: attachment.file.name,\n mimeType: attachment.file.type,\n size: attachment.file.size,\n },\n };\n }\n\n // No transformer — use URL encoding (fast, no progress needed)\n const url = await backend.prepareForSend(attachment.file);\n\n if (attachment.file.type.startsWith('image/')) {\n return { type: 'image', url };\n }\n\n return {\n type: 'file',\n url,\n mimeType: attachment.file.type,\n name: attachment.file.name,\n };\n}\n\n/**\n * Process file attachments into multimodal content for AI.\n * Handles transformation (with caching) or URL encoding.\n *\n * Files matching the same transformer are grouped and passed together\n * to `transformer.transform()`.\n *\n * @param attachments - The file attachments to process\n * @param config - Processing configuration\n * @returns Array of multimodal content parts\n * @throws On any processing error - caller should handle and show to user\n *\n * @example\n * ```typescript\n * const content = await processAttachments(attachments, {\n * transformers: { 'application/pdf': pdfTransformer },\n * onFileProgress: (id, state) => setProgress(prev => new Map(prev).set(id, state)),\n * });\n * ```\n */\nexport async function processAttachments(\n attachments: FileAttachment[],\n config: ProcessAttachmentsConfig\n): Promise<MultimodalContent[]> {\n const { getCurrentChat, backend = new EmbedFileUploadBackend(), transformers = {}, onFileProgress } = config;\n const contentParts: MultimodalContent[] = [];\n\n // Get current chat once for all transformers\n const chat = await getCurrentChat();\n const context: FileTransformerContext = { chat };\n\n // Group attachments by transformer pattern key (null = no transformer)\n const groups = groupBy(attachments, (attachment) =>\n attachment.transformedContent !== undefined\n ? null\n : findTransformerPattern(attachment.file.type, transformers) ?? null\n );\n\n for (const [key, groupAttachments] of groups) {\n // No transformer matched — convert directly (fast, no progress needed)\n if (key === null) {\n const parts = await Promise.all(\n groupAttachments.map((a) => toContentPart(a, backend))\n );\n contentParts.push(...parts);\n continue;\n }\n\n // Run the matching transformer for this group\n const transformer = transformers[key];\n const files = groupAttachments.map((a) => a.file);\n groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: 'processing' }));\n\n try {\n const results = await getTransformedContent(\n files,\n transformer,\n context,\n (progress) => {\n groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: 'processing', progress }));\n }\n );\n\n // Build content parts from transformer results and notify completion\n results.forEach((text, i) => {\n contentParts.push({\n type: 'transformed_file',\n text,\n originalFile: {\n name: files[i].name,\n mimeType: files[i].type,\n size: files[i].size,\n },\n });\n onFileProgress?.(groupAttachments[i].id, { status: 'done' });\n });\n } catch (error) {\n groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: 'error' }));\n throw error;\n }\n }\n\n return contentParts;\n}\n\n/**\n * Clear the transformation cache.\n * Useful for testing or when memory needs to be freed.\n */\nexport function clearTransformationCache(): void {\n transformationCache.clear();\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, { useState } from 'react';\nimport type { ToolAnnotations } from '../types';\nimport type { UseAITheme, UseAIStrings } from '../theme';\n\n/**\n * A single pending tool approval item.\n */\nexport interface PendingToolItem {\n toolCallId: string;\n toolCallName: string;\n toolCallArgs: Record<string, unknown>;\n annotations?: ToolAnnotations;\n /** Optional message explaining why approval is needed (runtime approval) */\n message?: string;\n}\n\n/**\n * Props for the ToolApprovalDialog component.\n */\nexport interface ToolApprovalDialogProps {\n /** The name of the tool requesting approval (first tool in batch) */\n toolCallName: string;\n /** The arguments passed to the tool (first tool in batch) */\n toolCallArgs: Record<string, unknown>;\n /** Optional tool annotations (e.g., title for display) */\n annotations?: ToolAnnotations;\n /** Number of tool calls awaiting approval (for batch mode) */\n toolCount?: number;\n /** All pending tools (for batch mode details view) */\n pendingTools?: PendingToolItem[];\n /** Callback when user approves the tool execution (all tools in batch) */\n onApprove: () => void;\n /** Callback when user rejects the tool execution (all tools in batch) */\n onReject: (reason?: string) => void;\n /** Theme configuration for styling */\n theme: UseAITheme;\n /** Localized strings for UI text */\n strings: UseAIStrings;\n}\n\n/**\n * Confirmation dialog for destructive tool calls.\n * Replaces the chat input area when approval is needed.\n */\nexport function ToolApprovalDialog({\n toolCallName,\n toolCallArgs,\n annotations,\n toolCount = 1,\n pendingTools = [],\n onApprove,\n onReject,\n theme,\n strings,\n}: ToolApprovalDialogProps) {\n const [showDetails, setShowDetails] = useState(false);\n\n const displayName = annotations?.title || toolCallName;\n const isBatch = toolCount > 1;\n\n // Check if any pending tool has a runtime approval message\n const runtimeMessage = pendingTools.find(t => t.message)?.message;\n\n // For batch mode, show count; otherwise show the tool name\n // Runtime messages take priority over default generated messages\n const message = runtimeMessage\n ? runtimeMessage\n : isBatch\n ? strings.toolApproval.batchMessage?.replace('{count}', String(toolCount))\n ?? `${toolCount} actions are waiting for your approval`\n : strings.toolApproval.message.replace('{toolName}', displayName);\n\n // Get display name for a tool (use annotation title if available)\n const getToolDisplayName = (tool: PendingToolItem) =>\n tool.annotations?.title || tool.toolCallName;\n\n return (\n <div\n data-testid=\"tool-approval-dialog\"\n style={{\n border: `2px solid ${theme.primaryColor}`,\n borderRadius: '12px',\n background: theme.backgroundColor,\n overflow: 'hidden',\n }}\n >\n {/* Header with warning icon and message */}\n <div\n style={{\n padding: '12px 14px',\n borderBottom: `1px solid ${theme.borderColor}`,\n background: theme.assistantMessageBackground,\n }}\n >\n <div style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n marginBottom: '4px',\n }}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke={theme.primaryColor} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\" />\n <line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\" />\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\" />\n </svg>\n <span style={{\n fontWeight: 600,\n fontSize: '14px',\n color: theme.textColor,\n }}>\n {strings.toolApproval.title}\n </span>\n </div>\n <div style={{\n fontSize: '13px',\n color: theme.secondaryTextColor,\n paddingLeft: '24px',\n }}>\n {message}\n </div>\n </div>\n\n {/* Collapsible details */}\n <div style={{ padding: '8px 14px' }}>\n <button\n onClick={() => setShowDetails(!showDetails)}\n style={{\n background: 'transparent',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n fontSize: '12px',\n color: theme.secondaryTextColor,\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n }}\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n style={{\n transform: showDetails ? 'rotate(90deg)' : 'rotate(0deg)',\n transition: 'transform 0.15s',\n }}\n >\n <path d=\"M4 2L8 6L4 10\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n </svg>\n {strings.toolApproval.showDetails}\n </button>\n {showDetails && (\n <div style={{\n marginTop: '6px',\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n maxHeight: '200px',\n overflow: 'auto',\n }}>\n {/* Use pendingTools if available, otherwise create a single-item list from props */}\n {(pendingTools.length > 0 ? pendingTools : [{\n toolCallId: 'single',\n toolCallName,\n toolCallArgs,\n annotations,\n }]).map((tool, index) => (\n <div\n key={tool.toolCallId}\n style={{\n padding: '8px',\n background: theme.hoverBackground,\n borderRadius: '6px',\n }}\n >\n <div style={{\n fontSize: '12px',\n fontWeight: 500,\n color: theme.textColor,\n marginBottom: '4px',\n }}>\n {getToolDisplayName(tool)}\n </div>\n <pre style={{\n margin: 0,\n fontSize: '11px',\n overflow: 'auto',\n color: theme.secondaryTextColor,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n }}>\n {JSON.stringify(tool.toolCallArgs, null, 2)}\n </pre>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Action buttons */}\n <div style={{\n display: 'flex',\n gap: '8px',\n padding: '8px 14px 12px',\n }}>\n <button\n data-testid=\"approve-tool-button\"\n onClick={onApprove}\n style={{\n flex: 1,\n padding: '8px 16px',\n borderRadius: '8px',\n border: 'none',\n background: theme.primaryGradient,\n color: 'white',\n fontWeight: 500,\n cursor: 'pointer',\n fontSize: '13px',\n transition: 'opacity 0.15s',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.opacity = '0.9';\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.opacity = '1';\n }}\n >\n {isBatch\n ? (strings.toolApproval.approveAll ?? 'Approve All')\n : strings.toolApproval.approve}\n </button>\n <button\n data-testid=\"reject-tool-button\"\n onClick={() => onReject()}\n style={{\n flex: 1,\n padding: '8px 16px',\n borderRadius: '8px',\n border: `1px solid ${theme.borderColor}`,\n background: 'transparent',\n color: theme.textColor,\n fontWeight: 500,\n cursor: 'pointer',\n fontSize: '13px',\n transition: 'all 0.15s',\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 {isBatch\n ? (strings.toolApproval.rejectAll ?? 'Reject All')\n : strings.toolApproval.reject}\n </button>\n </div>\n </div>\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, FeedbackValue, ToolAnnotations } from '../types';\nimport type { FileUploadConfig, FileAttachment, FileProcessingState } 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 /** Gets the current chat (with frozen metadata) */\n get: () => Promise<Chat | null>;\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 /** File processing state for send-time transformations (e.g., OCR) */\n fileProcessing: FileProcessingState | null;\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 /** Tool execution and approval state */\n tools: {\n /** Currently executing tool info for status display */\n executing: { displayText: string } | null;\n /** Pending tool approval */\n pending: {\n /** All tools awaiting approval */\n tools: Array<{\n toolCallId: string;\n toolCallName: string;\n toolCallArgs: Record<string, unknown>;\n annotations?: ToolAnnotations;\n }>;\n /** Approve all pending tool calls */\n approveAll: () => void;\n /** Reject all pending tool calls with optional reason */\n rejectAll: (reason?: string) => void;\n };\n };\n /** Feedback functionality */\n feedback?: {\n /** Whether feedback is enabled (requires Langfuse on server) */\n enabled: boolean;\n /** Submit feedback for a message */\n submit: (messageId: string, traceId: string, feedback: FeedbackValue) => 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 onGetChat: ctx.history.get,\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 fileProcessing: ctx.fileProcessing,\n commands: ctx.commands.list,\n onSaveCommand: ctx.commands.save,\n onRenameCommand: ctx.commands.rename,\n onDeleteCommand: ctx.commands.delete,\n executingTool: ctx.tools.executing,\n feedbackEnabled: ctx.feedback?.enabled,\n onFeedback: ctx.feedback?.submit,\n pendingApprovals: ctx.tools.pending.tools,\n onApproveToolCall: ctx.tools.pending.tools.length > 0 ? ctx.tools.pending.approveAll : undefined,\n onRejectToolCall: ctx.tools.pending.tools.length > 0 ? ctx.tools.pending.rejectAll : undefined,\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 ToolApprovalResponseMessage,\n AgentInfo,\n MultimodalContent,\n FeedbackValue,\n UseAIForwardedProps,\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 // 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 // Feedback tracking\n private _langfuseEnabled = false;\n private langfuseConfigHandlers: Set<(enabled: boolean) => void> = new Set();\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 // Listen for server config (including Langfuse enabled status)\n this.socket.on('config', (data: { langfuseEnabled?: boolean }) => {\n console.log('[Client] Received server config:', data);\n this._langfuseEnabled = data.langfuseEnabled ?? false;\n // Notify listeners\n this.langfuseConfigHandlers.forEach(handler => handler(this._langfuseEnabled));\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 * 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 * @param forwardedProps - Optional props to forward to the server (e.g., telemetryMetadata, mcpHeaders).\n * Internally merged with other forwardedProps.\n */\n async sendPrompt(prompt: string, multimodalContent?: MultimodalContent[], forwardedProps?: UseAIForwardedProps) {\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 if (part.type === 'file') {\n return {\n type: 'file',\n url: part.url,\n mimeType: part.mimeType,\n };\n } else {\n // transformed_file - pass through as-is, server will convert to text\n return {\n type: 'transformed_file',\n text: part.text,\n originalFile: part.originalFile,\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 // 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 annotations: t.annotations,\n })),\n state: this._state,\n context: [],\n forwardedProps: {\n ...(this._selectedAgent ? { agent: this._selectedAgent } : {}),\n ...(forwardedProps || {}),\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 // use-ai extension: include current tools and state for mid-run updates\n // (e.g., when navigation causes new components to mount)\n forwardedProps: {\n tools: this._tools,\n state: this._state,\n },\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 * Sends a tool approval response back to the server.\n *\n * @param toolCallId - The ID of the tool call being approved/rejected\n * @param approved - Whether the tool execution is approved\n * @param reason - Optional reason for rejection (shown to AI)\n */\n sendToolApprovalResponse(toolCallId: string, approved: boolean, reason?: string) {\n const message: ToolApprovalResponseMessage = {\n type: 'tool_approval_response',\n data: {\n toolCallId,\n approved,\n reason,\n },\n };\n\n this.send(message);\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 /**\n * Subscribes to Langfuse config changes.\n *\n * @param handler - Callback function receiving langfuse enabled status\n * @returns Cleanup function to unsubscribe\n */\n onLangfuseConfigChange(handler: (enabled: boolean) => void): () => void {\n this.langfuseConfigHandlers.add(handler);\n // Immediately call with current value\n handler(this._langfuseEnabled);\n return () => {\n this.langfuseConfigHandlers.delete(handler);\n };\n }\n\n /**\n * Submits feedback for an assistant message.\n * Sends feedback to the server, which forwards it to Langfuse.\n *\n * @param messageId - The client-side message ID\n * @param traceId - The Langfuse trace ID (runId from RUN_FINISHED)\n * @param feedback - 'upvote' for positive, 'downvote' for negative, null to remove\n */\n submitFeedback(messageId: string, traceId: string, feedback: FeedbackValue): void {\n if (!this.socket?.connected) {\n console.warn('[UseAI] Cannot submit feedback: not connected');\n return;\n }\n\n if (!this._langfuseEnabled) {\n console.warn('[UseAI] Cannot submit feedback: Langfuse not enabled on server');\n return;\n }\n\n this.send({\n type: 'message_feedback',\n data: { messageId, traceId, feedback },\n });\n }\n}\n","import { z } from 'zod';\nimport type { ToolDefinition, ToolAnnotations } from '@meetsmore-oss/use-ai-core';\n\n// Re-export ToolAnnotations for convenience\nexport type { ToolAnnotations };\n\n/**\n * Context provided to tool execution functions.\n * Allows tools to dynamically request user approval at runtime.\n */\nexport interface ToolExecutionContext {\n /**\n * Request user approval during tool execution.\n * Use this for conditional approval based on runtime values\n * (e.g., API endpoint, input values, computed risk).\n *\n * @param input - Approval request details\n * @returns Promise resolving with approval decision\n *\n * @example\n * ```typescript\n * const callApi = defineTool(\n * 'Call an external API',\n * z.object({ url: z.string() }),\n * async (input, ctx) => {\n * if (input.url.includes('production')) {\n * const { approved } = await ctx.requestApproval({\n * message: `This will call production API: ${input.url}`,\n * });\n * if (!approved) return { error: 'User rejected the action' };\n * }\n * return fetch(input.url);\n * }\n * );\n * ```\n */\n requestApproval(input: {\n message: string;\n metadata?: Record<string, unknown>;\n }): Promise<{ approved: boolean; reason?: string }>;\n}\n\n/**\n * Options for configuring tool behavior.\n */\nexport interface ToolOptions {\n /** MCP-aligned annotations for tool behavior hints */\n annotations?: ToolAnnotations;\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>, ctx: ToolExecutionContext) => 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, ctx: ToolExecutionContext) => 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: (ctx: ToolExecutionContext) => 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>, ctx: ToolExecutionContext) => 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 | ((ctx: ToolExecutionContext) => unknown),\n fnOrOptions?: ((input: z.infer<T>, ctx: ToolExecutionContext) => 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>, ctx: ToolExecutionContext) => unknown | Promise<unknown>;\n let actualOptions: ToolOptions;\n\n if (isNoParamFunction) {\n // Wrap no-param function: user writes (ctx?) => ..., we adapt to (input, ctx) => ...\n const noParamFn = schemaOrFn as (ctx: ToolExecutionContext) => unknown | Promise<unknown>;\n actualFn = (_input: z.infer<T>, ctx: ToolExecutionContext) => noParamFn(ctx);\n actualOptions = (fnOrOptions as ToolOptions) || {};\n } else {\n actualFn = fnOrOptions as (input: z.infer<T>, ctx: ToolExecutionContext) => 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 // Pass through annotations if present\n if (this._options.annotations) {\n toolDef.annotations = this._options.annotations;\n }\n\n return toolDef;\n },\n\n async _execute(input: unknown, ctx: ToolExecutionContext) {\n const validated = this._zodSchema.parse(input);\n return await actualFn(validated, ctx);\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 ctx: ToolExecutionContext\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, ctx);\n}\n","import type {\n Chat,\n ChatMetadata,\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 metadata: options?.metadata,\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 async updateMetadata(id: string, metadata: ChatMetadata, overwrite = false): Promise<void> {\n const chat = await this.loadChat(id);\n if (!chat) {\n throw new Error(`Chat not found: ${id}`);\n }\n\n chat.metadata = overwrite ? metadata : { ...chat.metadata, ...metadata };\n await this.saveChat(chat);\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 { useState, useCallback, useRef, useEffect } from 'react';\nimport type { ChatRepository, Chat, ChatMetadata, CreateChatOptions, 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 * Deep equality comparison using JSON serialization.\n * Works for JSON-serializable values (primitives, arrays, plain objects).\n */\nfunction deepEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\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 traceId?: string;\n feedback?: 'upvote' | 'downvote' | null;\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 traceId: msg.traceId,\n feedback: msg.feedback,\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 /** Current messages state (owned by provider) */\n messages: Message[];\n /** Setter for messages state (owned by provider) */\n setMessages: React.Dispatch<React.SetStateAction<Message[]>>;\n /** Whether the client is connected */\n connected?: boolean;\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 /** The displayed chat ID (pending or current) */\n displayedChatId: string | null;\n /** Creates a new chat and switches to it */\n createNewChat: (options?: CreateChatOptions) => 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', traceId?: string) => Promise<void>;\n /** Reloads messages from storage for the given chat ID */\n reloadMessages: (chatId: string) => Promise<void>;\n /** Get the current chat object. Metadata is frozen to prevent accidental mutation. */\n getCurrentChat: () => Promise<Chat | null>;\n /** Update metadata for the current chat */\n updateMetadata: (metadata: ChatMetadata, overwrite?: boolean) => 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 */\nexport function useChatManagement({\n repository,\n clientRef,\n messages,\n setMessages,\n connected,\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 /**\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 /** Loads messages from storage for a given chat ID. */\n const loadChatMessages = useCallback(async (chatId: string): Promise<Message[]> => {\n try {\n const chat = await repository.loadChat(chatId);\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 /** Reloads messages from storage and updates state. */\n const reloadMessages = useCallback(async (chatId: string) => {\n const loadedMessages = await loadChatMessages(chatId);\n setMessages(loadedMessages);\n }, [loadChatMessages, setMessages]);\n\n /** Creates a new chat. */\n const createNewChat = useCallback(async (options?: CreateChatOptions): Promise<string> => {\n console.log('[ChatManagement] createNewChat called - currentChatId:', currentChatId, 'pendingChatId:', pendingChatId, 'messages.length:', messages.length);\n\n // Reuse last created blank chat if options match\n if (pendingChatId && messages.length === 0) {\n const existingChat = await repository.loadChat(pendingChatId);\n const optionsMatch = existingChat\n && existingChat.title === options?.title\n && deepEquals(existingChat.metadata, options?.metadata);\n if (optionsMatch) {\n console.log('[ChatManagement] Last created chat has matching options, reusing:', pendingChatId);\n return pendingChatId;\n }\n }\n\n console.log('[ChatManagement] Creating new chat...');\n const chatId = await repository.createChat(options);\n\n // Set as pending - don't switch currentChatId until user sends a message\n setPendingChatId(chatId);\n setMessages([]);\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, setMessages]);\n\n /** Loads an existing chat by ID. */\n const loadChat = useCallback(async (chatId: string): Promise<void> => {\n // Set as pending chat - don't activate until user sends a message\n setPendingChatId(chatId);\n await reloadMessages(chatId);\n\n // Set threadId so the server recognizes this as a different conversation\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 /** Deletes a chat by ID. */\n const deleteChat = useCallback(async (chatId: string): Promise<void> => {\n await repository.deleteChat(chatId);\n\n if (currentChatId === chatId) {\n setCurrentChatId(null);\n setMessages([]);\n }\n\n if (pendingChatId === chatId) {\n setPendingChatId(null);\n setMessages([]);\n }\n\n console.log('[ChatManagement] Deleted chat:', chatId);\n }, [currentChatId, pendingChatId, repository, setMessages]);\n\n /** Lists all available chats. */\n const listChats = useCallback(async (): Promise<Array<Omit<Chat, 'messages'>>> => {\n return await repository.listChats();\n }, [repository]);\n\n /** Clears the current chat messages. */\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, setMessages]);\n\n /**\n * Gets the current chat object (including metadata).\n * Metadata is frozen to prevent accidental mutation.\n */\n const getCurrentChat = useCallback(async (): Promise<Chat | null> => {\n const chatId = pendingChatId || currentChatId;\n if (!chatId) return null;\n const chat = await repository.loadChat(chatId);\n if (chat?.metadata) {\n chat.metadata = Object.freeze({ ...chat.metadata });\n }\n return chat;\n }, [pendingChatId, currentChatId, repository]);\n\n /** Updates metadata for the current chat. */\n const updateMetadata = useCallback(async (metadata: ChatMetadata, overwrite = false): Promise<void> => {\n const chatId = pendingChatId || currentChatId;\n if (!chatId) {\n throw new Error('No active chat');\n }\n await repository.updateMetadata(chatId, metadata, overwrite);\n }, [pendingChatId, 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 existing 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 /** Saves a user message to storage. */\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 /** Saves an AI response to storage and updates UI. */\n const saveAIResponse = useCallback(async (\n content: string,\n displayMode?: 'default' | 'error',\n traceId?: string\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 traceId,\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, setMessages]);\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 displayedChatId,\n createNewChat,\n loadChat,\n deleteChat,\n listChats,\n clearCurrentChat,\n activatePendingChat,\n saveUserMessage,\n saveAIResponse,\n reloadMessages,\n getCurrentChat,\n updateMetadata,\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, type RefObject, type MutableRefObject } from 'react';\nimport type { ToolAnnotations, ToolApprovalRequestEvent } from '../types';\nimport type { UseAIClient } from '../client';\nimport type { ToolsDefinition, ToolExecutionContext } from '../defineTool';\nimport { executeDefinedTool } from '../defineTool';\n\n// ── Registry Types ──────────────────────────────────────────────────────────\n\nexport interface RegisterToolsOptions {\n /** Mark component as invisible (no visual state, skip prompt wait) */\n invisible?: boolean;\n}\n\n// ── Execution Types ─────────────────────────────────────────────────────────\n\n/**\n * Pending tool approval request state.\n */\nexport interface PendingToolApproval {\n toolCallId: string;\n toolCallName: string;\n toolCallArgs: Record<string, unknown>;\n annotations?: ToolAnnotations;\n /** Optional message explaining why approval is needed (runtime approval) */\n message?: string;\n /** Optional metadata for the approval request (runtime approval) */\n metadata?: Record<string, unknown>;\n}\n\n// ── Hook Options & Return ───────────────────────────────────────────────────\n\nexport interface UseToolSystemOptions {\n /** Reference to the UseAI client for sending responses */\n clientRef: RefObject<UseAIClient | null>;\n /** Builds the aggregated state from all registered prompts */\n buildState: () => unknown;\n}\n\nexport interface UseToolSystemReturn {\n // ── Registry ──────────────────────────────────────────────────────────────\n\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: MutableRefObject<ToolsDefinition>;\n /** Signals that a component has completed its registration in useLayoutEffect */\n signalReady: (id: string) => void;\n /** Current tool registry version (increments when tools change) */\n toolRegistryVersion: number;\n\n // ── Waiters ───────────────────────────────────────────────────────────────\n\n /** Registers a waiter function for a component (called when tool exec needs to wait for re-render) */\n registerWaiter: (id: string, waiter: () => Promise<void>) => void;\n /** Unregisters a waiter function */\n unregisterWaiter: (id: string) => void;\n\n // ── Execution ─────────────────────────────────────────────────────────────\n\n /** All pending tool approval requests */\n pendingApprovals: PendingToolApproval[];\n /** Handle a tool approval request event from the server */\n handleApprovalRequest: (event: ToolApprovalRequestEvent) => void;\n /** Execute a tool call and send the response to the server */\n executeToolCall: (toolCallId: string, name: string, input: unknown) => Promise<void>;\n /** Store a tool call as pending approval (deferred execution) */\n storePendingToolCall: (toolCallId: string, name: string, input: unknown, toolCallData: { name: string; args: string }) => void;\n /** Approve all pending tool calls and execute them */\n approveAll: () => Promise<void>;\n /** Reject all pending tool calls with optional reason */\n rejectAll: (reason?: string) => void;\n}\n\n/**\n * Unified hook for the tool lifecycle: registration, waiter coordination,\n * and execution (including approval flow).\n *\n * Merges what were previously three separate concerns:\n * - Tool registry (registration, aggregation, ownership tracking)\n * - Waiters (waiting for component re-renders after tool execution)\n * - Tool execution (running tools, sending responses, approval flow)\n *\n * The only external dependency is `buildState` from prompt management,\n * which provides the aggregated app state sent alongside tool responses.\n */\nexport function useToolSystem({\n clientRef,\n buildState,\n}: UseToolSystemOptions): UseToolSystemReturn {\n\n // ── Registry State ──────────────────────────────────────────────────────\n\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 // Ready state tracking for each component\n const readyStateRef = useRef<Map<string, boolean>>(new Map());\n const readyListenersRef = useRef<Set<() => void>>(new Set());\n\n // ── Waiter State ────────────────────────────────────────────────────────\n\n const waitersRef = useRef<Map<string, () => Promise<void>>>(new Map());\n\n // ── Execution State ─────────────────────────────────────────────────────\n\n const [pendingApprovals, setPendingApprovals] = useState<PendingToolApproval[]>([]);\n const pendingApprovalToolCallsRef = useRef<Map<string, { name: string; input: unknown; toolCallData: { name: string; args: string } }>>(new Map());\n\n /** Resolvers for runtime approval requests (from ctx.requestApproval) */\n const runtimeApprovalResolversRef = useRef<Map<string, (result: { approved: boolean; reason?: string }) => void>>(new Map());\n\n // ── Registry Methods ────────────────────────────────────────────────────\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 // Mark as NOT ready - will be set ready by signalReady in useLayoutEffect\n readyStateRef.current.set(id, false);\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 setToolRegistryVersion(v => v + 1);\n }\n\n Object.keys(tools).forEach(toolName => {\n toolOwnershipRef.current.set(toolName, id);\n });\n\n if (options?.invisible) {\n invisibleRef.current.add(id);\n } else {\n invisibleRef.current.delete(id);\n }\n }, []);\n\n const signalReady = useCallback((id: string) => {\n if (!toolRegistryRef.current.has(id)) {\n return;\n }\n readyStateRef.current.set(id, true);\n readyListenersRef.current.forEach(listener => listener());\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 readyStateRef.current.delete(id);\n setToolRegistryVersion(v => v + 1);\n invisibleRef.current.delete(id);\n\n readyListenersRef.current.forEach(listener => listener());\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 const aggregatedToolsRef = useRef(aggregatedTools);\n aggregatedToolsRef.current = aggregatedTools;\n\n /**\n * Waits for tools to stabilize using React lifecycle signaling.\n *\n * Strategy:\n * 1. Wait for the next macrotask to let React process pending state updates\n * 2. Wait for all registered components to signal ready\n * 3. Safety timeout to prevent infinite waiting\n */\n const waitForToolsToStabilize = useCallback(async (): Promise<void> => {\n const maxWaitMs = 500;\n\n const checkAllReady = (): boolean => {\n if (readyStateRef.current.size === 0) return true;\n for (const ready of readyStateRef.current.values()) {\n if (!ready) return false;\n }\n return true;\n };\n\n await new Promise(resolve => setTimeout(resolve, 0));\n\n if (checkAllReady()) {\n return;\n }\n\n return new Promise<void>((resolve) => {\n let safetyTimeout: ReturnType<typeof setTimeout> | null = null;\n let resolved = false;\n\n const cleanup = () => {\n if (resolved) return;\n resolved = true;\n if (safetyTimeout) clearTimeout(safetyTimeout);\n readyListenersRef.current.delete(onReadyChange);\n };\n\n const onReadyChange = () => {\n if (resolved) return;\n if (checkAllReady()) {\n cleanup();\n resolve();\n }\n };\n\n safetyTimeout = setTimeout(() => {\n cleanup();\n resolve();\n }, maxWaitMs);\n\n readyListenersRef.current.add(onReadyChange);\n onReadyChange();\n });\n }, []);\n\n // ── Waiter Methods ──────────────────────────────────────────────────────\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 // ── Execution Methods ───────────────────────────────────────────────────\n\n const handleApprovalRequest = useCallback((event: ToolApprovalRequestEvent) => {\n console.log('[useToolSystem] Tool approval requested:', event.toolCallName, event.toolCallId);\n setPendingApprovals(prev => [\n ...prev,\n {\n toolCallId: event.toolCallId,\n toolCallName: event.toolCallName,\n toolCallArgs: event.toolCallArgs,\n annotations: event.annotations,\n message: event.message,\n metadata: event.metadata,\n },\n ]);\n }, []);\n\n const executeToolCall = useCallback(async (\n toolCallId: string,\n name: string,\n input: unknown\n ) => {\n const client = clientRef.current;\n if (!client) {\n console.error('[useToolSystem] No client available for tool execution');\n return;\n }\n\n try {\n const ownerId = toolOwnershipRef.current.get(name);\n console.log(`[useToolSystem] Tool \"${name}\" owned by component:`, ownerId);\n\n // Build ToolExecutionContext with requestApproval for runtime approvals\n const ctx: ToolExecutionContext = {\n requestApproval: ({ message, metadata }) => {\n return new Promise<{ approved: boolean; reason?: string }>((resolve) => {\n const approvalId = `${toolCallId}-runtime-${Date.now()}`;\n runtimeApprovalResolversRef.current.set(approvalId, resolve);\n\n setPendingApprovals(prev => [\n ...prev,\n {\n toolCallId: approvalId,\n toolCallName: name,\n toolCallArgs: (input as Record<string, unknown>) || {},\n message,\n metadata,\n },\n ]);\n });\n },\n };\n\n console.log('[useToolSystem] Executing tool...');\n const result = await executeDefinedTool(aggregatedToolsRef.current, name, input, ctx);\n\n const isErrorResult = result && typeof result === 'object' &&\n ('error' in result || (result as Record<string, unknown>).success === false);\n\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(`[useToolSystem] Waiting for prompt change from ${ownerId}...`);\n await waiter();\n console.log('[useToolSystem] Prompt change wait complete');\n }\n } else if (isErrorResult) {\n console.log('[useToolSystem] Tool returned error, skipping prompt wait');\n } else if (ownerIsInvisible) {\n console.log('[useToolSystem] Component is invisible, skipping prompt wait');\n }\n\n // Wait for tools to stabilize after execution (navigation/mount-unmount)\n console.log('[useToolSystem] Waiting for tools to stabilize...');\n await waitForToolsToStabilize();\n console.log('[useToolSystem] Tools stabilized');\n\n const updatedState = buildState();\n console.log(`[useToolSystem] Updated state (aggregated from all hooks)`);\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 }, [clientRef, isInvisible, getWaiter, waitForToolsToStabilize, buildState]);\n\n const storePendingToolCall = useCallback((\n toolCallId: string,\n name: string,\n input: unknown,\n toolCallData: { name: string; args: string }\n ) => {\n console.log(`[useToolSystem] Storing pending tool call \"${name}\" for approval`);\n pendingApprovalToolCallsRef.current.set(toolCallId, { name, input, toolCallData });\n }, []);\n\n const executePendingToolAfterApproval = useCallback(async (toolCallId: string) => {\n const pendingTool = pendingApprovalToolCallsRef.current.get(toolCallId);\n if (!pendingTool) {\n console.warn(`[useToolSystem] No pending tool found for ${toolCallId}`);\n return;\n }\n\n pendingApprovalToolCallsRef.current.delete(toolCallId);\n await executeToolCall(toolCallId, pendingTool.name, pendingTool.input);\n }, [executeToolCall]);\n\n const approveAll = useCallback(async () => {\n if (!clientRef.current) return;\n console.log('[useToolSystem] Approving all tool calls:', pendingApprovals.length);\n\n const pendingTools = [...pendingApprovals];\n setPendingApprovals([]);\n\n for (const pending of pendingTools) {\n // Check if this is a runtime client-side approval (from ctx.requestApproval)\n const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);\n if (runtimeResolver) {\n runtimeApprovalResolversRef.current.delete(pending.toolCallId);\n runtimeResolver({ approved: true });\n } else {\n // Server-side approval (destructiveHint flow)\n clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);\n await executePendingToolAfterApproval(pending.toolCallId);\n }\n }\n }, [clientRef, pendingApprovals, executePendingToolAfterApproval]);\n\n const rejectAll = useCallback((reason?: string) => {\n if (!clientRef.current) return;\n console.log('[useToolSystem] Rejecting all tool calls:', pendingApprovals.length, reason);\n\n const pendingTools = [...pendingApprovals];\n setPendingApprovals([]);\n\n for (const pending of pendingTools) {\n // Check if this is a runtime client-side approval (from ctx.requestApproval)\n const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);\n if (runtimeResolver) {\n runtimeApprovalResolversRef.current.delete(pending.toolCallId);\n runtimeResolver({ approved: false, reason });\n } else {\n // Server-side approval (destructiveHint flow)\n clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);\n pendingApprovalToolCallsRef.current.delete(pending.toolCallId);\n }\n }\n }, [clientRef, pendingApprovals]);\n\n // ── Return ──────────────────────────────────────────────────────────────\n\n return {\n // Registry\n registerTools,\n unregisterTools,\n isInvisible,\n aggregatedTools,\n hasTools,\n aggregatedToolsRef,\n signalReady,\n toolRegistryVersion,\n\n // Waiters\n registerWaiter,\n unregisterWaiter,\n\n // Execution\n pendingApprovals,\n handleApprovalRequest,\n executeToolCall,\n storePendingToolCall,\n approveAll,\n rejectAll,\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 /** All suggestions aggregated from registered components */\n aggregatedSuggestions: string[];\n /** Builds the aggregated state from all registered prompts */\n buildStateFromPrompts: () => { context: string } | null;\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 * - 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 [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 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 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 aggregatedSuggestions,\n buildStateFromPrompts,\n };\n}\n","import { useState, useEffect, useRef, useCallback } from 'react';\nimport type { UseAIClient } from '../client';\nimport type { FeedbackValue } from '../types';\nimport type { ChatRepository } from '../providers/chatRepository/types';\nimport type { Message } from '../components/UseAIChatPanel';\n\nexport interface UseFeedbackOptions {\n /** Reference to the UseAIClient */\n clientRef: React.MutableRefObject<UseAIClient | null>;\n /** Chat repository for persisting feedback */\n repository: ChatRepository;\n /** Callback to get the currently displayed chat ID */\n getDisplayedChatId: () => string | null;\n /** Setter for messages state (for optimistic UI updates) */\n setMessages: React.Dispatch<React.SetStateAction<Message[]>>;\n}\n\nexport interface UseFeedbackReturn {\n /** Whether Langfuse feedback is enabled on the server */\n enabled: boolean;\n /** Submits feedback for a message (updates storage and sends to server) */\n submitFeedback: (messageId: string, traceId: string, feedback: FeedbackValue) => void;\n}\n\n/**\n * Hook for managing user feedback on AI messages.\n *\n * Responsibilities:\n * - Tracks whether Langfuse feedback is enabled on the server\n * - Captures traceId from RUN_FINISHED events\n * - Persists feedback to chat storage\n * - Sends feedback to server (Langfuse)\n *\n * @example\n * ```typescript\n * const { enabled, getTraceId, clearTraceId, submitFeedback } = useFeedback({\n * clientRef,\n * repository,\n * getDisplayedChatId: () => displayedChatId,\n * setMessages,\n * });\n * ```\n */\nexport function useFeedback({\n clientRef,\n repository,\n getDisplayedChatId,\n setMessages,\n}: UseFeedbackOptions): UseFeedbackReturn {\n const [enabled, setEnabled] = useState(false);\n const enabledRef = useRef(false);\n\n // Keep enabledRef in sync with state\n useEffect(() => {\n enabledRef.current = enabled;\n }, [enabled]);\n\n // Subscribe to Langfuse config changes from server\n useEffect(() => {\n const client = clientRef.current;\n if (!client) return;\n\n const unsubscribe = client.onLangfuseConfigChange((isEnabled) => {\n setEnabled(isEnabled);\n });\n\n return unsubscribe;\n }, [clientRef.current]);\n\n /**\n * Updates feedback in storage and local state.\n */\n const updateFeedbackInStorage = useCallback(async (\n messageId: string,\n feedback: FeedbackValue\n ): Promise<void> => {\n const displayedChatId = getDisplayedChatId();\n\n if (!displayedChatId) {\n console.warn('[useFeedback] No chat ID, cannot update feedback');\n return;\n }\n\n try {\n const chat = await repository.loadChat(displayedChatId);\n\n if (!chat) {\n console.error('[useFeedback] Chat not found:', displayedChatId);\n return;\n }\n\n // Find and update the message\n const message = chat.messages.find(msg => msg.id === messageId);\n if (message) {\n message.feedback = feedback;\n await repository.saveChat(chat);\n\n // Update local state immediately for responsive UI\n setMessages(prevMessages =>\n prevMessages.map(msg =>\n msg.id === messageId ? { ...msg, feedback } : msg\n )\n );\n } else {\n console.warn('[useFeedback] Message not found:', messageId);\n }\n } catch (error) {\n console.error('[useFeedback] Failed to update feedback:', error);\n }\n }, [repository, getDisplayedChatId, setMessages]);\n\n const submitFeedback = useCallback((\n messageId: string,\n traceId: string,\n feedback: FeedbackValue\n ) => {\n // Update storage and local state\n updateFeedbackInStorage(messageId, feedback);\n\n // Send to server if connected and Langfuse is enabled\n const client = clientRef.current;\n if (client && enabledRef.current) {\n client.submitFeedback(messageId, traceId, feedback);\n }\n }, [clientRef, updateFeedbackInStorage]);\n\n return {\n enabled,\n submitFeedback,\n };\n}\n","import { useState, useCallback, useRef } from 'react';\nimport type {\n AGUIEvent,\n ToolCallStartEvent,\n ToolCallEndEvent,\n RunErrorEvent,\n RunFinishedEvent,\n TextMessageContentEvent,\n ToolCallStartExtensions,\n ToolApprovalRequestEvent,\n} from '../types';\nimport { EventType, ErrorCode, TOOL_APPROVAL_REQUEST } from '../types';\nimport type { UseAIClient } from '../client';\nimport type { UseToolSystemReturn } from './useToolSystem';\nimport type { UseAIStrings } from '../theme';\n\nexport interface UseServerEventsOptions {\n /** Tool system for executing tools and looking up tool metadata */\n toolSystem: UseToolSystemReturn;\n /** Saves an AI response to chat storage */\n saveAIResponse: (content: string, displayMode?: 'default' | 'error', traceId?: string) => Promise<void>;\n /** UI strings for error messages and tool execution fallbacks */\n strings: UseAIStrings;\n}\n\nexport interface ExecutingToolDisplay {\n displayText: string;\n}\n\nexport interface UseServerEventsReturn {\n /** Whether the AI is currently loading/processing a response */\n loading: boolean;\n /** Set the loading state (e.g., when sending a message) */\n setLoading: React.Dispatch<React.SetStateAction<boolean>>;\n /** Current streaming text from the AI response */\n streamingText: string;\n /** Clear streaming text (e.g., when starting a new message) */\n clearStreamingText: () => void;\n /** Currently executing tool info for UI display, or null */\n executingTool: ExecutingToolDisplay | null;\n /** Ref tracking which chat the current streaming text belongs to */\n streamingChatIdRef: React.MutableRefObject<string | null>;\n /**\n * Handles a server event. Called from the provider's client subscription.\n * Takes the client instance so it can access client-internal state\n * (currentToolCalls, currentMessageContent).\n */\n handleServerEvent: (client: UseAIClient, event: AGUIEvent) => Promise<void>;\n}\n\n/**\n * Hook that owns all server event handling state and logic.\n *\n * Manages:\n * - Loading state (set on message send, cleared on RUN_FINISHED/RUN_ERROR)\n * - Streaming text accumulation (TEXT_MESSAGE_CONTENT/END events)\n * - Executing tool display (TOOL_CALL_START/END events)\n * - Tool execution dispatch (delegates to toolSystem)\n * - Error handling (RUN_ERROR events)\n *\n * The provider creates the client and subscribes `handleServerEvent` to it.\n * This hook doesn't manage the client lifecycle — only the event handling.\n */\nexport function useServerEvents({\n toolSystem,\n saveAIResponse,\n strings,\n}: UseServerEventsOptions): UseServerEventsReturn {\n const [loading, setLoading] = useState(false);\n const [streamingText, setStreamingText] = useState('');\n const streamingChatIdRef = useRef<string | null>(null);\n\n // Executing tool state for UI display\n const [executingToolRaw, setExecutingTool] = useState<{\n toolCallId: string;\n title: string | null;\n } | null>(null);\n const executingToolFallbackRef = useRef<string | null>(null);\n\n const clearStreamingText = useCallback(() => {\n setStreamingText('');\n }, []);\n\n // Keep refs to avoid stale closures in the event handler\n // (the handler is captured once per client lifecycle, but these deps may change)\n const toolSystemRef = useRef(toolSystem);\n toolSystemRef.current = toolSystem;\n\n const saveAIResponseRef = useRef(saveAIResponse);\n saveAIResponseRef.current = saveAIResponse;\n\n const stringsRef = useRef(strings);\n stringsRef.current = strings;\n\n const handleServerEvent = useCallback(async (client: UseAIClient, event: AGUIEvent) => {\n const ts = toolSystemRef.current;\n const strs = stringsRef.current;\n\n if (event.type === EventType.TOOL_CALL_START) {\n const e = event as ToolCallStartEvent & Partial<ToolCallStartExtensions>;\n\n // Get title from event annotations, local tool definition, or null (fallback)\n const tool = ts.aggregatedToolsRef.current[e.toolCallName];\n const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;\n\n if (!title) {\n const fallbacks = strs.toolExecution.fallbackMessages;\n executingToolFallbackRef.current = fallbacks[Math.floor(Math.random() * fallbacks.length)];\n }\n\n setExecutingTool({ toolCallId: e.toolCallId, title });\n } else if (event.type === EventType.TOOL_CALL_END) {\n const toolCallEnd = event as ToolCallEndEvent;\n const toolCallId = toolCallEnd.toolCallId;\n\n setExecutingTool(prev => prev?.toolCallId === toolCallId ? null : prev);\n\n const toolCallData = (client as unknown as { currentToolCalls: Map<string, { name: string; args: string }> }).currentToolCalls.get(toolCallId);\n if (!toolCallData) {\n console.error(`[ServerEvents] Tool call ${toolCallId} not found`);\n return;\n }\n\n const name = toolCallData.name;\n const input = JSON.parse(toolCallData.args);\n\n // Skip tools not in our registry (likely workflow tools)\n const tool = ts.aggregatedToolsRef.current[name];\n if (!tool) {\n console.log(`[ServerEvents] Tool \"${name}\" not found in useAI tools, skipping (likely a workflow tool)`);\n return;\n }\n\n // Defer execution if tool requires approval\n if (tool._options?.annotations?.destructiveHint === true) {\n console.log(`[ServerEvents] Tool \"${name}\" requires approval, deferring execution`);\n ts.storePendingToolCall(toolCallId, name, input, toolCallData);\n return;\n }\n\n await ts.executeToolCall(toolCallId, name, input);\n } else if ((event.type as string) === TOOL_APPROVAL_REQUEST) {\n const e = event as unknown as ToolApprovalRequestEvent;\n ts.handleApprovalRequest(e);\n } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {\n const contentEvent = event as TextMessageContentEvent;\n setStreamingText(prev => prev + contentEvent.delta);\n } else if (event.type === EventType.TEXT_MESSAGE_END) {\n setStreamingText('');\n streamingChatIdRef.current = null;\n } else if (event.type === EventType.RUN_FINISHED) {\n const content = client.currentMessageContent;\n if (content) {\n const finishedEvent = event as RunFinishedEvent;\n const traceId = finishedEvent.runId;\n saveAIResponseRef.current(content, undefined, traceId);\n }\n setLoading(false);\n } else if (event.type === EventType.RUN_ERROR) {\n const errorEvent = event as RunErrorEvent;\n const errorCode = errorEvent.message as ErrorCode;\n console.error('[ServerEvents] Run error:', errorCode);\n\n const userMessage = strs.errors[errorCode] || strs.errors[ErrorCode.UNKNOWN_ERROR];\n\n saveAIResponseRef.current(userMessage, 'error');\n setStreamingText('');\n streamingChatIdRef.current = null;\n\n setLoading(false);\n }\n }, []);\n\n // Compute display value for UI\n const executingTool = executingToolRaw ? {\n displayText: executingToolRaw.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0],\n } : null;\n\n return {\n loading,\n setLoading,\n streamingText,\n clearStreamingText,\n executingTool,\n streamingChatIdRef,\n handleServerEvent,\n };\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 ToolAnnotations,\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 ToolCallStartExtensions,\n WorkflowStatus,\n UseAIClientMessage,\n RunWorkflowMessage,\n FeedbackMessage,\n FeedbackValue,\n McpHeadersConfig,\n McpHeadersMap,\n AgentInfo,\n UseAIForwardedProps,\n // Tool approval types\n ToolApprovalRequestEvent,\n ToolApprovalResponseMessage,\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, TOOL_APPROVAL_REQUEST } from '@meetsmore-oss/use-ai-core';\n","import { useCallback, useRef, useEffect } from 'react';\nimport type { FileAttachment } from '../fileUpload/types';\nimport type { UseAIForwardedProps } from '../types';\nimport type { CreateChatOptions, ChatMetadata } from '../providers/chatRepository/types';\n\n/**\n * Options for programmatically sending a message via sendMessage().\n */\nexport interface SendMessageOptions {\n /** Start a new chat before sending. Default: false (continue existing chat) */\n newChat?: boolean;\n /** File attachments to include with the message */\n attachments?: File[];\n /** Open the chat panel after sending. Default: true */\n openChat?: boolean;\n /** Metadata to set on the new chat (only used when newChat: true) */\n metadata?: ChatMetadata;\n /**\n * Forwarded props for observability and configuration (e.g., telemetryMetadata, mcpHeaders).\n * This is merged with provider-level forwardedProps (message-level takes precedence).\n */\n forwardedProps?: UseAIForwardedProps;\n}\n\nexport interface UseMessageQueueOptions {\n /** The function that actually sends a message (the provider's handleSendMessage) */\n sendFn: (message: string, attachments?: FileAttachment[], forwardedProps?: UseAIForwardedProps) => Promise<void>;\n /** Creates a new chat */\n createNewChat: (options?: CreateChatOptions) => Promise<string>;\n /** Callback to open/close the chat panel */\n setOpen?: (open: boolean) => void;\n /** Whether the client is connected */\n connected: boolean;\n /** Whether the AI is currently loading/processing */\n loading: boolean;\n /** Whether there's a pending tool approval blocking the queue */\n hasPendingApproval: boolean;\n}\n\nexport interface UseMessageQueueReturn {\n /**\n * Programmatically send a message to the chat.\n * Messages are queued and processed one at a time.\n * Throws on failure (e.g., not connected, no sendFn).\n */\n sendMessage: (message: string, options?: SendMessageOptions) => Promise<void>;\n}\n\n/**\n * Hook for queuing and sending programmatic messages.\n *\n * Handles:\n * - Message queuing (one at a time)\n * - Waiting for loading + approval to complete between messages\n * - Creating new chats before sending\n * - Opening the chat panel after sending\n * - Converting File[] to FileAttachment[]\n */\nexport function useMessageQueue({\n sendFn,\n createNewChat,\n setOpen,\n connected,\n loading,\n hasPendingApproval,\n}: UseMessageQueueOptions): UseMessageQueueReturn {\n const pendingMessagesRef = useRef<Array<{ message: string; options?: SendMessageOptions }>>([]);\n const isProcessingQueueRef = useRef(false);\n\n // Use refs for callbacks that may change between renders.\n // The queue processor runs across async boundaries (awaits), during which\n // React may re-render and update these functions with fresh closures.\n // Refs ensure we always call the latest version.\n const sendFnRef = useRef(sendFn);\n sendFnRef.current = sendFn;\n\n const createNewChatRef = useRef(createNewChat);\n createNewChatRef.current = createNewChat;\n\n const setOpenRef = useRef(setOpen);\n setOpenRef.current = setOpen;\n\n const loadingRef = useRef(loading);\n useEffect(() => {\n loadingRef.current = loading;\n }, [loading]);\n\n const hasPendingApprovalRef = useRef(hasPendingApproval);\n useEffect(() => {\n hasPendingApprovalRef.current = hasPendingApproval;\n }, [hasPendingApproval]);\n\n const processMessageQueue = useCallback(async () => {\n if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0) {\n return;\n }\n\n isProcessingQueueRef.current = true;\n\n while (pendingMessagesRef.current.length > 0) {\n const { message, options } = pendingMessagesRef.current.shift()!;\n const { newChat = false, attachments = [], openChat = true, metadata, forwardedProps } = options ?? {};\n\n if (newChat) {\n await createNewChatRef.current({ metadata });\n }\n\n // Convert File[] to FileAttachment[]\n const fileAttachments: FileAttachment[] = await Promise.all(\n attachments.map(async (file) => {\n let preview: string | undefined;\n if (file.type.startsWith('image/')) {\n preview = await new Promise<string | undefined>((resolve) => {\n const reader = new FileReader();\n reader.onload = () => resolve(typeof reader.result === 'string' ? reader.result : undefined);\n reader.onerror = () => resolve(undefined);\n reader.readAsDataURL(file);\n });\n }\n return {\n id: crypto.randomUUID(),\n file,\n preview,\n };\n })\n );\n\n await sendFnRef.current(message, fileAttachments.length > 0 ? fileAttachments : undefined, forwardedProps);\n\n if (openChat && setOpenRef.current) {\n setOpenRef.current(true);\n }\n\n // Wait for loading and pending approval to complete before processing next message\n await new Promise<void>((resolve) => {\n const checkReady = () => {\n setTimeout(() => {\n if (!loadingRef.current && !hasPendingApprovalRef.current) {\n resolve();\n } else {\n checkReady();\n }\n }, 100);\n };\n checkReady();\n });\n }\n\n isProcessingQueueRef.current = false;\n }, []);\n\n const sendMessage = useCallback(async (message: string, options?: SendMessageOptions): Promise<void> => {\n if (!connected) {\n throw new Error('Not connected to UseAI server');\n }\n\n pendingMessagesRef.current.push({ message, options });\n await processMessageQueue();\n }, [connected, processMessageQueue]);\n\n return { sendMessage };\n}\n","import { useRef } from 'react';\nimport type { ToolsDefinition, DefinedTool, ToolExecutionContext } 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, annotations) 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, ctx: ToolExecutionContext) => {\n const currentTool = latestToolsRef.current[name];\n if (!currentTool) {\n throw new Error(`Tool \"${name}\" no longer exists`);\n }\n return currentTool.fn(input, ctx);\n };\n\n // Create the stable _execute function\n const stableExecute = async (input: unknown, ctx: ToolExecutionContext) => {\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, ctx);\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 // Workflows are headless — no UI for runtime approval, so provide a no-op context\n const noopCtx = {\n requestApproval: async () => ({ approved: true }),\n };\n const result = await executeDefinedTool(currentWorkflow.tools, toolName, toolArgs, noopCtx);\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';\n/** @hidden */\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 { SendMessageOptions } from './hooks/useMessageQueue';\nexport type { DefinedTool, ToolsDefinition, ToolOptions, ToolAnnotations, ToolExecutionContext } 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 ChatMetadata,\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 { matchesMimeType, findTransformerPattern } from './fileUpload/mimeTypeMatcher';\nexport { processAttachments, clearTransformationCache } from './fileUpload/processAttachments';\nexport type {\n FileUploadBackend,\n FileUploadConfig,\n FileAttachment,\n PersistedFileMetadata,\n FileTransformer,\n FileTransformerContext,\n FileTransformerMap,\n FileProcessingStatus,\n FileProcessingState,\n} from './fileUpload/types';\nexport type { ProcessAttachmentsConfig } from './fileUpload/processAttachments';\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 system (registry + execution + waiters)\nexport { useToolSystem } from './hooks/useToolSystem';\nexport type {\n RegisterToolsOptions,\n PendingToolApproval,\n UseToolSystemOptions,\n UseToolSystemReturn,\n} from './hooks/useToolSystem';\n\n// Prompt state\nexport { usePromptState } from './hooks/usePromptState';\nexport type {\n UsePromptStateOptions,\n UsePromptStateReturn,\n} from './hooks/usePromptState';\n\n// Server events\nexport { useServerEvents } from './hooks/useServerEvents';\nexport type {\n UseServerEventsOptions,\n UseServerEventsReturn,\n ExecutingToolDisplay,\n} from './hooks/useServerEvents';\n\n// Message queue\nexport { useMessageQueue } from './hooks/useMessageQueue';\nexport type {\n UseMessageQueueOptions,\n UseMessageQueueReturn,\n} from './hooks/useMessageQueue';\n\n// Feedback\nexport { useFeedback } from './hooks/useFeedback';\nexport type {\n UseFeedbackOptions,\n UseFeedbackReturn,\n} from './hooks/useFeedback';\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","jsx","jsxs","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","useState","jsx","jsxs","jsx","jsxs","useRef","useState","useEffect","Fragment","jsx","jsxs","createContext","useContext","jsx","createContext","useContext","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","useRef","useState","useCallback","useMemo","useState","useCallback","useRef","useMemo","useEffect","useState","useEffect","useRef","useCallback","useState","useCallback","useRef","EventType","useState","useRef","useCallback","EventType","useCallback","useRef","useEffect","Fragment","jsx","jsxs","createContext","useState","useCallback","useRef","useEffect","useContext","useRef","useState","useRef","useMemo","useEffect","useCallback","EventType","error","useState","useCallback","useRef","useEffect","EventType","uuidv4","useState","useRef","useCallback","useEffect","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/useAI.ts","../src/providers/useAIProvider.tsx","../src/theme/strings.ts","../src/theme/theme.ts","../src/components/UseAIFloatingButton.tsx","../src/components/UseAIChatPanel.tsx","../src/utils/messageContent.ts","../src/components/MarkdownContent.tsx","../src/components/Spinner.tsx","../src/components/ProgressBar.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/fileUpload/mimeTypeMatcher.ts","../src/fileUpload/EmbedFileUploadBackend.ts","../src/fileUpload/processAttachments.ts","../src/hooks/useDropdownState.tsx","../src/components/ToolApprovalDialog.tsx","../src/components/UseAIFloatingChatWrapper.tsx","../src/components/UseAIChat.tsx","../src/client.ts","../src/defineTool.ts","../src/providers/chatRepository/types.ts","../src/providers/chatRepository/LocalStorageChatRepository.ts","../src/hooks/useChatManagement.ts","../src/hooks/useAgentSelection.ts","../src/hooks/useCommandManagement.ts","../src/commands/LocalStorageCommandRepository.ts","../src/hooks/useToolSystem.ts","../src/hooks/usePromptState.ts","../src/hooks/useFeedback.ts","../src/hooks/useServerEvents.ts","../src/types.ts","../src/hooks/useMessageQueue.ts","../src/hooks/useStableTools.ts","../src/useAIWorkflow.ts","../src/index.ts"],"sourcesContent":["import { useState, useEffect, useLayoutEffect, 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, signalReady, registerWaiter, unregisterWaiter } = tools;\n const { update: updatePrompt } = 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 using useLayoutEffect for deterministic timing\n // This runs synchronously after DOM mutations, allowing waitForToolsToStabilize\n // to reliably detect when all components have finished registering.\n useLayoutEffect(() => {\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 // Signal that this component has finished registering its tools\n // This is used by waitForToolsToStabilize to know when all components are ready\n signalReady(hookId.current);\n }\n\n return () => {\n if (stableTools) {\n unregisterTools(hookId.current);\n }\n };\n }, [enabled, toolsKey, stableTools, options.id, options.invisible, registerTools, unregisterTools, signalReady]);\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, AgentInfo, UseAIForwardedProps } from '../types';\nimport { UseAIFloatingButton } from '../components/UseAIFloatingButton';\nimport { UseAIChatPanel } from '../components/UseAIChatPanel';\nimport { UseAIFloatingChatWrapper, CloseButton } from '../components/UseAIFloatingChatWrapper';\nimport { __UseAIChatContext, type ChatUIContextValue } from '../components/UseAIChat';\nimport { UseAIClient } from '../client';\nimport { convertToolsToDefinitions, type ToolsDefinition } from '../defineTool';\nimport type { ChatRepository, Chat, ChatMetadata, CreateChatOptions, PersistedMessage, PersistedMessageContent, PersistedContentPart } from './chatRepository/types';\nimport { LocalStorageChatRepository } from './chatRepository/LocalStorageChatRepository';\nimport type { FileAttachment, FileUploadConfig, FileProcessingState } from '../fileUpload/types';\nimport { processAttachments } from '../fileUpload/processAttachments';\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 { useToolSystem, type RegisterToolsOptions } from '../hooks/useToolSystem';\nimport { usePromptState } from '../hooks/usePromptState';\nimport { useFeedback } from '../hooks/useFeedback';\nimport { useServerEvents } from '../hooks/useServerEvents';\nimport { useMessageQueue, type SendMessageOptions } from '../hooks/useMessageQueue';\nimport { ThemeContext, StringsContext, defaultTheme, defaultStrings } from '../theme';\nimport type { UseAITheme, UseAIStrings } from '../theme';\n\n// ── Context Types ───────────────────────────────────────────────────────────\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: (options?: CreateChatOptions) => 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 * Programmatically send a message to the chat.\n * Throws on failure (e.g., not connected).\n */\n sendMessage: (message: string, options?: SendMessageOptions) => Promise<void>;\n /** Get the current chat object. Metadata is frozen to prevent accidental mutation. */\n get: () => Promise<Chat | null>;\n /**\n * Update metadata for the current chat.\n * @param metadata Metadata to set/merge\n * @param overwrite If true, replaces all metadata instead of merging (default: false)\n */\n updateMetadata: (metadata: ChatMetadata, overwrite?: boolean) => 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 system context (from useToolSystem hook).\n */\nexport interface ToolRegistryContextValue {\n /** Registers tools for a specific component */\n register: (id: string, tools: ToolsDefinition, options?: RegisterToolsOptions) => void;\n /** Unregisters tools for a specific component */\n unregister: (id: string) => void;\n /** Signals that a component has completed registration in useLayoutEffect */\n signalReady: (id: 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 * 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}\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 system (registry, waiters, execution) */\n tools: ToolRegistryContextValue;\n /** Prompt management */\n prompts: PromptsContextValue;\n /** Chat management */\n chat: ChatContextValue;\n /** Agent selection */\n agents: AgentContextValue;\n /** Command management */\n commands: CommandContextValue;\n}\n\n/**\n * React context for UseAI provider state.\n * @internal Exported only for testing purposes. Use {@link useAIContext} 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 signalReady: () => {},\n registerWaiter: () => {},\n unregisterWaiter: () => {},\n },\n prompts: {\n update: () => {},\n },\n chat: {\n currentId: null,\n create: async () => '',\n load: async () => {},\n delete: async () => {},\n list: async () => [],\n clear: async () => {},\n sendMessage: async () => {},\n get: async () => null,\n updateMetadata: 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// ── Component Props ─────────────────────────────────────────────────────────\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: PersistedMessage[];\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 * Provider function for forwarded props (telemetry metadata, MCP headers, etc.).\n * Called before each message is sent. Can be sync or async.\n * Props from this provider are merged with message-level forwardedProps,\n * with message-level taking precedence.\n *\n * @example\n * ```tsx\n * <UseAIProvider\n * serverUrl=\"wss://your-server.com\"\n * forwardedPropsProvider={() => ({\n * mcpHeaders: {\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 * telemetryMetadata: {\n * userId: currentUser.id,\n * tenantId: tenant.id,\n * },\n * })}\n * >\n * <App />\n * </UseAIProvider>\n * ```\n */\n forwardedPropsProvider?: () => UseAIForwardedProps | Promise<UseAIForwardedProps>;\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 * Callback when the chat open state should change.\n * Called by programmatic actions like `sendMessage({ openChat: true })`.\n * Useful when `renderChat=false` and you control the chat panel's visibility externally.\n *\n * @example\n * ```tsx\n * const [sidebarOpen, setSidebarOpen] = useState(false);\n *\n * <UseAIProvider\n * serverUrl=\"ws://localhost:8081\"\n * renderChat={false}\n * onOpenChange={(isOpen) => {\n * // Sync with external sidebar state\n * setSidebarOpen(isOpen);\n * }}\n * >\n * <Sidebar isOpen={sidebarOpen} onClose={() => setSidebarOpen(false)}>\n * <UseAIChat />\n * </Sidebar>\n * </UseAIProvider>\n * ```\n */\n onOpenChange?: (isOpen: boolean) => void;\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// ── Provider Component ──────────────────────────────────────────────────────\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 forwardedPropsProvider,\n fileUploadConfig: fileUploadConfigProp,\n commandRepository,\n renderChat = true,\n theme: customTheme,\n strings: customStrings,\n visibleAgentIds,\n onOpenChange,\n}: UseAIProviderProps) {\n const fileUploadConfig = fileUploadConfigProp === false\n ? undefined\n : (fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG);\n\n const theme = { ...defaultTheme, ...customTheme };\n const strings = { ...defaultStrings, ...customStrings };\n\n // ── Core State ──────────────────────────────────────────────────────────\n\n const [connected, setConnected] = useState(false);\n const [isChatOpen, setIsChatOpen] = useState(false);\n const [messages, setMessages] = useState<PersistedMessage[]>([]);\n const [fileProcessingState, setFileProcessingState] = useState<FileProcessingState | null>(null);\n\n const handleSetChatOpen = useCallback((open: boolean) => {\n setIsChatOpen(open);\n onOpenChange?.(open);\n }, [onOpenChange]);\n\n const clientRef = useRef<UseAIClient | null>(null);\n const repositoryRef = useRef<ChatRepository>(\n chatRepository || new LocalStorageChatRepository()\n );\n\n // ── Hooks ───────────────────────────────────────────────────────────────\n\n const promptState = usePromptState({\n systemPrompt,\n clientRef,\n connected,\n });\n\n const toolSystem = useToolSystem({\n clientRef,\n buildState: promptState.buildStateFromPrompts,\n });\n\n const chatManagement = useChatManagement({\n repository: repositoryRef.current,\n clientRef,\n messages,\n setMessages,\n connected,\n });\n\n const serverEvents = useServerEvents({\n toolSystem,\n saveAIResponse: chatManagement.saveAIResponse,\n strings,\n });\n\n const feedback = useFeedback({\n clientRef,\n repository: repositoryRef.current,\n getDisplayedChatId: () => chatManagement.displayedChatId,\n setMessages,\n });\n\n const {\n availableAgents,\n defaultAgent,\n selectedAgent,\n setAgent,\n } = useAgentSelection({ clientRef, connected, visibleAgentIds });\n\n const {\n commands,\n refreshCommands,\n saveCommand,\n renameCommand,\n deleteCommand,\n } = useCommandManagement({ repository: commandRepository });\n\n // ── Client Lifecycle ────────────────────────────────────────────────────\n\n // Ref to always call the latest handleServerEvent from the stable subscription\n const handleServerEventRef = useRef(serverEvents.handleServerEvent);\n handleServerEventRef.current = serverEvents.handleServerEvent;\n\n useEffect(() => {\n console.log('[UseAIProvider] Initializing client with serverUrl:', serverUrl);\n const client = new UseAIClient(serverUrl);\n\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 await handleServerEventRef.current(client, event);\n });\n\n clientRef.current = client;\n\n return () => {\n unsubscribeConnection();\n unsubscribe();\n client.disconnect();\n };\n }, [serverUrl]);\n\n // ── Tool Registration Sync ──────────────────────────────────────────────\n\n const lastRegisteredToolsRef = useRef<string>('');\n\n useEffect(() => {\n const client = clientRef.current;\n if (!client || !client.isConnected()) return;\n\n if (!toolSystem.hasTools) {\n // All tools were unregistered (e.g., page navigation).\n // Clear stale tools from the client instance.\n if (lastRegisteredToolsRef.current !== '') {\n lastRegisteredToolsRef.current = '';\n client.registerTools([]);\n console.log('[Provider] All tools unregistered, clearing client tools');\n }\n return;\n }\n\n const toolKeys = Object.keys(toolSystem.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(toolSystem.aggregatedTools);\n console.log(`[Provider] Registering ${toolDefinitions.length} tools`);\n client.registerTools(toolDefinitions);\n } catch (err) {\n console.error('Failed to register tools:', err);\n }\n }, [toolSystem.hasTools, toolSystem.aggregatedTools, connected]);\n\n // ── Message Sending ─────────────────────────────────────────────────────\n\n const handleSendMessage = useCallback(async (message: string, attachments?: FileAttachment[], messageForwardedProps?: UseAIForwardedProps) => {\n if (!clientRef.current) return;\n\n serverEvents.clearStreamingText();\n\n const activatedChatId = chatManagement.activatePendingChat();\n const activeChatId = activatedChatId || chatManagement.currentChatId;\n\n serverEvents.streamingChatIdRef.current = activeChatId;\n\n let persistedContent: PersistedMessageContent = message;\n let multimodalContent: MultimodalContent[] | undefined;\n\n if (attachments && attachments.length > 0) {\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 if (activeChatId) {\n await chatManagement.saveUserMessage(activeChatId, persistedContent);\n }\n\n serverEvents.setLoading(true);\n\n try {\n const fileContent = await processAttachments(attachments, {\n getCurrentChat: chatManagement.getCurrentChat,\n backend: fileUploadConfig?.backend,\n transformers: fileUploadConfig?.transformers,\n onFileProgress: (_fileId, state) => {\n setFileProcessingState(state);\n },\n });\n\n multimodalContent = [];\n if (message.trim()) {\n multimodalContent.push({ type: 'text', text: message });\n }\n multimodalContent.push(...fileContent);\n } catch (error) {\n serverEvents.setLoading(false);\n throw error;\n } finally {\n setFileProcessingState(null);\n }\n } else {\n if (activeChatId) {\n await chatManagement.saveUserMessage(activeChatId, persistedContent);\n }\n serverEvents.setLoading(true);\n }\n\n const providerResult = forwardedPropsProvider ? forwardedPropsProvider() : {};\n const providerProps = providerResult instanceof Promise ? await providerResult : providerResult;\n\n const mergedForwardedProps = {\n ...providerProps,\n ...messageForwardedProps,\n };\n\n await clientRef.current.sendPrompt(\n message,\n multimodalContent,\n Object.keys(mergedForwardedProps).length > 0\n ? mergedForwardedProps\n : undefined\n );\n }, [chatManagement, serverEvents, fileUploadConfig, forwardedPropsProvider]);\n\n // ── Message Queue (programmatic sendMessage) ────────────────────────────\n\n const messageQueue = useMessageQueue({\n sendFn: handleSendMessage,\n createNewChat: chatManagement.createNewChat,\n setOpen: handleSetChatOpen,\n connected,\n loading: serverEvents.loading,\n hasPendingApproval: toolSystem.pendingApprovals.length > 0,\n });\n\n // ── Context Values ──────────────────────────────────────────────────────\n\n const value: UseAIContextValue = {\n serverUrl,\n connected,\n client: clientRef.current,\n tools: {\n register: toolSystem.registerTools,\n unregister: toolSystem.unregisterTools,\n signalReady: toolSystem.signalReady,\n registerWaiter: toolSystem.registerWaiter,\n unregisterWaiter: toolSystem.unregisterWaiter,\n },\n prompts: {\n update: promptState.updatePrompt,\n },\n chat: {\n currentId: chatManagement.currentChatId,\n create: chatManagement.createNewChat,\n load: chatManagement.loadChat,\n delete: chatManagement.deleteChat,\n list: chatManagement.listChats,\n clear: chatManagement.clearCurrentChat,\n sendMessage: messageQueue.sendMessage,\n get: chatManagement.getCurrentChat,\n updateMetadata: chatManagement.updateMetadata,\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 // ── Chat UI ─────────────────────────────────────────────────────────────\n\n const effectiveStreamingText = serverEvents.streamingChatIdRef.current === chatManagement.displayedChatId\n ? serverEvents.streamingText : '';\n\n const chatUIContextValue: ChatUIContextValue = {\n connected,\n loading: serverEvents.loading,\n sendMessage: handleSendMessage,\n messages,\n streamingText: effectiveStreamingText,\n suggestions: promptState.aggregatedSuggestions,\n fileUploadConfig,\n fileProcessing: fileProcessingState,\n history: {\n currentId: chatManagement.displayedChatId,\n create: chatManagement.createNewChat,\n load: chatManagement.loadChat,\n delete: chatManagement.deleteChat,\n list: chatManagement.listChats,\n get: chatManagement.getCurrentChat,\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: handleSetChatOpen,\n },\n tools: {\n executing: serverEvents.executingTool,\n pending: {\n tools: toolSystem.pendingApprovals,\n approveAll: toolSystem.approveAll,\n rejectAll: toolSystem.rejectAll,\n },\n },\n feedback: {\n enabled: feedback.enabled,\n submit: feedback.submitFeedback,\n },\n };\n\n const isUIDisabled = CustomButton === null || CustomChat === null;\n const ButtonComponent = isUIDisabled ? null : (CustomButton || UseAIFloatingButton);\n const hasCustomChat = CustomChat !== undefined && CustomChat !== null;\n\n const chatPanelProps = {\n onSendMessage: handleSendMessage,\n messages,\n loading: serverEvents.loading,\n connected,\n streamingText: effectiveStreamingText,\n currentChatId: chatManagement.displayedChatId,\n onNewChat: chatManagement.createNewChat,\n onLoadChat: chatManagement.loadChat,\n onDeleteChat: chatManagement.deleteChat,\n onListChats: chatManagement.listChats,\n suggestions: promptState.aggregatedSuggestions,\n availableAgents,\n defaultAgent,\n selectedAgent,\n onAgentChange: setAgent,\n fileUploadConfig,\n fileProcessing: fileProcessingState,\n commands,\n onSaveCommand: saveCommand,\n onRenameCommand: renameCommand,\n onDeleteCommand: deleteCommand,\n executingTool: serverEvents.executingTool,\n feedbackEnabled: feedback.enabled,\n onFeedback: feedback.submitFeedback,\n pendingApprovals: toolSystem.pendingApprovals,\n onApproveToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.approveAll : undefined,\n onRejectToolCall: toolSystem.pendingApprovals.length > 0 ? toolSystem.rejectAll : undefined,\n };\n\n const renderDefaultChat = () => {\n if (isUIDisabled) return null;\n return (\n <UseAIFloatingChatWrapper isOpen={isChatOpen} onClose={() => handleSetChatOpen(false)}>\n <UseAIChatPanel\n {...chatPanelProps}\n closeButton={<CloseButton onClick={() => handleSetChatOpen(false)} />}\n />\n </UseAIFloatingChatWrapper>\n );\n };\n\n const renderCustomChat = () => {\n if (!CustomChat) return null;\n return (\n <CustomChat\n isOpen={isChatOpen}\n onClose={() => handleSetChatOpen(false)}\n onSendMessage={handleSendMessage}\n messages={messages}\n loading={serverEvents.loading}\n connected={connected}\n suggestions={promptState.aggregatedSuggestions}\n availableAgents={availableAgents}\n defaultAgent={defaultAgent}\n selectedAgent={selectedAgent}\n onAgentChange={setAgent}\n />\n );\n };\n\n const renderBuiltInChat = () => {\n if (!renderChat) return null;\n return (\n <>\n {ButtonComponent && (\n <ButtonComponent\n onClick={() => handleSetChatOpen(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// ── Context Hook ────────────────────────────────────────────────────────────\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 */\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","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 /** Loading indicator text */\n thinking: 'Thinking',\n /** File processing indicator text (shown during file transformation like OCR) */\n processingFile: 'Processing file...',\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 // Tool execution status\n toolExecution: {\n /** Fallback messages when no tool title is provided (one randomly selected) */\n fallbackMessages: ['Working', 'Processing', 'Thinking'],\n },\n\n // Tool approval dialog\n toolApproval: {\n /** Title shown in the approval dialog */\n title: 'Confirmation Required',\n /** Message shown in the approval dialog. {toolName} is replaced with tool name. */\n message: '\"{toolName}\" is waiting for your approval.',\n /** Message shown when multiple tools are awaiting approval. {count} is replaced with number. */\n batchMessage: '{count} actions are waiting for your approval.',\n /** Label for approve button */\n approve: 'Allow',\n /** Label for approve all button (batch mode) */\n approveAll: 'Allow All',\n /** Label for reject button */\n reject: 'Deny',\n /** Label for reject all button (batch mode) */\n rejectAll: 'Deny All',\n /** Label for showing tool arguments */\n showDetails: 'Show details',\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, PersistedMessage, PersistedMessageContent, PersistedContentPart, MessageDisplayMode } from '../providers/chatRepository/types';\nimport { getTextFromContent } from '../utils/messageContent';\nimport type { AgentInfo, FeedbackValue, ToolAnnotations } from '../types';\nimport type { FileAttachment, FileUploadConfig, FileProcessingState } 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';\nimport { ToolApprovalDialog } from './ToolApprovalDialog';\n\n// Re-export types for backwards compatibility\nexport type UseAIChatPanelStrings = UseAIStrings;\nexport type UseAIChatPanelTheme = UseAITheme;\n\n/**\n * @deprecated Use `PersistedMessage` directly instead.\n */\ntype Message = PersistedMessage;\n\n/**\n * Props for the FeedbackButton component.\n */\ninterface FeedbackButtonProps {\n /** The type of feedback this button represents */\n type: 'upvote' | 'downvote';\n /** Whether this feedback type is currently selected */\n isSelected: boolean;\n /** Callback when clicked */\n onClick: () => void;\n /** Color when selected */\n selectedColor: string;\n /** Color when not selected */\n unselectedColor: string;\n}\n\n/**\n * Thumbs up/down feedback button with pop animation.\n */\nfunction FeedbackButton({ type, isSelected, onClick, selectedColor, unselectedColor }: FeedbackButtonProps) {\n const buttonRef = useRef<HTMLButtonElement>(null);\n\n const handleClick = () => {\n // Pop animation only on select, not de-select\n if (!isSelected && buttonRef.current) {\n buttonRef.current.style.transform = 'scale(1.3)';\n setTimeout(() => {\n if (buttonRef.current) {\n buttonRef.current.style.transform = 'scale(1)';\n }\n }, 150);\n }\n onClick();\n };\n\n const thumbsUpPath = \"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\";\n const thumbsDownPath = \"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\";\n\n return (\n <button\n ref={buttonRef}\n data-testid={`feedback-${type}`}\n onClick={handleClick}\n title={type === 'upvote' ? 'Good response' : 'Poor response'}\n style={{\n background: 'transparent',\n border: 'none',\n padding: '4px',\n cursor: 'pointer',\n color: isSelected ? selectedColor : unselectedColor,\n opacity: isSelected ? 1 : 0.5,\n transition: 'all 0.15s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '4px',\n transform: 'scale(1)',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (!isSelected) {\n e.currentTarget.style.opacity = '0.8';\n e.currentTarget.style.color = selectedColor;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (!isSelected) {\n e.currentTarget.style.opacity = '0.5';\n e.currentTarget.style.color = unselectedColor;\n }\n }}\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill={isSelected ? 'currentColor' : 'none'}\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d={type === 'upvote' ? thumbsUpPath : thumbsDownPath} />\n </svg>\n </button>\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 /** Gets the current chat */\n onGetChat?: () => Promise<Chat | null>;\n suggestions?: string[];\n availableAgents?: AgentInfo[];\n defaultAgent?: string | null;\n selectedAgent?: string | null;\n onAgentChange?: (agentId: string | null) => void;\n fileUploadConfig?: FileUploadConfig;\n /** File processing state for send-time transformations (e.g., OCR) */\n fileProcessing?: FileProcessingState | null;\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 /** Currently executing tool info for status display */\n executingTool?: { displayText: string } | null;\n /** Whether feedback buttons are enabled (requires Langfuse on server) */\n feedbackEnabled?: boolean;\n /** Callback when user submits feedback on a message */\n onFeedback?: (messageId: string, traceId: string, feedback: FeedbackValue) => void;\n /** Pending tool approvals awaiting user confirmation */\n pendingApprovals?: Array<{\n toolCallId: string;\n toolCallName: string;\n toolCallArgs: Record<string, unknown>;\n annotations?: ToolAnnotations;\n }>;\n /** Callback to approve all pending tool calls */\n onApproveToolCall?: () => void;\n /** Callback to reject all pending tool calls */\n onRejectToolCall?: (reason?: string) => void;\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 onGetChat,\n suggestions,\n availableAgents,\n defaultAgent,\n selectedAgent,\n onAgentChange,\n fileUploadConfig,\n fileProcessing,\n commands = [],\n onSaveCommand,\n onRenameCommand,\n onDeleteCommand,\n closeButton,\n executingTool,\n feedbackEnabled,\n onFeedback,\n pendingApprovals = [],\n onApproveToolCall,\n onRejectToolCall,\n}: UseAIChatPanelProps) {\n const strings = useStrings();\n const theme = useTheme();\n\n // Filter out internal protocol messages at render time:\n // - Tool result messages (role === 'tool')\n // - Intermediate assistant messages that only contain tool calls (no user-visible text)\n const displayMessages = messages.filter(m => {\n if (m.role === 'tool') return false;\n if (m.role === 'assistant' && m.toolCalls && m.toolCalls.length > 0 && !getTextFromContent(m.content)) return false;\n return true;\n });\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 const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Message hover state for save button\n const [hoveredMessageId, setHoveredMessageId] = useState<string | null>(null);\n\n // File upload hook - includes processing state for transformation progress\n const {\n attachments,\n fileError,\n enabled: fileUploadEnabled,\n acceptedTypes,\n processingState: fileProcessingState,\n fileInputRef,\n removeAttachment,\n clearAttachments,\n openFilePicker,\n handleFileInputChange,\n getDropZoneProps,\n DropZoneOverlay,\n } = useFileUpload({\n getCurrentChat: onGetChat ?? (async () => null),\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 const maxTextareaHeight = 160;\n\n // Auto-resize textarea based on content\n useEffect(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n // Reset to single row to measure actual content height\n textarea.style.height = 'auto';\n\n // Calculate new height based on scrollHeight (clamped to max)\n const newHeight = Math.min(textarea.scrollHeight, maxTextareaHeight);\n textarea.style.height = `${newHeight}px`;\n }, [input]);\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 (displayMessages.length > 0) {\n const firstUserMsg = displayMessages.find((m) => m.role === 'user');\n if (firstUserMsg) {\n const textContent = getTextFromContent(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 && displayMessages.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 {displayMessages.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 {displayMessages.map((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 = getTextFromContent(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={getTextFromContent(message.content)} />\n ) : (\n getTextFromContent(message.content)\n )}\n </div>\n {/* Inline save command UI - glued to chat bubble */}\n {slashCommands.renderInlineSaveUI({\n messageId: message.id,\n messageText: getTextFromContent(message.content),\n })}\n </div>\n {/* Feedback buttons - only for assistant messages with traceId */}\n {message.role === 'assistant' && message.traceId && feedbackEnabled && onFeedback && (\n <div\n data-testid=\"feedback-buttons\"\n style={{\n display: 'flex',\n gap: '4px',\n marginTop: '4px',\n padding: '0 4px',\n }}\n >\n <FeedbackButton\n type=\"upvote\"\n isSelected={message.feedback === 'upvote'}\n onClick={() => {\n const newFeedback = message.feedback === 'upvote' ? null : 'upvote';\n onFeedback(message.id, message.traceId!, newFeedback);\n }}\n selectedColor={theme.primaryColor}\n unselectedColor={theme.secondaryTextColor}\n />\n <FeedbackButton\n type=\"downvote\"\n isSelected={message.feedback === 'downvote'}\n onClick={() => {\n const newFeedback = message.feedback === 'downvote' ? null : 'downvote';\n onFeedback(message.id, message.traceId!, newFeedback);\n }}\n selectedColor={theme.errorTextColor}\n unselectedColor={theme.secondaryTextColor}\n />\n </div>\n )}\n <div\n style={{\n fontSize: '11px',\n color: theme.secondaryTextColor,\n marginTop: '4px',\n padding: '0 4px',\n }}\n >\n {message.createdAt.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 ) : fileProcessing && fileProcessing.status === 'processing' ? (\n <div>\n <span style={{ opacity: 0.6 }}>{strings.input.processingFile}</span>\n {fileProcessing.progress != null && (\n <>\n <span style={{ opacity: 0.6, marginLeft: '4px' }}>\n {Math.round(fileProcessing.progress)}%\n </span>\n <div style={{\n marginTop: '6px',\n height: '4px',\n borderRadius: '2px',\n background: theme.borderColor,\n overflow: 'hidden',\n }}>\n <div style={{\n height: '100%',\n width: `${fileProcessing.progress}%`,\n borderRadius: '2px',\n background: theme.primaryColor,\n transition: 'width 0.3s ease',\n }} />\n </div>\n </>\n )}\n {fileProcessing.progress == null && (\n <span className=\"dots\" style={{ marginLeft: '4px' }}>...</span>\n )}\n </div>\n ) : (\n <span className=\"dots\" style={{ opacity: 0.6 }}>...</span>\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 processingState={fileProcessingState.get(attachment.id)}\n />\n ))}\n </div>\n )}\n\n {/* Hidden file input - always rendered */}\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n data-testid=\"file-input\"\n style={{ display: 'none' }}\n onChange={handleFileInputChange}\n accept={acceptedTypes?.join(',')}\n />\n\n {/* Tool approval dialog - replaces input when pending */}\n {pendingApprovals.length > 0 && onApproveToolCall && onRejectToolCall ? (\n <ToolApprovalDialog\n toolCallName={pendingApprovals[0].toolCallName}\n toolCallArgs={pendingApprovals[0].toolCallArgs}\n annotations={pendingApprovals[0].annotations}\n toolCount={pendingApprovals.length}\n pendingTools={pendingApprovals}\n onApprove={onApproveToolCall}\n onReject={onRejectToolCall}\n theme={theme}\n strings={strings}\n />\n ) : (\n /* Input container - single border around everything */\n <div\n style={{\n border: `1px solid ${theme.borderColor}`,\n borderRadius: '12px',\n background: theme.backgroundColor,\n overflow: 'hidden',\n position: 'relative',\n }}\n >\n {/* Command Autocomplete */}\n {slashCommands.AutocompleteComponent}\n\n {/* Textarea area */}\n <textarea\n ref={textareaRef}\n data-testid=\"chat-input\"\n className=\"chat-input\"\n value={input}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n placeholder={\n !connected\n ? strings.input.connectingPlaceholder\n : loading\n ? `${executingTool?.displayText ?? strings.input.thinking}...`\n : strings.input.placeholder\n }\n disabled={!connected || loading || pendingApprovals.length > 0}\n rows={1}\n style={{\n width: '100%',\n padding: '10px 14px 6px',\n border: 'none',\n fontSize: '14px',\n lineHeight: '1.4',\n resize: 'none',\n maxHeight: `${maxTextareaHeight}px`,\n fontFamily: 'inherit',\n outline: 'none',\n background: 'transparent',\n overflowY: 'auto',\n boxSizing: 'border-box',\n }}\n />\n\n {/* Bottom toolbar - fixed */}\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '4px 8px',\n }}\n >\n {/* Left side - file picker */}\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n {fileUploadEnabled && (\n <button\n data-testid=\"file-picker-button\"\n onClick={openFilePicker}\n disabled={!connected || loading || pendingApprovals.length > 0}\n style={{\n padding: '4px',\n background: 'transparent',\n border: `1px solid ${theme.borderColor}`,\n borderRadius: '50%',\n cursor: connected && !loading && pendingApprovals.length === 0 ? 'pointer' : 'not-allowed',\n color: theme.secondaryTextColor,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '28px',\n height: '28px',\n transition: 'all 0.15s',\n opacity: connected && !loading && pendingApprovals.length === 0 ? 1 : 0.5,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (connected && !loading && pendingApprovals.length === 0) {\n e.currentTarget.style.color = theme.primaryColor;\n e.currentTarget.style.borderColor = theme.primaryColor;\n }\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.color = theme.secondaryTextColor;\n e.currentTarget.style.borderColor = theme.borderColor;\n }}\n title={strings.fileUpload.attachFiles}\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n </button>\n )}\n </div>\n\n {/* Right side - send button */}\n <button\n data-testid=\"chat-send-button\"\n className=\"chat-send-button\"\n onClick={handleSend}\n disabled={!connected || loading || pendingApprovals.length > 0 || (!input.trim() && attachments.length === 0)}\n style={{\n padding: '6px',\n background: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0)\n ? theme.primaryGradient\n : theme.buttonDisabledBackground,\n color: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0) ? 'white' : theme.secondaryTextColor,\n border: 'none',\n borderRadius: '50%',\n cursor: connected && !loading && pendingApprovals.length === 0 && (input.trim() || attachments.length > 0) ? 'pointer' : 'not-allowed',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '32px',\n height: '32px',\n transition: 'all 0.2s',\n }}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <line x1=\"12\" y1=\"19\" x2=\"12\" y2=\"5\" />\n <polyline points=\"5 12 12 5 19 12\" />\n </svg>\n </button>\n </div>\n </div>\n )}\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 type { PersistedMessageContent } from '../providers/chatRepository/types';\n\n/**\n * Extracts text content from persisted message content.\n * Handles both simple string content and multimodal content arrays.\n */\nexport function 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","import React from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\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 remarkPlugins={[remarkGfm]}\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 // Render images as links to prevent automatic HTTP requests.\n // <img> tags fire GET requests on render, which could be exploited\n // via prompt injection to exfiltrate sensitive data through URLs.\n img: ({ src, alt }) => (\n <a\n href={src}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{\n color: 'inherit',\n textDecoration: 'underline',\n textUnderlineOffset: '2px',\n }}\n >\n {alt || 'Image'}\n </a>\n ),\n }}\n >\n {content}\n </ReactMarkdown>\n );\n}\n","import React from 'react';\n\n/**\n * Props for the Spinner component.\n */\nexport interface SpinnerProps {\n /** Size of the spinner in pixels (default: 16) */\n size?: number;\n /** Color of the spinner (default: currentColor) */\n color?: string;\n /** Color of the track behind the spinner (default: same as color with 0.25 opacity) */\n trackColor?: string;\n /** Stroke width (default: 2) */\n strokeWidth?: number;\n}\n\n/**\n * A circular spinner component for indeterminate progress.\n * Matches the visual style of CircularProgress.\n */\nexport function Spinner({\n size = 16,\n color = 'currentColor',\n trackColor,\n strokeWidth = 2,\n}: SpinnerProps) {\n const radius = 10;\n const circumference = 2 * Math.PI * radius;\n\n return (\n <svg\n data-testid=\"spinner\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n style={{\n animation: 'use-ai-spin 1s linear infinite',\n }}\n >\n <style>\n {`\n @keyframes use-ai-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n `}\n </style>\n {/* Background track */}\n <circle\n cx=\"12\"\n cy=\"12\"\n r={radius}\n fill=\"none\"\n stroke={trackColor || color}\n strokeWidth={strokeWidth}\n opacity={trackColor ? 1 : 0.25}\n />\n {/* Spinning arc (25% of circle) */}\n <circle\n cx=\"12\"\n cy=\"12\"\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n strokeDashoffset={circumference * 0.75}\n style={{\n transformOrigin: 'center',\n }}\n />\n </svg>\n );\n}\n","import React from 'react';\n\n/**\n * Props for the CircularProgress component.\n */\nexport interface ProgressBarProps {\n /** Progress value from 0 to 100 */\n progress: number;\n /** Size of the progress indicator in pixels (default: 16) */\n size?: number;\n /** Color of the progress fill (default: currentColor) */\n color?: string;\n /** Color of the track behind the progress (default: same as color with 0.25 opacity) */\n trackColor?: string;\n /** Stroke width (default: 2) */\n strokeWidth?: number;\n}\n\n/**\n * A circular progress indicator component for determinate progress (0-100%).\n * Matches the visual style of Spinner.\n */\nexport function ProgressBar({\n progress,\n size = 16,\n color = 'currentColor',\n trackColor,\n strokeWidth = 2,\n}: ProgressBarProps) {\n const clampedProgress = Math.min(100, Math.max(0, progress));\n const radius = 10;\n const circumference = 2 * Math.PI * radius;\n const strokeDashoffset = circumference * (1 - clampedProgress / 100);\n\n return (\n <svg\n data-testid=\"progress-bar\"\n role=\"progressbar\"\n aria-valuenow={clampedProgress}\n aria-valuemin={0}\n aria-valuemax={100}\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n style={{\n transform: 'rotate(-90deg)',\n }}\n >\n {/* Background track */}\n <circle\n cx=\"12\"\n cy=\"12\"\n r={radius}\n fill=\"none\"\n stroke={trackColor || color}\n strokeWidth={strokeWidth}\n opacity={trackColor ? 1 : 0.25}\n />\n {/* Progress arc */}\n <circle\n cx=\"12\"\n cy=\"12\"\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n strokeDashoffset={strokeDashoffset}\n style={{\n transition: 'stroke-dashoffset 0.2s ease',\n }}\n />\n </svg>\n );\n}\n","import React from 'react';\nimport type { FileAttachment, FileProcessingState } from '../fileUpload/types';\nimport { useTheme } from '../theme';\nimport { Spinner } from './Spinner';\nimport { ProgressBar } from './ProgressBar';\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 /** Processing state for this file (for transformation progress) */\n processingState?: FileProcessingState;\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 * - Shows processing overlay with spinner or progress bar\n */\nexport function FileChip({ attachment, onRemove, disabled, processingState }: FileChipProps) {\n const theme = useTheme();\n const { file, preview } = attachment;\n const isImage = file.type.startsWith('image/');\n const isProcessing = processingState?.status === 'processing';\n const hasError = processingState?.status === 'error';\n const progress = processingState?.progress;\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 position: 'relative',\n opacity: isProcessing ? 0.7 : 1,\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 || isProcessing}\n style={{\n background: 'transparent',\n border: 'none',\n padding: '2px 4px',\n cursor: disabled || isProcessing ? 'not-allowed' : 'pointer',\n color: theme.placeholderTextColor,\n fontSize: '16px',\n lineHeight: 1,\n borderRadius: '4px',\n transition: 'all 0.15s',\n opacity: disabled || isProcessing ? 0.5 : 1,\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n if (!disabled && !isProcessing) {\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\n {/* Processing overlay */}\n {isProcessing && (\n <div\n data-testid=\"file-chip-processing\"\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'rgba(255, 255, 255, 0.7)',\n borderRadius: 'inherit',\n }}\n >\n {progress !== undefined ? (\n <ProgressBar progress={progress} size={16} color={theme.secondaryTextColor} />\n ) : (\n <Spinner size={16} color={theme.secondaryTextColor} />\n )}\n </div>\n )}\n\n {/* Error indicator */}\n {hasError && (\n <div\n data-testid=\"file-chip-error\"\n style={{\n position: 'absolute',\n bottom: '-2px',\n right: '-2px',\n width: '12px',\n height: '12px',\n borderRadius: '50%',\n background: '#ef4444',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '8px',\n color: 'white',\n fontWeight: 'bold',\n }}\n >\n !\n </div>\n )}\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, FileProcessingState, FileTransformerContext, FileTransformerMap } from '../fileUpload/types';\nimport type { Chat } from '../providers/chatRepository/types';\nimport { DEFAULT_MAX_FILE_SIZE } from '../fileUpload/types';\nimport { findTransformerPattern } from '../fileUpload/mimeTypeMatcher';\nimport { getTransformedContent } from '../fileUpload/processAttachments';\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 /** Function to get the current chat (for transformer context) */\n getCurrentChat: () => Promise<Chat | null>;\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 /** Processing state for each file (by attachment ID) */\n processingState: Map<string, FileProcessingState>;\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 getCurrentChat,\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 [processingState, setProcessingState] = useState<Map<string, FileProcessingState>>(new Map());\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 const transformers = config?.transformers;\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 setProcessingState(new Map());\n }, [resetDependency]);\n\n /**\n * Runs a transformer for a file attachment.\n * Updates processing state and attachment with transformed content.\n * Uses centralized cache to avoid re-transforming the same file.\n */\n const runTransformer = useCallback(async (\n attachmentId: string,\n file: File,\n transformerKey: string\n ) => {\n const transformer = transformers?.[transformerKey];\n if (!transformer) return;\n\n // Set initial processing state\n setProcessingState(prev => new Map(prev).set(attachmentId, { status: 'processing' }));\n\n try {\n // Get current chat for context\n const chat = await getCurrentChat();\n const context: FileTransformerContext = { chat };\n\n const [transformedContent] = await getTransformedContent([file], transformer, context, (progress) => {\n setProcessingState(prev => new Map(prev).set(attachmentId, {\n status: 'processing',\n progress,\n }));\n });\n\n // Update attachment with transformed content\n setAttachments(prev => prev.map(a =>\n a.id === attachmentId ? { ...a, transformedContent } : a\n ));\n\n // Mark as done\n setProcessingState(prev => new Map(prev).set(attachmentId, { status: 'done' }));\n } catch (error) {\n console.error(`[useFileUpload] Transformation failed for ${file.name}:`, error);\n setProcessingState(prev => new Map(prev).set(attachmentId, { status: 'error' }));\n }\n }, [getCurrentChat, transformers]);\n\n /**\n * Validates and adds files to attachments.\n * If a transformer matches the file type, transformation starts immediately.\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 // Create attachment\n const attachmentId = uuidv4();\n const attachment: FileAttachment = {\n id: attachmentId,\n file,\n preview,\n };\n\n // Add to attachments\n setAttachments(prev => [...prev, attachment]);\n\n // Check for transformer and start transformation immediately\n const transformerKey = findTransformerPattern(file.type, transformers);\n if (transformerKey) {\n // Run transformer in background (don't await - let it update state as it progresses)\n runTransformer(attachmentId, file, transformerKey);\n }\n }\n }, [maxFileSize, acceptedTypes, strings, transformers, runTransformer]);\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 setProcessingState(prev => {\n const next = new Map(prev);\n next.delete(id);\n return next;\n });\n }, []);\n\n /**\n * Clears all attachments.\n */\n const clearAttachments = useCallback(() => {\n setAttachments([]);\n setProcessingState(new Map());\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 processingState,\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","import type { Chat } from '../providers/chatRepository/types';\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 * Transformed content (if a transformer matched this file's MIME type).\n * Populated asynchronously after attachment - check processingState for status.\n */\n transformedContent?: 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 * Context provided to file transformers.\n */\nexport interface FileTransformerContext {\n /** The current chat (includes metadata) */\n chat: Chat | null;\n}\n\n/**\n * A transformer that converts files into string representations for the AI.\n *\n * Receives all files that were matched to this transformer instance\n * and returns one string per file in the same order.\n *\n * @example\n * ```typescript\n * const pdfTransformer: FileTransformer = {\n * transform: async (files, context) =>\n * Promise.all(files.map(f => extractText(f))),\n * };\n * ```\n */\nexport interface FileTransformer {\n /**\n * Transform files into string representations for the AI.\n *\n * @param files - The files to transform (all matched to this transformer instance)\n * @param context - Context including the current chat and its metadata\n * @param onProgress - Optional callback for reporting progress (0-100).\n * If called, UI shows progress bar; otherwise shows spinner.\n * @returns One string per input file, in the same order\n * @throws If transformation fails\n */\n transform(\n files: File[],\n context: FileTransformerContext,\n onProgress?: (progress: number) => void\n ): Promise<string[]>;\n}\n\n/**\n * Map of MIME type patterns to transformers.\n *\n * Keys are MIME type patterns:\n * - Exact match: 'application/pdf'\n * - Partial wildcard: 'image/*'\n * - Global wildcard: '*' or '*\\/*'\n *\n * When multiple patterns match, the most specific one wins:\n * 1. Exact match (e.g., 'application/pdf')\n * 2. Partial wildcard (e.g., 'image/*')\n * 3. Global wildcard ('*' or '*\\/*')\n */\nexport type FileTransformerMap = Record<string, FileTransformer>;\n\n/**\n * Status of file processing during send.\n */\nexport type FileProcessingStatus = 'idle' | 'processing' | 'done' | 'error';\n\n/**\n * Processing state for a file attachment.\n */\nexport interface FileProcessingState {\n status: FileProcessingStatus;\n /** Progress 0-100, or undefined for indeterminate (spinner) */\n progress?: number;\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 * Map of MIME type patterns to transformers.\n * Files matching a transformer pattern will be converted to text\n * before being sent to the AI.\n */\n transformers?: FileTransformerMap;\n}\n","import type { FileTransformerMap } from './types';\n\n/**\n * Check if a MIME type matches a pattern.\n * Supports exact match and wildcard patterns ending with '*'.\n *\n * Examples:\n * - 'application/pdf' matches 'application/pdf' (exact)\n * - 'image/png' matches 'image/*' (partial wildcard)\n * - 'text/plain' matches '*' (global wildcard)\n * - 'text/plain' matches '*\\/*' (global wildcard)\n */\nexport function matchesMimeType(mimeType: string, pattern: string): boolean {\n // Exact match\n if (!pattern.includes('*')) {\n return mimeType === pattern;\n }\n\n // Wildcard match: convert pattern to regex\n // 'image/*' -> /^image\\/.*$/\n // '*' -> /^.*$/\n // '*/*' -> /^.*\\/.*$/\n const regexPattern = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // Escape regex special chars (except *)\n .replace(/\\*/g, '.*'); // Convert * to .*\n const regex = new RegExp(`^${regexPattern}$`);\n return regex.test(mimeType);\n}\n\n/**\n * Find the most specific transformer pattern key for a MIME type.\n * Returns the pattern string (e.g., 'application/pdf', 'image/*') or undefined.\n *\n * Specificity rules:\n * 1. Exact match (no wildcard) always wins\n * 2. Among wildcard patterns, longer pattern = more specific\n *\n * Example for 'image/png':\n * - 'image/png' (exact, wins)\n * - 'image/*' (length 7, second)\n * - '*' (length 1, last)\n */\nexport function findTransformerPattern(\n mimeType: string,\n transformers: FileTransformerMap | undefined\n): string | undefined {\n if (!transformers) {\n return undefined;\n }\n\n let bestKey: string | undefined;\n let bestIsExact = false;\n let bestLength = -1;\n\n for (const pattern of Object.keys(transformers)) {\n if (!matchesMimeType(mimeType, pattern)) {\n continue;\n }\n\n const isExact = !pattern.includes('*');\n\n // Exact match always wins over wildcard\n if (isExact && !bestIsExact) {\n bestKey = pattern;\n bestIsExact = true;\n bestLength = pattern.length;\n continue;\n }\n\n // If both are exact or both are wildcard, longer pattern wins\n if (isExact === bestIsExact && pattern.length > bestLength) {\n bestKey = pattern;\n bestLength = pattern.length;\n }\n }\n\n return bestKey;\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 type { MultimodalContent } from '@meetsmore-oss/use-ai-core';\nimport type {\n FileAttachment,\n FileUploadBackend,\n FileTransformerMap,\n FileTransformer,\n FileTransformerContext,\n FileProcessingState,\n} from './types';\nimport type { Chat } from '../providers/chatRepository/types';\nimport { findTransformerPattern } from './mimeTypeMatcher';\nimport { EmbedFileUploadBackend } from './EmbedFileUploadBackend';\n\n/**\n * Group items by a key derived from each item.\n * Based on the Map.groupBy proposal polyfill.\n */\nfunction groupBy<K, V>(items: Iterable<V>, keyFn: (item: V) => K): Map<K, V[]> {\n const map = new Map<K, V[]>();\n for (const item of items) {\n const key = keyFn(item);\n const list = map.get(key);\n list ? list.push(item) : map.set(key, [item]);\n }\n return map;\n}\n\n/**\n * Configuration for processing file attachments.\n */\nexport interface ProcessAttachmentsConfig {\n /** Function to get the current chat (for transformer context) */\n getCurrentChat: () => Promise<Chat | null>;\n /** Backend for converting files to URLs (default: EmbedFileUploadBackend) */\n backend?: FileUploadBackend;\n /** Map of MIME type patterns to transformers */\n transformers?: FileTransformerMap;\n /** Called when a file's processing state changes */\n onFileProgress?: (fileId: string, state: FileProcessingState) => void;\n}\n\n/**\n * In-memory cache for transformed file content.\n * Keyed by a SHA-256 hash of the file group identity.\n */\nconst transformationCache = new Map<string, string[]>();\n\n/**\n * Generate a cache key fragment for a single file based on its identity.\n */\nfunction getFileCacheKey(file: File): string {\n return `${file.name}:${file.size}:${file.lastModified}`;\n}\n\n/**\n * Hash a group of files into a fixed-length cache key.\n * Each file is identified by name, size, and lastModified, then the\n * combined string is hashed with SHA-256 to keep key size constant\n * regardless of how many files are in the group.\n *\n * Order matters: [A, B] and [B, A] produce different cache keys because\n * transformer results are positional (results[i] corresponds to files[i]).\n *\n * @example\n * // Input: [\"report.pdf:1024:1700000000\", \"scan.pdf:2048:1700000001\"]\n * // Hashed: \"a3f1...b7c2\" (64-char hex string)\n */\nasync function hashGroupCacheKey(files: File[]): Promise<string> {\n const raw = files.map(getFileCacheKey).join(', ');\n const data = new TextEncoder().encode(raw);\n const hash = await crypto.subtle.digest('SHA-256', data);\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Get transformed content for files, using cache when possible.\n * Results are cached per group (same set of files in the same order = cache hit).\n *\n * @param files - The files to transform (must all belong to the same transformer)\n * @param transformer - The transformer to use\n * @param context - Context for the transformer (including current chat)\n * @param onProgress - Optional progress callback (0-100)\n * @returns Transformer results (one or more strings, depending on the transformer)\n */\nexport async function getTransformedContent(\n files: File[],\n transformer: FileTransformer,\n context: FileTransformerContext,\n onProgress?: (progress: number) => void\n): Promise<string[]> {\n if (files.length === 0) {\n return [];\n }\n\n const cacheKey = await hashGroupCacheKey(files);\n const cached = transformationCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const results = await transformer.transform(files, context, onProgress);\n\n transformationCache.set(cacheKey, results);\n\n return results;\n}\n\n/**\n * Convert a single attachment to a multimodal content part.\n */\nasync function toContentPart(\n attachment: FileAttachment,\n backend: FileUploadBackend\n): Promise<MultimodalContent> {\n // Pre-transformed content (transformation at attach time)\n if (attachment.transformedContent !== undefined) {\n return {\n type: 'transformed_file',\n text: attachment.transformedContent,\n originalFile: {\n name: attachment.file.name,\n mimeType: attachment.file.type,\n size: attachment.file.size,\n },\n };\n }\n\n // No transformer — use URL encoding (fast, no progress needed)\n const url = await backend.prepareForSend(attachment.file);\n\n if (attachment.file.type.startsWith('image/')) {\n return { type: 'image', url };\n }\n\n return {\n type: 'file',\n url,\n mimeType: attachment.file.type,\n name: attachment.file.name,\n };\n}\n\n/**\n * Process file attachments into multimodal content for AI.\n * Handles transformation (with caching) or URL encoding.\n *\n * Files matching the same transformer are grouped and passed together\n * to `transformer.transform()`.\n *\n * @param attachments - The file attachments to process\n * @param config - Processing configuration\n * @returns Array of multimodal content parts\n * @throws On any processing error - caller should handle and show to user\n *\n * @example\n * ```typescript\n * const content = await processAttachments(attachments, {\n * transformers: { 'application/pdf': pdfTransformer },\n * onFileProgress: (id, state) => setProgress(prev => new Map(prev).set(id, state)),\n * });\n * ```\n */\nexport async function processAttachments(\n attachments: FileAttachment[],\n config: ProcessAttachmentsConfig\n): Promise<MultimodalContent[]> {\n const { getCurrentChat, backend = new EmbedFileUploadBackend(), transformers = {}, onFileProgress } = config;\n const contentParts: MultimodalContent[] = [];\n\n // Get current chat once for all transformers\n const chat = await getCurrentChat();\n const context: FileTransformerContext = { chat };\n\n // Group attachments by transformer pattern key (null = no transformer)\n const groups = groupBy(attachments, (attachment) =>\n attachment.transformedContent !== undefined\n ? null\n : findTransformerPattern(attachment.file.type, transformers) ?? null\n );\n\n for (const [key, groupAttachments] of groups) {\n // No transformer matched — convert directly (fast, no progress needed)\n if (key === null) {\n const parts = await Promise.all(\n groupAttachments.map((a) => toContentPart(a, backend))\n );\n contentParts.push(...parts);\n continue;\n }\n\n // Run the matching transformer for this group\n const transformer = transformers[key];\n const files = groupAttachments.map((a) => a.file);\n groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: 'processing' }));\n\n try {\n const results = await getTransformedContent(\n files,\n transformer,\n context,\n (progress) => {\n groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: 'processing', progress }));\n }\n );\n\n // Build content parts from transformer results and notify completion\n results.forEach((text, i) => {\n contentParts.push({\n type: 'transformed_file',\n text,\n originalFile: {\n name: files[i].name,\n mimeType: files[i].type,\n size: files[i].size,\n },\n });\n onFileProgress?.(groupAttachments[i].id, { status: 'done' });\n });\n } catch (error) {\n groupAttachments.forEach((a) => onFileProgress?.(a.id, { status: 'error' }));\n throw error;\n }\n }\n\n return contentParts;\n}\n\n/**\n * Clear the transformation cache.\n * Useful for testing or when memory needs to be freed.\n */\nexport function clearTransformationCache(): void {\n transformationCache.clear();\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, { useState } from 'react';\nimport type { ToolAnnotations } from '../types';\nimport type { UseAITheme, UseAIStrings } from '../theme';\n\n/**\n * A single pending tool approval item.\n */\nexport interface PendingToolItem {\n toolCallId: string;\n toolCallName: string;\n toolCallArgs: Record<string, unknown>;\n annotations?: ToolAnnotations;\n /** Optional message explaining why approval is needed (runtime approval) */\n message?: string;\n}\n\n/**\n * Props for the ToolApprovalDialog component.\n */\nexport interface ToolApprovalDialogProps {\n /** The name of the tool requesting approval (first tool in batch) */\n toolCallName: string;\n /** The arguments passed to the tool (first tool in batch) */\n toolCallArgs: Record<string, unknown>;\n /** Optional tool annotations (e.g., title for display) */\n annotations?: ToolAnnotations;\n /** Number of tool calls awaiting approval (for batch mode) */\n toolCount?: number;\n /** All pending tools (for batch mode details view) */\n pendingTools?: PendingToolItem[];\n /** Callback when user approves the tool execution (all tools in batch) */\n onApprove: () => void;\n /** Callback when user rejects the tool execution (all tools in batch) */\n onReject: (reason?: string) => void;\n /** Theme configuration for styling */\n theme: UseAITheme;\n /** Localized strings for UI text */\n strings: UseAIStrings;\n}\n\n/**\n * Confirmation dialog for destructive tool calls.\n * Replaces the chat input area when approval is needed.\n */\nexport function ToolApprovalDialog({\n toolCallName,\n toolCallArgs,\n annotations,\n toolCount = 1,\n pendingTools = [],\n onApprove,\n onReject,\n theme,\n strings,\n}: ToolApprovalDialogProps) {\n const [showDetails, setShowDetails] = useState(false);\n\n const displayName = annotations?.title || toolCallName;\n const isBatch = toolCount > 1;\n\n // Check if any pending tool has a runtime approval message\n const runtimeMessage = pendingTools.find(t => t.message)?.message;\n\n // For batch mode, show count; otherwise show the tool name\n // Runtime messages take priority over default generated messages\n const message = runtimeMessage\n ? runtimeMessage\n : isBatch\n ? strings.toolApproval.batchMessage?.replace('{count}', String(toolCount))\n ?? `${toolCount} actions are waiting for your approval`\n : strings.toolApproval.message.replace('{toolName}', displayName);\n\n // Get display name for a tool (use annotation title if available)\n const getToolDisplayName = (tool: PendingToolItem) =>\n tool.annotations?.title || tool.toolCallName;\n\n return (\n <div\n data-testid=\"tool-approval-dialog\"\n style={{\n border: `2px solid ${theme.primaryColor}`,\n borderRadius: '12px',\n background: theme.backgroundColor,\n overflow: 'hidden',\n }}\n >\n {/* Header with warning icon and message */}\n <div\n style={{\n padding: '12px 14px',\n borderBottom: `1px solid ${theme.borderColor}`,\n background: theme.assistantMessageBackground,\n }}\n >\n <div style={{\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n marginBottom: '4px',\n }}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke={theme.primaryColor} strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\" />\n <line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\" />\n <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\" />\n </svg>\n <span style={{\n fontWeight: 600,\n fontSize: '14px',\n color: theme.textColor,\n }}>\n {strings.toolApproval.title}\n </span>\n </div>\n <div style={{\n fontSize: '13px',\n color: theme.secondaryTextColor,\n paddingLeft: '24px',\n }}>\n {message}\n </div>\n </div>\n\n {/* Collapsible details */}\n <div style={{ padding: '8px 14px' }}>\n <button\n onClick={() => setShowDetails(!showDetails)}\n style={{\n background: 'transparent',\n border: 'none',\n padding: 0,\n cursor: 'pointer',\n fontSize: '12px',\n color: theme.secondaryTextColor,\n display: 'flex',\n alignItems: 'center',\n gap: '4px',\n }}\n >\n <svg\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n style={{\n transform: showDetails ? 'rotate(90deg)' : 'rotate(0deg)',\n transition: 'transform 0.15s',\n }}\n >\n <path d=\"M4 2L8 6L4 10\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\"/>\n </svg>\n {strings.toolApproval.showDetails}\n </button>\n {showDetails && (\n <div style={{\n marginTop: '6px',\n display: 'flex',\n flexDirection: 'column',\n gap: '8px',\n maxHeight: '200px',\n overflow: 'auto',\n }}>\n {/* Use pendingTools if available, otherwise create a single-item list from props */}\n {(pendingTools.length > 0 ? pendingTools : [{\n toolCallId: 'single',\n toolCallName,\n toolCallArgs,\n annotations,\n }]).map((tool, index) => (\n <div\n key={tool.toolCallId}\n style={{\n padding: '8px',\n background: theme.hoverBackground,\n borderRadius: '6px',\n }}\n >\n <div style={{\n fontSize: '12px',\n fontWeight: 500,\n color: theme.textColor,\n marginBottom: '4px',\n }}>\n {getToolDisplayName(tool)}\n </div>\n <pre style={{\n margin: 0,\n fontSize: '11px',\n overflow: 'auto',\n color: theme.secondaryTextColor,\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n }}>\n {JSON.stringify(tool.toolCallArgs, null, 2)}\n </pre>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Action buttons */}\n <div style={{\n display: 'flex',\n gap: '8px',\n padding: '8px 14px 12px',\n }}>\n <button\n data-testid=\"approve-tool-button\"\n onClick={onApprove}\n style={{\n flex: 1,\n padding: '8px 16px',\n borderRadius: '8px',\n border: 'none',\n background: theme.primaryGradient,\n color: 'white',\n fontWeight: 500,\n cursor: 'pointer',\n fontSize: '13px',\n transition: 'opacity 0.15s',\n }}\n onMouseEnter={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.opacity = '0.9';\n }}\n onMouseLeave={(e: React.MouseEvent<HTMLButtonElement>) => {\n e.currentTarget.style.opacity = '1';\n }}\n >\n {isBatch\n ? (strings.toolApproval.approveAll ?? 'Approve All')\n : strings.toolApproval.approve}\n </button>\n <button\n data-testid=\"reject-tool-button\"\n onClick={() => onReject()}\n style={{\n flex: 1,\n padding: '8px 16px',\n borderRadius: '8px',\n border: `1px solid ${theme.borderColor}`,\n background: 'transparent',\n color: theme.textColor,\n fontWeight: 500,\n cursor: 'pointer',\n fontSize: '13px',\n transition: 'all 0.15s',\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 {isBatch\n ? (strings.toolApproval.rejectAll ?? 'Reject All')\n : strings.toolApproval.reject}\n </button>\n </div>\n </div>\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 { PersistedMessage } from '../providers/chatRepository/types';\nimport type { AgentInfo, FeedbackValue, ToolAnnotations } from '../types';\nimport type { FileUploadConfig, FileAttachment, FileProcessingState } 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: PersistedMessage[];\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 /** Gets the current chat (with frozen metadata) */\n get: () => Promise<Chat | null>;\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 /** File processing state for send-time transformations (e.g., OCR) */\n fileProcessing: FileProcessingState | null;\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 /** Tool execution and approval state */\n tools: {\n /** Currently executing tool info for status display */\n executing: { displayText: string } | null;\n /** Pending tool approval */\n pending: {\n /** All tools awaiting approval */\n tools: Array<{\n toolCallId: string;\n toolCallName: string;\n toolCallArgs: Record<string, unknown>;\n annotations?: ToolAnnotations;\n }>;\n /** Approve all pending tool calls */\n approveAll: () => void;\n /** Reject all pending tool calls with optional reason */\n rejectAll: (reason?: string) => void;\n };\n };\n /** Feedback functionality */\n feedback?: {\n /** Whether feedback is enabled (requires Langfuse on server) */\n enabled: boolean;\n /** Submit feedback for a message */\n submit: (messageId: string, traceId: string, feedback: FeedbackValue) => 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 onGetChat: ctx.history.get,\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 fileProcessing: ctx.fileProcessing,\n commands: ctx.commands.list,\n onSaveCommand: ctx.commands.save,\n onRenameCommand: ctx.commands.rename,\n onDeleteCommand: ctx.commands.delete,\n executingTool: ctx.tools.executing,\n feedbackEnabled: ctx.feedback?.enabled,\n onFeedback: ctx.feedback?.submit,\n pendingApprovals: ctx.tools.pending.tools,\n onApproveToolCall: ctx.tools.pending.tools.length > 0 ? ctx.tools.pending.approveAll : undefined,\n onRejectToolCall: ctx.tools.pending.tools.length > 0 ? ctx.tools.pending.rejectAll : undefined,\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 ToolApprovalResponseMessage,\n AgentInfo,\n MultimodalContent,\n FeedbackValue,\n UseAIForwardedProps,\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 * 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 // 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 // Tool results collected during a turn, pushed to _messages in correct order at RUN_FINISHED\n private _pendingToolResults: Message[] = [];\n\n // Tool call assembly\n private currentToolCalls: Map<string, {\n name: string;\n args: string;\n }> = new Map();\n\n // Feedback tracking\n private _langfuseEnabled = false;\n private langfuseConfigHandlers: Set<(enabled: boolean) => void> = new Set();\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 // Listen for server config (including Langfuse enabled status)\n this.socket.on('config', (data: { langfuseEnabled?: boolean }) => {\n console.log('[Client] Received server config:', data);\n this._langfuseEnabled = data.langfuseEnabled ?? false;\n // Notify listeners\n this.langfuseConfigHandlers.forEach(handler => handler(this._langfuseEnabled));\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 this._pendingToolResults = [];\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 messages to conversation history in correct API order:\n // assistant(toolCalls) → tool results → assistant(text)\n if (this._currentAssistantMessage) {\n if (this._currentAssistantToolCalls.length > 0) {\n // Intermediate assistant message with tool calls only\n const toolCallMessage: Message = {\n id: uuidv4(),\n role: 'assistant',\n content: '',\n toolCalls: this._currentAssistantToolCalls,\n };\n this._messages.push(toolCallMessage);\n\n // Tool result messages\n this._messages.push(...this._pendingToolResults);\n\n // Final assistant message with text only\n const textMessage: Message = {\n id: this._currentAssistantMessage.id!,\n role: 'assistant',\n content: this._currentAssistantMessage.content || '',\n };\n this._messages.push(textMessage);\n } else {\n // No tool calls - just a text response\n const assistantMessage: Message = {\n id: this._currentAssistantMessage.id!,\n role: 'assistant',\n content: this._currentAssistantMessage.content || '',\n };\n this._messages.push(assistantMessage);\n }\n\n // Reset for next message\n this._currentAssistantMessage = null;\n this._currentAssistantToolCalls = [];\n this._pendingToolResults = [];\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 * 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 * @param forwardedProps - Optional props to forward to the server (e.g., telemetryMetadata, mcpHeaders).\n * Internally merged with other forwardedProps.\n */\n async sendPrompt(prompt: string, multimodalContent?: MultimodalContent[], forwardedProps?: UseAIForwardedProps) {\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 if (part.type === 'file') {\n return {\n type: 'file',\n url: part.url,\n mimeType: part.mimeType,\n };\n } else {\n // transformed_file - pass through as-is, server will convert to text\n return {\n type: 'transformed_file',\n text: part.text,\n originalFile: part.originalFile,\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 // 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 annotations: t.annotations,\n })),\n state: this._state,\n context: [],\n forwardedProps: {\n ...(this._selectedAgent ? { agent: this._selectedAgent } : {}),\n ...(forwardedProps || {}),\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 // use-ai extension: include current tools and state for mid-run updates\n // (e.g., when navigation causes new components to mount)\n forwardedProps: {\n tools: this._tools,\n state: this._state,\n },\n },\n };\n\n // Collect tool result for deferred push at RUN_FINISHED (ensures correct\n // API ordering: assistant(toolCalls) → tool results → assistant(text))\n const toolResultMsg: Message = {\n id: toolResultMessage.data.messageId,\n role: 'tool',\n content: toolResultMessage.data.content,\n toolCallId,\n };\n this._pendingToolResults.push(toolResultMsg);\n\n this.send(toolResultMessage);\n }\n\n /**\n * Sends a tool approval response back to the server.\n *\n * @param toolCallId - The ID of the tool call being approved/rejected\n * @param approved - Whether the tool execution is approved\n * @param reason - Optional reason for rejection (shown to AI)\n */\n sendToolApprovalResponse(toolCallId: string, approved: boolean, reason?: string) {\n const message: ToolApprovalResponseMessage = {\n type: 'tool_approval_response',\n data: {\n toolCallId,\n approved,\n reason,\n },\n };\n\n // When a tool is rejected, the server handles it internally and never sends\n // a tool_result event back to the client. Track a synthetic tool result so\n // the conversation history has the required tool_use → tool_result pairing.\n // (Approved tools get their result tracked via sendToolResponse after execution.)\n if (!approved) {\n this._pendingToolResults.push({\n id: uuidv4(),\n role: 'tool',\n content: JSON.stringify({ rejected: true, reason: reason || 'User rejected tool execution' }),\n toolCallId,\n });\n }\n\n this.send(message);\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 this._pendingToolResults = [];\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 /**\n * Subscribes to Langfuse config changes.\n *\n * @param handler - Callback function receiving langfuse enabled status\n * @returns Cleanup function to unsubscribe\n */\n onLangfuseConfigChange(handler: (enabled: boolean) => void): () => void {\n this.langfuseConfigHandlers.add(handler);\n // Immediately call with current value\n handler(this._langfuseEnabled);\n return () => {\n this.langfuseConfigHandlers.delete(handler);\n };\n }\n\n /**\n * Submits feedback for an assistant message.\n * Sends feedback to the server, which forwards it to Langfuse.\n *\n * @param messageId - The client-side message ID\n * @param traceId - The Langfuse trace ID (runId from RUN_FINISHED)\n * @param feedback - 'upvote' for positive, 'downvote' for negative, null to remove\n */\n submitFeedback(messageId: string, traceId: string, feedback: FeedbackValue): void {\n if (!this.socket?.connected) {\n console.warn('[UseAI] Cannot submit feedback: not connected');\n return;\n }\n\n if (!this._langfuseEnabled) {\n console.warn('[UseAI] Cannot submit feedback: Langfuse not enabled on server');\n return;\n }\n\n this.send({\n type: 'message_feedback',\n data: { messageId, traceId, feedback },\n });\n }\n}\n","import { z } from 'zod';\nimport type { ToolDefinition, ToolAnnotations } from '@meetsmore-oss/use-ai-core';\n\n// Re-export ToolAnnotations for convenience\nexport type { ToolAnnotations };\n\n/**\n * Context provided to tool execution functions.\n * Allows tools to dynamically request user approval at runtime.\n */\nexport interface ToolExecutionContext {\n /**\n * Request user approval during tool execution.\n * Use this for conditional approval based on runtime values\n * (e.g., API endpoint, input values, computed risk).\n *\n * @param input - Approval request details\n * @returns Promise resolving with approval decision\n *\n * @example\n * ```typescript\n * const callApi = defineTool(\n * 'Call an external API',\n * z.object({ url: z.string() }),\n * async (input, ctx) => {\n * if (input.url.includes('production')) {\n * const { approved } = await ctx.requestApproval({\n * message: `This will call production API: ${input.url}`,\n * });\n * if (!approved) return { error: 'User rejected the action' };\n * }\n * return fetch(input.url);\n * }\n * );\n * ```\n */\n requestApproval(input: {\n message: string;\n metadata?: Record<string, unknown>;\n }): Promise<{ approved: boolean; reason?: string }>;\n}\n\n/**\n * Options for configuring tool behavior.\n */\nexport interface ToolOptions {\n /** MCP-aligned annotations for tool behavior hints */\n annotations?: ToolAnnotations;\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>, ctx: ToolExecutionContext) => 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, ctx: ToolExecutionContext) => 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: (ctx: ToolExecutionContext) => 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>, ctx: ToolExecutionContext) => 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 | ((ctx: ToolExecutionContext) => unknown),\n fnOrOptions?: ((input: z.infer<T>, ctx: ToolExecutionContext) => 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>, ctx: ToolExecutionContext) => unknown | Promise<unknown>;\n let actualOptions: ToolOptions;\n\n if (isNoParamFunction) {\n // Wrap no-param function: user writes (ctx?) => ..., we adapt to (input, ctx) => ...\n const noParamFn = schemaOrFn as (ctx: ToolExecutionContext) => unknown | Promise<unknown>;\n actualFn = (_input: z.infer<T>, ctx: ToolExecutionContext) => noParamFn(ctx);\n actualOptions = (fnOrOptions as ToolOptions) || {};\n } else {\n actualFn = fnOrOptions as (input: z.infer<T>, ctx: ToolExecutionContext) => 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 // Pass through annotations if present\n if (this._options.annotations) {\n toolDef.annotations = this._options.annotations;\n }\n\n return toolDef;\n },\n\n async _execute(input: unknown, ctx: ToolExecutionContext) {\n const validated = this._zodSchema.parse(input);\n return await actualFn(validated, ctx);\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 ctx: ToolExecutionContext\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, ctx);\n}\n","import type { PersistedFileMetadata } from '../../fileUpload/types';\nimport type { FeedbackValue } from '@meetsmore-oss/use-ai-core';\n\n/**\n * Arbitrary metadata attached to a chat.\n * Use this to store context about the chat (e.g., how it was invoked, document type being processed).\n */\nexport type ChatMetadata = Record<string, unknown>;\n\n/**\n * Display mode for chat messages.\n * Determines the visual styling of the message bubble.\n */\nexport type MessageDisplayMode = 'default' | 'error';\n\n/**\n * Text content part for persisted messages.\n */\nexport interface PersistedTextContent {\n type: 'text';\n text: string;\n}\n\n/**\n * File content part for persisted messages.\n * Only stores metadata, not the actual file data.\n */\nexport interface PersistedFileContent {\n type: 'file';\n file: PersistedFileMetadata;\n}\n\n/**\n * Content part for persisted messages.\n * Can be text or file metadata.\n */\nexport type PersistedContentPart = PersistedTextContent | PersistedFileContent;\n\n/**\n * Content that can be persisted.\n * Simple string for text-only messages, or array for multimodal content.\n */\nexport type PersistedMessageContent = string | PersistedContentPart[];\n\n/**\n * Message format for persisted chat history.\n * Compatible with AI SDK's UIMessage format for future integration.\n */\n/**\n * Tool call entry on an assistant message.\n * Matches the AG-UI MessageToolCall format.\n */\nexport interface PersistedToolCall {\n /** @example \"toolu_01abc123\" */\n id: string;\n type: 'function';\n function: {\n name: string;\n /** JSON-serialized arguments */\n arguments: string;\n };\n}\n\nexport interface PersistedMessage {\n id: string;\n role: 'user' | 'assistant' | 'tool';\n /** Content can be a string or multimodal content array */\n content: PersistedMessageContent;\n createdAt: Date;\n displayMode?: MessageDisplayMode;\n /** Langfuse trace ID for feedback tracking (only for assistant messages) */\n traceId?: string;\n /** User feedback on this message (only for assistant messages) */\n feedback?: FeedbackValue;\n /** Tool calls made by the assistant in this message (only for assistant messages) */\n toolCalls?: PersistedToolCall[];\n /**\n * ID of the tool call this message is a result of (only for tool messages)\n * @example \"toolu_01abc123\"\n */\n toolCallId?: string;\n}\n\n/**\n * Represents a stored chat conversation.\n */\nexport interface Chat {\n id: string;\n title?: string;\n messages: PersistedMessage[];\n createdAt: Date;\n updatedAt: Date;\n /** Arbitrary metadata attached to the chat */\n metadata?: ChatMetadata;\n}\n\n/**\n * Options for creating a new chat.\n */\nexport interface CreateChatOptions {\n title?: string;\n /** Initial metadata for the chat */\n metadata?: ChatMetadata;\n}\n\n/**\n * Options for listing chats.\n */\nexport interface ListChatsOptions {\n limit?: number;\n offset?: number;\n}\n\n/**\n * Abstract repository interface for chat persistence.\n * Implementations can store chats locally (localStorage, IndexedDB)\n * or remotely (REST API, GraphQL, etc.)\n */\nexport interface ChatRepository {\n /**\n * Creates a new chat and returns its ID.\n * @param options Optional configuration for the new chat\n * @returns Promise resolving to the new chat ID\n */\n createChat(options?: CreateChatOptions): Promise<string>;\n\n /**\n * Loads a chat by ID.\n * @param id Chat ID to load\n * @returns Promise resolving to the chat, or null if not found\n */\n loadChat(id: string): Promise<Chat | null>;\n\n /**\n * Saves or updates a chat.\n * @param chat Chat to save\n * @returns Promise resolving when save is complete\n */\n saveChat(chat: Chat): Promise<void>;\n\n /**\n * Deletes a chat by ID.\n * @param id Chat ID to delete\n * @returns Promise resolving when deletion is complete\n */\n deleteChat(id: string): Promise<void>;\n\n /**\n * Lists all available chats (metadata only, without full message history).\n * @param options Optional pagination and filtering options\n * @returns Promise resolving to array of chat metadata\n */\n listChats(options?: ListChatsOptions): Promise<Array<Omit<Chat, 'messages'>>>;\n\n /**\n * Deletes all stored chats.\n * @returns Promise resolving when all chats are deleted\n */\n deleteAll(): Promise<void>;\n\n /**\n * Updates metadata for a chat.\n * By default, merges with existing metadata. Set `overwrite: true` to replace entirely.\n * @param id Chat ID\n * @param metadata Metadata to set/merge\n * @param overwrite If true, replaces all metadata instead of merging\n * @returns Promise resolving when update is complete\n */\n updateMetadata(id: string, metadata: ChatMetadata, overwrite?: boolean): Promise<void>;\n}\n\n/**\n * Generates a unique chat ID.\n */\nexport function generateChatId(): string {\n return `chat_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n\n/**\n * Generates a unique message ID.\n */\nexport function generateMessageId(): string {\n return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n}\n","import type {\n Chat,\n ChatMetadata,\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 metadata: options?.metadata,\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 async updateMetadata(id: string, metadata: ChatMetadata, overwrite = false): Promise<void> {\n const chat = await this.loadChat(id);\n if (!chat) {\n throw new Error(`Chat not found: ${id}`);\n }\n\n chat.metadata = overwrite ? metadata : { ...chat.metadata, ...metadata };\n await this.saveChat(chat);\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 { useState, useCallback, useRef, useEffect } from 'react';\nimport type { ChatRepository, Chat, ChatMetadata, CreateChatOptions, PersistedMessageContent, PersistedMessage, PersistedToolCall } from '../providers/chatRepository/types';\nimport { generateMessageId } from '../providers/chatRepository/types';\nimport type { UseAIClient } from '../client';\nimport type { Message as AGUIMessage } from '../types';\nimport { getTextFromContent } from '../utils/messageContent';\n\n// Constants\nconst CHAT_TITLE_MAX_LENGTH = 50;\n\n/**\n * Deep equality comparison using JSON serialization.\n * Works for JSON-serializable values (primitives, arrays, plain objects).\n */\nfunction deepEquals(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\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 * Transforms persisted messages to AG-UI message format for loading into client.\n * Preserves toolCalls on assistant messages and toolCallId on tool messages\n * so the server can reconstruct valid API messages.\n */\nfunction transformMessagesToClientFormat(persistedMessages: PersistedMessage[]): AGUIMessage[] {\n return persistedMessages.map((msg): AGUIMessage => {\n const textContent = getTextFromContent(msg.content);\n\n switch (msg.role) {\n case 'tool':\n return {\n id: msg.id,\n role: 'tool',\n content: textContent,\n toolCallId: msg.toolCallId || '',\n };\n case 'assistant':\n return {\n id: msg.id,\n role: 'assistant',\n content: textContent,\n ...(msg.toolCalls && msg.toolCalls.length > 0 ? { toolCalls: msg.toolCalls } : {}),\n };\n case 'user':\n return {\n id: msg.id,\n role: 'user',\n content: textContent,\n };\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 /** Current messages state (owned by provider) */\n messages: PersistedMessage[];\n /** Setter for messages state (owned by provider) */\n setMessages: React.Dispatch<React.SetStateAction<PersistedMessage[]>>;\n /** Whether the client is connected */\n connected?: boolean;\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 /** The displayed chat ID (pending or current) */\n displayedChatId: string | null;\n /** Creates a new chat and switches to it */\n createNewChat: (options?: CreateChatOptions) => 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 /**\n * Saves an AI response to storage and optionally reloads messages.\n * When turnMessages are provided, intermediate assistant+tool messages from\n * the turn are persisted before the final assistant message, preserving the\n * complete tool call context for conversation history.\n */\n saveAIResponse: (content: string, displayMode?: 'default' | 'error', traceId?: string, turnMessages?: PersistedMessage[]) => Promise<void>;\n /** Reloads messages from storage for the given chat ID */\n reloadMessages: (chatId: string) => Promise<void>;\n /** Get the current chat object. Metadata is frozen to prevent accidental mutation. */\n getCurrentChat: () => Promise<Chat | null>;\n /** Update metadata for the current chat */\n updateMetadata: (metadata: ChatMetadata, overwrite?: boolean) => 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 */\nexport function useChatManagement({\n repository,\n clientRef,\n messages,\n setMessages,\n connected,\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 /**\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 /** Loads messages from storage for a given chat ID. */\n const loadChatMessages = useCallback(async (chatId: string): Promise<PersistedMessage[]> => {\n try {\n const chat = await repository.loadChat(chatId);\n if (chat) {\n console.log('[ChatManagement] Loaded', chat.messages.length, 'messages from storage for chat:', chatId);\n return chat.messages;\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 /** Reloads messages from storage and updates state. */\n const reloadMessages = useCallback(async (chatId: string) => {\n const loadedMessages = await loadChatMessages(chatId);\n setMessages(loadedMessages);\n }, [loadChatMessages, setMessages]);\n\n /** Creates a new chat. */\n const createNewChat = useCallback(async (options?: CreateChatOptions): Promise<string> => {\n console.log('[ChatManagement] createNewChat called - currentChatId:', currentChatId, 'pendingChatId:', pendingChatId, 'messages.length:', messages.length);\n\n // Reuse last created blank chat if options match\n if (pendingChatId && messages.length === 0) {\n const existingChat = await repository.loadChat(pendingChatId);\n const optionsMatch = existingChat\n && existingChat.title === options?.title\n && deepEquals(existingChat.metadata, options?.metadata);\n if (optionsMatch) {\n console.log('[ChatManagement] Last created chat has matching options, reusing:', pendingChatId);\n return pendingChatId;\n }\n }\n\n console.log('[ChatManagement] Creating new chat...');\n const chatId = await repository.createChat(options);\n\n // Set as pending - don't switch currentChatId until user sends a message\n setPendingChatId(chatId);\n setMessages([]);\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, setMessages]);\n\n /** Loads an existing chat by ID. */\n const loadChat = useCallback(async (chatId: string): Promise<void> => {\n // Set as pending chat - don't activate until user sends a message\n setPendingChatId(chatId);\n await reloadMessages(chatId);\n\n // Set threadId so the server recognizes this as a different conversation\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 /** Deletes a chat by ID. */\n const deleteChat = useCallback(async (chatId: string): Promise<void> => {\n await repository.deleteChat(chatId);\n\n if (currentChatId === chatId) {\n setCurrentChatId(null);\n setMessages([]);\n }\n\n if (pendingChatId === chatId) {\n setPendingChatId(null);\n setMessages([]);\n }\n\n console.log('[ChatManagement] Deleted chat:', chatId);\n }, [currentChatId, pendingChatId, repository, setMessages]);\n\n /** Lists all available chats. */\n const listChats = useCallback(async (): Promise<Array<Omit<Chat, 'messages'>>> => {\n return await repository.listChats();\n }, [repository]);\n\n /** Clears the current chat messages. */\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, setMessages]);\n\n /**\n * Gets the current chat object (including metadata).\n * Metadata is frozen to prevent accidental mutation.\n */\n const getCurrentChat = useCallback(async (): Promise<Chat | null> => {\n const chatId = pendingChatId || currentChatId;\n if (!chatId) return null;\n const chat = await repository.loadChat(chatId);\n if (chat?.metadata) {\n chat.metadata = Object.freeze({ ...chat.metadata });\n }\n return chat;\n }, [pendingChatId, currentChatId, repository]);\n\n /** Updates metadata for the current chat. */\n const updateMetadata = useCallback(async (metadata: ChatMetadata, overwrite = false): Promise<void> => {\n const chatId = pendingChatId || currentChatId;\n if (!chatId) {\n throw new Error('No active chat');\n }\n await repository.updateMetadata(chatId, metadata, overwrite);\n }, [pendingChatId, 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 existing messages into client for conversation history\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, clientRef, messages]);\n\n /** Saves a user message to storage. */\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 newMessage: PersistedMessage = {\n id: generateMessageId(),\n role: 'user',\n content,\n createdAt: new Date(),\n };\n chat.messages.push(newMessage);\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 // Append to local state directly (no reload round-trip)\n setMessages(prev => [...prev, newMessage]);\n return true;\n } catch (error) {\n console.error('[ChatManagement] Failed to save user message:', error);\n return false;\n }\n }, [repository, setMessages]);\n\n /** Saves an AI response to storage and updates UI. */\n const saveAIResponse = useCallback(async (\n content: string,\n displayMode?: 'default' | 'error',\n traceId?: string,\n turnMessages?: PersistedMessage[]\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 // Save intermediate turn messages (assistant messages with tool calls,\n // and tool result messages) before the final assistant message.\n if (turnMessages && turnMessages.length > 0) {\n chat.messages.push(...turnMessages);\n }\n\n const finalMessage: PersistedMessage = {\n id: generateMessageId(),\n role: 'assistant',\n content,\n createdAt: new Date(),\n displayMode,\n traceId,\n };\n chat.messages.push(finalMessage);\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 // Append to local state directly if user is viewing this chat (no reload round-trip)\n if (displayedChatId === currentChatIdValue) {\n const newMessages: PersistedMessage[] = [\n ...(turnMessages ?? []),\n finalMessage,\n ];\n setMessages(prev => [...prev, ...newMessages]);\n }\n } catch (error) {\n console.error('[ChatManagement] Failed to save AI response:', error);\n }\n }, [repository, setMessages]);\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, setMessages]);\n\n // Reload messages from storage on reconnection.\n // After a server restart, the client's in-memory _messages may have tool\n // results ordered before their parent assistant messages (because tool results\n // are pushed during execution while assistant messages are pushed at\n // RUN_FINISHED). Storage has the correct ordering from extractTurnMessages.\n const connectionPhaseRef = useRef<'initial' | 'connected' | 'disconnected'>('initial');\n\n useEffect(() => {\n if (connected) {\n const isReconnect = connectionPhaseRef.current === 'disconnected';\n connectionPhaseRef.current = 'connected';\n\n if (isReconnect && currentChatId && clientRef.current) {\n (async () => {\n const loadedMessages = await loadChatMessages(currentChatId);\n if (loadedMessages.length > 0) {\n clientRef.current?.loadMessages(transformMessagesToClientFormat(loadedMessages));\n console.log('[ChatManagement] Reloaded', loadedMessages.length, 'messages from storage on reconnect');\n }\n })();\n }\n } else {\n if (connectionPhaseRef.current === 'connected') {\n connectionPhaseRef.current = 'disconnected';\n }\n }\n }, [connected, currentChatId, 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 displayedChatId,\n createNewChat,\n loadChat,\n deleteChat,\n listChats,\n clearCurrentChat,\n activatePendingChat,\n saveUserMessage,\n saveAIResponse,\n reloadMessages,\n getCurrentChat,\n updateMetadata,\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, type RefObject, type MutableRefObject } from 'react';\nimport type { ToolAnnotations, ToolApprovalRequestEvent } from '../types';\nimport type { UseAIClient } from '../client';\nimport type { ToolsDefinition, ToolExecutionContext } from '../defineTool';\nimport { executeDefinedTool } from '../defineTool';\n\n// ── Registry Types ──────────────────────────────────────────────────────────\n\nexport interface RegisterToolsOptions {\n /** Mark component as invisible (no visual state, skip prompt wait) */\n invisible?: boolean;\n}\n\n// ── Execution Types ─────────────────────────────────────────────────────────\n\n/**\n * Pending tool approval request state.\n */\nexport interface PendingToolApproval {\n toolCallId: string;\n toolCallName: string;\n toolCallArgs: Record<string, unknown>;\n annotations?: ToolAnnotations;\n /** Optional message explaining why approval is needed (runtime approval) */\n message?: string;\n /** Optional metadata for the approval request (runtime approval) */\n metadata?: Record<string, unknown>;\n}\n\n// ── Hook Options & Return ───────────────────────────────────────────────────\n\nexport interface UseToolSystemOptions {\n /** Reference to the UseAI client for sending responses */\n clientRef: RefObject<UseAIClient | null>;\n /** Builds the aggregated state from all registered prompts */\n buildState: () => unknown;\n}\n\nexport interface UseToolSystemReturn {\n // ── Registry ──────────────────────────────────────────────────────────────\n\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: MutableRefObject<ToolsDefinition>;\n /** Signals that a component has completed its registration in useLayoutEffect */\n signalReady: (id: string) => void;\n /** Current tool registry version (increments when tools change) */\n toolRegistryVersion: number;\n\n // ── Waiters ───────────────────────────────────────────────────────────────\n\n /** Registers a waiter function for a component (called when tool exec needs to wait for re-render) */\n registerWaiter: (id: string, waiter: () => Promise<void>) => void;\n /** Unregisters a waiter function */\n unregisterWaiter: (id: string) => void;\n\n // ── Execution ─────────────────────────────────────────────────────────────\n\n /** All pending tool approval requests */\n pendingApprovals: PendingToolApproval[];\n /** Handle a tool approval request event from the server */\n handleApprovalRequest: (event: ToolApprovalRequestEvent) => void;\n /** Execute a tool call and send the response to the server */\n executeToolCall: (toolCallId: string, name: string, input: unknown) => Promise<void>;\n /** Store a tool call as pending approval (deferred execution) */\n storePendingToolCall: (toolCallId: string, name: string, input: unknown, toolCallData: { name: string; args: string }) => void;\n /** Approve all pending tool calls and execute them */\n approveAll: () => Promise<void>;\n /** Reject all pending tool calls with optional reason */\n rejectAll: (reason?: string) => void;\n}\n\n/**\n * Unified hook for the tool lifecycle: registration, waiter coordination,\n * and execution (including approval flow).\n *\n * Merges what were previously three separate concerns:\n * - Tool registry (registration, aggregation, ownership tracking)\n * - Waiters (waiting for component re-renders after tool execution)\n * - Tool execution (running tools, sending responses, approval flow)\n *\n * The only external dependency is `buildState` from prompt management,\n * which provides the aggregated app state sent alongside tool responses.\n */\nexport function useToolSystem({\n clientRef,\n buildState,\n}: UseToolSystemOptions): UseToolSystemReturn {\n\n // ── Registry State ──────────────────────────────────────────────────────\n\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 // Ready state tracking for each component\n const readyStateRef = useRef<Map<string, boolean>>(new Map());\n const readyListenersRef = useRef<Set<() => void>>(new Set());\n\n // ── Waiter State ────────────────────────────────────────────────────────\n\n const waitersRef = useRef<Map<string, () => Promise<void>>>(new Map());\n\n // ── Execution State ─────────────────────────────────────────────────────\n\n const [pendingApprovals, setPendingApprovals] = useState<PendingToolApproval[]>([]);\n const pendingApprovalToolCallsRef = useRef<Map<string, { name: string; input: unknown; toolCallData: { name: string; args: string } }>>(new Map());\n\n /** Resolvers for runtime approval requests (from ctx.requestApproval) */\n const runtimeApprovalResolversRef = useRef<Map<string, (result: { approved: boolean; reason?: string }) => void>>(new Map());\n\n // ── Registry Methods ────────────────────────────────────────────────────\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 // Mark as NOT ready - will be set ready by signalReady in useLayoutEffect\n readyStateRef.current.set(id, false);\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 setToolRegistryVersion(v => v + 1);\n }\n\n Object.keys(tools).forEach(toolName => {\n toolOwnershipRef.current.set(toolName, id);\n });\n\n if (options?.invisible) {\n invisibleRef.current.add(id);\n } else {\n invisibleRef.current.delete(id);\n }\n }, []);\n\n const signalReady = useCallback((id: string) => {\n if (!toolRegistryRef.current.has(id)) {\n return;\n }\n readyStateRef.current.set(id, true);\n readyListenersRef.current.forEach(listener => listener());\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 readyStateRef.current.delete(id);\n setToolRegistryVersion(v => v + 1);\n invisibleRef.current.delete(id);\n\n readyListenersRef.current.forEach(listener => listener());\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 const aggregatedToolsRef = useRef(aggregatedTools);\n aggregatedToolsRef.current = aggregatedTools;\n\n /**\n * Waits for tools to stabilize using React lifecycle signaling.\n *\n * Strategy:\n * 1. Wait for the next macrotask to let React process pending state updates\n * 2. Wait for all registered components to signal ready\n * 3. Safety timeout to prevent infinite waiting\n */\n const waitForToolsToStabilize = useCallback(async (): Promise<void> => {\n const maxWaitMs = 500;\n\n const checkAllReady = (): boolean => {\n if (readyStateRef.current.size === 0) return true;\n for (const ready of readyStateRef.current.values()) {\n if (!ready) return false;\n }\n return true;\n };\n\n await new Promise(resolve => setTimeout(resolve, 0));\n\n if (checkAllReady()) {\n return;\n }\n\n return new Promise<void>((resolve) => {\n let safetyTimeout: ReturnType<typeof setTimeout> | null = null;\n let resolved = false;\n\n const cleanup = () => {\n if (resolved) return;\n resolved = true;\n if (safetyTimeout) clearTimeout(safetyTimeout);\n readyListenersRef.current.delete(onReadyChange);\n };\n\n const onReadyChange = () => {\n if (resolved) return;\n if (checkAllReady()) {\n cleanup();\n resolve();\n }\n };\n\n safetyTimeout = setTimeout(() => {\n cleanup();\n resolve();\n }, maxWaitMs);\n\n readyListenersRef.current.add(onReadyChange);\n onReadyChange();\n });\n }, []);\n\n // ── Waiter Methods ──────────────────────────────────────────────────────\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 // ── Execution Methods ───────────────────────────────────────────────────\n\n const handleApprovalRequest = useCallback((event: ToolApprovalRequestEvent) => {\n console.log('[useToolSystem] Tool approval requested:', event.toolCallName, event.toolCallId);\n setPendingApprovals(prev => [\n ...prev,\n {\n toolCallId: event.toolCallId,\n toolCallName: event.toolCallName,\n toolCallArgs: event.toolCallArgs,\n annotations: event.annotations,\n message: event.message,\n metadata: event.metadata,\n },\n ]);\n }, []);\n\n const executeToolCall = useCallback(async (\n toolCallId: string,\n name: string,\n input: unknown\n ) => {\n const client = clientRef.current;\n if (!client) {\n console.error('[useToolSystem] No client available for tool execution');\n return;\n }\n\n try {\n const ownerId = toolOwnershipRef.current.get(name);\n console.log(`[useToolSystem] Tool \"${name}\" owned by component:`, ownerId);\n\n // Build ToolExecutionContext with requestApproval for runtime approvals\n const ctx: ToolExecutionContext = {\n requestApproval: ({ message, metadata }) => {\n return new Promise<{ approved: boolean; reason?: string }>((resolve) => {\n const approvalId = `${toolCallId}-runtime-${Date.now()}`;\n runtimeApprovalResolversRef.current.set(approvalId, resolve);\n\n setPendingApprovals(prev => [\n ...prev,\n {\n toolCallId: approvalId,\n toolCallName: name,\n toolCallArgs: (input as Record<string, unknown>) || {},\n message,\n metadata,\n },\n ]);\n });\n },\n };\n\n console.log('[useToolSystem] Executing tool...');\n const result = await executeDefinedTool(aggregatedToolsRef.current, name, input, ctx);\n\n const isErrorResult = result && typeof result === 'object' &&\n ('error' in result || (result as Record<string, unknown>).success === false);\n\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(`[useToolSystem] Waiting for prompt change from ${ownerId}...`);\n await waiter();\n console.log('[useToolSystem] Prompt change wait complete');\n }\n } else if (isErrorResult) {\n console.log('[useToolSystem] Tool returned error, skipping prompt wait');\n } else if (ownerIsInvisible) {\n console.log('[useToolSystem] Component is invisible, skipping prompt wait');\n }\n\n // Wait for tools to stabilize after execution (navigation/mount-unmount)\n console.log('[useToolSystem] Waiting for tools to stabilize...');\n await waitForToolsToStabilize();\n console.log('[useToolSystem] Tools stabilized');\n\n const updatedState = buildState();\n console.log(`[useToolSystem] Updated state (aggregated from all hooks)`);\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 }, [clientRef, isInvisible, getWaiter, waitForToolsToStabilize, buildState]);\n\n const storePendingToolCall = useCallback((\n toolCallId: string,\n name: string,\n input: unknown,\n toolCallData: { name: string; args: string }\n ) => {\n console.log(`[useToolSystem] Storing pending tool call \"${name}\" for approval`);\n pendingApprovalToolCallsRef.current.set(toolCallId, { name, input, toolCallData });\n }, []);\n\n const executePendingToolAfterApproval = useCallback(async (toolCallId: string) => {\n const pendingTool = pendingApprovalToolCallsRef.current.get(toolCallId);\n if (!pendingTool) {\n console.warn(`[useToolSystem] No pending tool found for ${toolCallId}`);\n return;\n }\n\n pendingApprovalToolCallsRef.current.delete(toolCallId);\n await executeToolCall(toolCallId, pendingTool.name, pendingTool.input);\n }, [executeToolCall]);\n\n const approveAll = useCallback(async () => {\n if (!clientRef.current) return;\n console.log('[useToolSystem] Approving all tool calls:', pendingApprovals.length);\n\n const pendingTools = [...pendingApprovals];\n setPendingApprovals([]);\n\n for (const pending of pendingTools) {\n // Check if this is a runtime client-side approval (from ctx.requestApproval)\n const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);\n if (runtimeResolver) {\n runtimeApprovalResolversRef.current.delete(pending.toolCallId);\n runtimeResolver({ approved: true });\n } else {\n // Server-side approval (destructiveHint flow)\n clientRef.current.sendToolApprovalResponse(pending.toolCallId, true);\n await executePendingToolAfterApproval(pending.toolCallId);\n }\n }\n }, [clientRef, pendingApprovals, executePendingToolAfterApproval]);\n\n const rejectAll = useCallback((reason?: string) => {\n if (!clientRef.current) return;\n console.log('[useToolSystem] Rejecting all tool calls:', pendingApprovals.length, reason);\n\n const pendingTools = [...pendingApprovals];\n setPendingApprovals([]);\n\n for (const pending of pendingTools) {\n // Check if this is a runtime client-side approval (from ctx.requestApproval)\n const runtimeResolver = runtimeApprovalResolversRef.current.get(pending.toolCallId);\n if (runtimeResolver) {\n runtimeApprovalResolversRef.current.delete(pending.toolCallId);\n runtimeResolver({ approved: false, reason });\n } else {\n // Server-side approval (destructiveHint flow)\n clientRef.current.sendToolApprovalResponse(pending.toolCallId, false, reason);\n pendingApprovalToolCallsRef.current.delete(pending.toolCallId);\n }\n }\n }, [clientRef, pendingApprovals]);\n\n // ── Return ──────────────────────────────────────────────────────────────\n\n return {\n // Registry\n registerTools,\n unregisterTools,\n isInvisible,\n aggregatedTools,\n hasTools,\n aggregatedToolsRef,\n signalReady,\n toolRegistryVersion,\n\n // Waiters\n registerWaiter,\n unregisterWaiter,\n\n // Execution\n pendingApprovals,\n handleApprovalRequest,\n executeToolCall,\n storePendingToolCall,\n approveAll,\n rejectAll,\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 /** All suggestions aggregated from registered components */\n aggregatedSuggestions: string[];\n /** Builds the aggregated state from all registered prompts */\n buildStateFromPrompts: () => { context: string } | null;\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 * - 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 [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 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 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 aggregatedSuggestions,\n buildStateFromPrompts,\n };\n}\n","import { useState, useEffect, useRef, useCallback } from 'react';\nimport type { UseAIClient } from '../client';\nimport type { FeedbackValue } from '../types';\nimport type { ChatRepository, PersistedMessage } from '../providers/chatRepository/types';\n\nexport interface UseFeedbackOptions {\n /** Reference to the UseAIClient */\n clientRef: React.MutableRefObject<UseAIClient | null>;\n /** Chat repository for persisting feedback */\n repository: ChatRepository;\n /** Callback to get the currently displayed chat ID */\n getDisplayedChatId: () => string | null;\n /** Setter for messages state (for optimistic UI updates) */\n setMessages: React.Dispatch<React.SetStateAction<PersistedMessage[]>>;\n}\n\nexport interface UseFeedbackReturn {\n /** Whether Langfuse feedback is enabled on the server */\n enabled: boolean;\n /** Submits feedback for a message (updates storage and sends to server) */\n submitFeedback: (messageId: string, traceId: string, feedback: FeedbackValue) => void;\n}\n\n/**\n * Hook for managing user feedback on AI messages.\n *\n * Responsibilities:\n * - Tracks whether Langfuse feedback is enabled on the server\n * - Captures traceId from RUN_FINISHED events\n * - Persists feedback to chat storage\n * - Sends feedback to server (Langfuse)\n *\n * @example\n * ```typescript\n * const { enabled, getTraceId, clearTraceId, submitFeedback } = useFeedback({\n * clientRef,\n * repository,\n * getDisplayedChatId: () => displayedChatId,\n * setMessages,\n * });\n * ```\n */\nexport function useFeedback({\n clientRef,\n repository,\n getDisplayedChatId,\n setMessages,\n}: UseFeedbackOptions): UseFeedbackReturn {\n const [enabled, setEnabled] = useState(false);\n const enabledRef = useRef(false);\n\n // Keep enabledRef in sync with state\n useEffect(() => {\n enabledRef.current = enabled;\n }, [enabled]);\n\n // Subscribe to Langfuse config changes from server\n useEffect(() => {\n const client = clientRef.current;\n if (!client) return;\n\n const unsubscribe = client.onLangfuseConfigChange((isEnabled) => {\n setEnabled(isEnabled);\n });\n\n return unsubscribe;\n }, [clientRef.current]);\n\n /**\n * Updates feedback in storage and local state.\n */\n const updateFeedbackInStorage = useCallback(async (\n messageId: string,\n feedback: FeedbackValue\n ): Promise<void> => {\n const displayedChatId = getDisplayedChatId();\n\n if (!displayedChatId) {\n console.warn('[useFeedback] No chat ID, cannot update feedback');\n return;\n }\n\n try {\n const chat = await repository.loadChat(displayedChatId);\n\n if (!chat) {\n console.error('[useFeedback] Chat not found:', displayedChatId);\n return;\n }\n\n // Find and update the message\n const message = chat.messages.find(msg => msg.id === messageId);\n if (message) {\n message.feedback = feedback;\n await repository.saveChat(chat);\n\n // Update local state immediately for responsive UI\n setMessages(prevMessages =>\n prevMessages.map(msg =>\n msg.id === messageId ? { ...msg, feedback } : msg\n )\n );\n } else {\n console.warn('[useFeedback] Message not found:', messageId);\n }\n } catch (error) {\n console.error('[useFeedback] Failed to update feedback:', error);\n }\n }, [repository, getDisplayedChatId, setMessages]);\n\n const submitFeedback = useCallback((\n messageId: string,\n traceId: string,\n feedback: FeedbackValue\n ) => {\n // Update storage and local state\n updateFeedbackInStorage(messageId, feedback);\n\n // Send to server if connected and Langfuse is enabled\n const client = clientRef.current;\n if (client && enabledRef.current) {\n client.submitFeedback(messageId, traceId, feedback);\n }\n }, [clientRef, updateFeedbackInStorage]);\n\n return {\n enabled,\n submitFeedback,\n };\n}\n","import { useState, useCallback, useRef } from 'react';\nimport type {\n AGUIEvent,\n ToolCallStartEvent,\n ToolCallEndEvent,\n RunErrorEvent,\n RunFinishedEvent,\n TextMessageContentEvent,\n ToolCallStartExtensions,\n ToolApprovalRequestEvent,\n} from '../types';\nimport { EventType, ErrorCode, TOOL_APPROVAL_REQUEST } from '../types';\nimport type { UseAIClient } from '../client';\nimport type { Message } from '../types';\nimport type { UseToolSystemReturn } from './useToolSystem';\nimport type { UseAIStrings } from '../theme';\nimport type { PersistedMessage } from '../providers/chatRepository/types';\n\n/**\n * Extracts intermediate turn messages (assistant messages with tool calls and\n * tool result messages) from the client message history, starting from\n * `startIndex`. Converts them to `PersistedMessage[]` for storage.\n *\n * The final text-only assistant message is excluded here because it is\n * saved separately by `saveAIResponse`.\n *\n * Messages in `client._messages` are already in correct API order\n * (assistant(toolCalls) → tool results → assistant(text)) since the client\n * defers pushing tool results until RUN_FINISHED.\n */\nfunction extractTurnMessages(messages: Message[], startIndex: number): PersistedMessage[] {\n const turnSlice = messages.slice(startIndex);\n const result: PersistedMessage[] = [];\n\n for (const msg of turnSlice) {\n if (msg.role === 'assistant' && 'toolCalls' in msg && msg.toolCalls) {\n result.push({\n id: msg.id,\n role: 'assistant',\n content: '',\n createdAt: new Date(),\n toolCalls: msg.toolCalls as PersistedMessage['toolCalls'],\n });\n } else if (msg.role === 'tool') {\n result.push({\n id: msg.id,\n role: 'tool',\n content: typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content),\n createdAt: new Date(),\n toolCallId: ('toolCallId' in msg && msg.toolCallId) ? msg.toolCallId as string : undefined,\n });\n }\n }\n\n return result;\n}\n\nexport interface UseServerEventsOptions {\n /** Tool system for executing tools and looking up tool metadata */\n toolSystem: UseToolSystemReturn;\n /** Saves an AI response to chat storage */\n saveAIResponse: (content: string, displayMode?: 'default' | 'error', traceId?: string, turnMessages?: PersistedMessage[]) => Promise<void>;\n /** UI strings for error messages and tool execution fallbacks */\n strings: UseAIStrings;\n}\n\nexport interface ExecutingToolDisplay {\n displayText: string;\n}\n\nexport interface UseServerEventsReturn {\n /** Whether the AI is currently loading/processing a response */\n loading: boolean;\n /** Set the loading state (e.g., when sending a message) */\n setLoading: React.Dispatch<React.SetStateAction<boolean>>;\n /** Current streaming text from the AI response */\n streamingText: string;\n /** Clear streaming text (e.g., when starting a new message) */\n clearStreamingText: () => void;\n /** Currently executing tool info for UI display, or null */\n executingTool: ExecutingToolDisplay | null;\n /** Ref tracking which chat the current streaming text belongs to */\n streamingChatIdRef: React.MutableRefObject<string | null>;\n /**\n * Handles a server event. Called from the provider's client subscription.\n * Takes the client instance so it can access client-internal state\n * (currentToolCalls, currentMessageContent).\n */\n handleServerEvent: (client: UseAIClient, event: AGUIEvent) => Promise<void>;\n}\n\n/**\n * Hook that owns all server event handling state and logic.\n *\n * Manages:\n * - Loading state (set on message send, cleared on RUN_FINISHED/RUN_ERROR)\n * - Streaming text accumulation (TEXT_MESSAGE_CONTENT/END events)\n * - Executing tool display (TOOL_CALL_START/END events)\n * - Tool execution dispatch (delegates to toolSystem)\n * - Error handling (RUN_ERROR events)\n *\n * The provider creates the client and subscribes `handleServerEvent` to it.\n * This hook doesn't manage the client lifecycle — only the event handling.\n */\nexport function useServerEvents({\n toolSystem,\n saveAIResponse,\n strings,\n}: UseServerEventsOptions): UseServerEventsReturn {\n const [loading, setLoading] = useState(false);\n const [streamingText, setStreamingText] = useState('');\n const streamingChatIdRef = useRef<string | null>(null);\n\n // Track message count at run start to extract turn messages at run end\n const messageCountAtRunStartRef = useRef<number>(0);\n\n // Executing tool state for UI display\n const [executingToolRaw, setExecutingTool] = useState<{\n toolCallId: string;\n title: string | null;\n } | null>(null);\n const executingToolFallbackRef = useRef<string | null>(null);\n\n const clearStreamingText = useCallback(() => {\n setStreamingText('');\n }, []);\n\n // Keep refs to avoid stale closures in the event handler\n // (the handler is captured once per client lifecycle, but these deps may change)\n const toolSystemRef = useRef(toolSystem);\n toolSystemRef.current = toolSystem;\n\n const saveAIResponseRef = useRef(saveAIResponse);\n saveAIResponseRef.current = saveAIResponse;\n\n const stringsRef = useRef(strings);\n stringsRef.current = strings;\n\n const handleServerEvent = useCallback(async (client: UseAIClient, event: AGUIEvent) => {\n const ts = toolSystemRef.current;\n const strs = stringsRef.current;\n\n if (event.type === EventType.RUN_STARTED) {\n // Snapshot message count so we can extract turn messages at RUN_FINISHED.\n // The user message was already pushed to client.messages by sendPrompt(),\n // so messages added after this point are from the AI turn.\n messageCountAtRunStartRef.current = client.messages.length;\n } else if (event.type === EventType.TOOL_CALL_START) {\n const e = event as ToolCallStartEvent & Partial<ToolCallStartExtensions>;\n\n // Get title from event annotations, local tool definition, or null (fallback)\n const tool = ts.aggregatedToolsRef.current[e.toolCallName];\n const title = e.annotations?.title ?? tool?._options?.annotations?.title ?? null;\n\n if (!title) {\n const fallbacks = strs.toolExecution.fallbackMessages;\n executingToolFallbackRef.current = fallbacks[Math.floor(Math.random() * fallbacks.length)];\n }\n\n setExecutingTool({ toolCallId: e.toolCallId, title });\n } else if (event.type === EventType.TOOL_CALL_END) {\n const toolCallEnd = event as ToolCallEndEvent;\n const toolCallId = toolCallEnd.toolCallId;\n\n setExecutingTool(prev => prev?.toolCallId === toolCallId ? null : prev);\n\n const toolCallData = (client as unknown as { currentToolCalls: Map<string, { name: string; args: string }> }).currentToolCalls.get(toolCallId);\n if (!toolCallData) {\n console.error(`[ServerEvents] Tool call ${toolCallId} not found`);\n return;\n }\n\n const name = toolCallData.name;\n const input = JSON.parse(toolCallData.args);\n\n // Skip tools not in our registry (likely workflow tools)\n const tool = ts.aggregatedToolsRef.current[name];\n if (!tool) {\n console.log(`[ServerEvents] Tool \"${name}\" not found in useAI tools, skipping (likely a workflow tool)`);\n return;\n }\n\n // Defer execution if tool requires approval\n if (tool._options?.annotations?.destructiveHint === true) {\n console.log(`[ServerEvents] Tool \"${name}\" requires approval, deferring execution`);\n ts.storePendingToolCall(toolCallId, name, input, toolCallData);\n return;\n }\n\n await ts.executeToolCall(toolCallId, name, input);\n } else if ((event.type as string) === TOOL_APPROVAL_REQUEST) {\n const e = event as unknown as ToolApprovalRequestEvent;\n ts.handleApprovalRequest(e);\n } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {\n const contentEvent = event as TextMessageContentEvent;\n setStreamingText(prev => prev + contentEvent.delta);\n } else if (event.type === EventType.TEXT_MESSAGE_END) {\n setStreamingText('');\n streamingChatIdRef.current = null;\n } else if (event.type === EventType.RUN_FINISHED) {\n const content = client.currentMessageContent;\n if (content) {\n const finishedEvent = event as RunFinishedEvent;\n const traceId = finishedEvent.runId;\n\n const turnMessages = extractTurnMessages(client.messages, messageCountAtRunStartRef.current);\n\n saveAIResponseRef.current(content, undefined, traceId, turnMessages);\n }\n setLoading(false);\n } else if (event.type === EventType.RUN_ERROR) {\n const errorEvent = event as RunErrorEvent;\n const errorCode = errorEvent.message as ErrorCode;\n console.error('[ServerEvents] Run error:', errorCode);\n\n const userMessage = strs.errors[errorCode] || strs.errors[ErrorCode.UNKNOWN_ERROR];\n\n saveAIResponseRef.current(userMessage, 'error');\n setStreamingText('');\n streamingChatIdRef.current = null;\n\n setLoading(false);\n }\n }, []);\n\n // Compute display value for UI\n const executingTool = executingToolRaw ? {\n displayText: executingToolRaw.title ?? executingToolFallbackRef.current ?? strings.toolExecution.fallbackMessages[0],\n } : null;\n\n return {\n loading,\n setLoading,\n streamingText,\n clearStreamingText,\n executingTool,\n streamingChatIdRef,\n handleServerEvent,\n };\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 ToolAnnotations,\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 ToolCallStartExtensions,\n WorkflowStatus,\n UseAIClientMessage,\n RunWorkflowMessage,\n FeedbackMessage,\n FeedbackValue,\n McpHeadersConfig,\n McpHeadersMap,\n AgentInfo,\n UseAIForwardedProps,\n // Tool approval types\n ToolApprovalRequestEvent,\n ToolApprovalResponseMessage,\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, TOOL_APPROVAL_REQUEST } from '@meetsmore-oss/use-ai-core';\n","import { useCallback, useRef, useEffect } from 'react';\nimport type { FileAttachment } from '../fileUpload/types';\nimport type { UseAIForwardedProps } from '../types';\nimport type { CreateChatOptions, ChatMetadata } from '../providers/chatRepository/types';\n\n/**\n * Options for programmatically sending a message via sendMessage().\n */\nexport interface SendMessageOptions {\n /** Start a new chat before sending. Default: false (continue existing chat) */\n newChat?: boolean;\n /** File attachments to include with the message */\n attachments?: File[];\n /** Open the chat panel after sending. Default: true */\n openChat?: boolean;\n /** Metadata to set on the new chat (only used when newChat: true) */\n metadata?: ChatMetadata;\n /**\n * Forwarded props for observability and configuration (e.g., telemetryMetadata, mcpHeaders).\n * This is merged with provider-level forwardedProps (message-level takes precedence).\n */\n forwardedProps?: UseAIForwardedProps;\n}\n\nexport interface UseMessageQueueOptions {\n /** The function that actually sends a message (the provider's handleSendMessage) */\n sendFn: (message: string, attachments?: FileAttachment[], forwardedProps?: UseAIForwardedProps) => Promise<void>;\n /** Creates a new chat */\n createNewChat: (options?: CreateChatOptions) => Promise<string>;\n /** Callback to open/close the chat panel */\n setOpen?: (open: boolean) => void;\n /** Whether the client is connected */\n connected: boolean;\n /** Whether the AI is currently loading/processing */\n loading: boolean;\n /** Whether there's a pending tool approval blocking the queue */\n hasPendingApproval: boolean;\n}\n\nexport interface UseMessageQueueReturn {\n /**\n * Programmatically send a message to the chat.\n * Messages are queued and processed one at a time.\n * Throws on failure (e.g., not connected, no sendFn).\n */\n sendMessage: (message: string, options?: SendMessageOptions) => Promise<void>;\n}\n\n/**\n * Hook for queuing and sending programmatic messages.\n *\n * Handles:\n * - Message queuing (one at a time)\n * - Waiting for loading + approval to complete between messages\n * - Creating new chats before sending\n * - Opening the chat panel after sending\n * - Converting File[] to FileAttachment[]\n */\nexport function useMessageQueue({\n sendFn,\n createNewChat,\n setOpen,\n connected,\n loading,\n hasPendingApproval,\n}: UseMessageQueueOptions): UseMessageQueueReturn {\n const pendingMessagesRef = useRef<Array<{ message: string; options?: SendMessageOptions }>>([]);\n const isProcessingQueueRef = useRef(false);\n\n // Use refs for callbacks that may change between renders.\n // The queue processor runs across async boundaries (awaits), during which\n // React may re-render and update these functions with fresh closures.\n // Refs ensure we always call the latest version.\n const sendFnRef = useRef(sendFn);\n sendFnRef.current = sendFn;\n\n const createNewChatRef = useRef(createNewChat);\n createNewChatRef.current = createNewChat;\n\n const setOpenRef = useRef(setOpen);\n setOpenRef.current = setOpen;\n\n const loadingRef = useRef(loading);\n useEffect(() => {\n loadingRef.current = loading;\n }, [loading]);\n\n const hasPendingApprovalRef = useRef(hasPendingApproval);\n useEffect(() => {\n hasPendingApprovalRef.current = hasPendingApproval;\n }, [hasPendingApproval]);\n\n const processMessageQueue = useCallback(async () => {\n if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0) {\n return;\n }\n\n isProcessingQueueRef.current = true;\n\n while (pendingMessagesRef.current.length > 0) {\n const { message, options } = pendingMessagesRef.current.shift()!;\n const { newChat = false, attachments = [], openChat = true, metadata, forwardedProps } = options ?? {};\n\n if (newChat) {\n await createNewChatRef.current({ metadata });\n }\n\n // Convert File[] to FileAttachment[]\n const fileAttachments: FileAttachment[] = await Promise.all(\n attachments.map(async (file) => {\n let preview: string | undefined;\n if (file.type.startsWith('image/')) {\n preview = await new Promise<string | undefined>((resolve) => {\n const reader = new FileReader();\n reader.onload = () => resolve(typeof reader.result === 'string' ? reader.result : undefined);\n reader.onerror = () => resolve(undefined);\n reader.readAsDataURL(file);\n });\n }\n return {\n id: crypto.randomUUID(),\n file,\n preview,\n };\n })\n );\n\n await sendFnRef.current(message, fileAttachments.length > 0 ? fileAttachments : undefined, forwardedProps);\n\n if (openChat && setOpenRef.current) {\n setOpenRef.current(true);\n }\n\n // Wait for loading and pending approval to complete before processing next message\n await new Promise<void>((resolve) => {\n const checkReady = () => {\n setTimeout(() => {\n if (!loadingRef.current && !hasPendingApprovalRef.current) {\n resolve();\n } else {\n checkReady();\n }\n }, 100);\n };\n checkReady();\n });\n }\n\n isProcessingQueueRef.current = false;\n }, []);\n\n const sendMessage = useCallback(async (message: string, options?: SendMessageOptions): Promise<void> => {\n if (!connected) {\n throw new Error('Not connected to UseAI server');\n }\n\n pendingMessagesRef.current.push({ message, options });\n await processMessageQueue();\n }, [connected, processMessageQueue]);\n\n return { sendMessage };\n}\n","import { useRef } from 'react';\nimport type { ToolsDefinition, DefinedTool, ToolExecutionContext } 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, annotations) 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, ctx: ToolExecutionContext) => {\n const currentTool = latestToolsRef.current[name];\n if (!currentTool) {\n throw new Error(`Tool \"${name}\" no longer exists`);\n }\n return currentTool.fn(input, ctx);\n };\n\n // Create the stable _execute function\n const stableExecute = async (input: unknown, ctx: ToolExecutionContext) => {\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, ctx);\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 // Workflows are headless — no UI for runtime approval, so provide a no-op context\n const noopCtx = {\n requestApproval: async () => ({ approved: true }),\n };\n const result = await executeDefinedTool(currentWorkflow.tools, toolName, toolArgs, noopCtx);\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';\n/** @hidden */\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 { SendMessageOptions } from './hooks/useMessageQueue';\nexport type { DefinedTool, ToolsDefinition, ToolOptions, ToolAnnotations, ToolExecutionContext } 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 ChatMetadata,\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 { matchesMimeType, findTransformerPattern } from './fileUpload/mimeTypeMatcher';\nexport { processAttachments, clearTransformationCache } from './fileUpload/processAttachments';\nexport type {\n FileUploadBackend,\n FileUploadConfig,\n FileAttachment,\n PersistedFileMetadata,\n FileTransformer,\n FileTransformerContext,\n FileTransformerMap,\n FileProcessingStatus,\n FileProcessingState,\n} from './fileUpload/types';\nexport type { ProcessAttachmentsConfig } from './fileUpload/processAttachments';\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 system (registry + execution + waiters)\nexport { useToolSystem } from './hooks/useToolSystem';\nexport type {\n RegisterToolsOptions,\n PendingToolApproval,\n UseToolSystemOptions,\n UseToolSystemReturn,\n} from './hooks/useToolSystem';\n\n// Prompt state\nexport { usePromptState } from './hooks/usePromptState';\nexport type {\n UsePromptStateOptions,\n UsePromptStateReturn,\n} from './hooks/usePromptState';\n\n// Server events\nexport { useServerEvents } from './hooks/useServerEvents';\nexport type {\n UseServerEventsOptions,\n UseServerEventsReturn,\n ExecutingToolDisplay,\n} from './hooks/useServerEvents';\n\n// Message queue\nexport { useMessageQueue } from './hooks/useMessageQueue';\nexport type {\n UseMessageQueueOptions,\n UseMessageQueueReturn,\n} from './hooks/useMessageQueue';\n\n// Feedback\nexport { useFeedback } from './hooks/useFeedback';\nexport type {\n UseFeedbackOptions,\n UseFeedbackReturn,\n} from './hooks/useFeedback';\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","jsx","jsxs","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","useState","jsx","jsxs","jsx","jsxs","useRef","useState","useEffect","Fragment","jsx","jsxs","createContext","useContext","jsx","createContext","useContext","uuidv4","useState","useCallback","useRef","useEffect","useState","useRef","useEffect","useCallback","displayedChatId","useState","useCallback","useEffect","useMemo","useState","useCallback","useRef","useEffect","STORAGE_KEY_PREFIX","STORAGE_INDEX_KEY","useRef","useState","useCallback","useEffect","useState","useCallback","useRef","useMemo","useRef","useState","useCallback","useMemo","useState","useCallback","useRef","useMemo","useEffect","useState","useEffect","useRef","useCallback","useState","useCallback","useRef","EventType","useState","useRef","useCallback","EventType","useCallback","useRef","useEffect","Fragment","jsx","jsxs","createContext","useState","useCallback","useRef","useEffect","useContext","useRef","useState","useRef","useMemo","useEffect","useCallback","EventType","error","useState","useCallback","useRef","useEffect","EventType","uuidv4","useState","useRef","useCallback","useEffect","z"]}
|