@surf-kit/agent 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat/index.cjs +733 -222
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +13 -7
- package/dist/chat/index.d.ts +13 -7
- package/dist/chat/index.js +715 -204
- package/dist/chat/index.js.map +1 -1
- package/dist/{chat-ChYl2XjV.d.cts → chat-BRY3xGg_.d.cts} +11 -2
- package/dist/{chat--OifhIRe.d.ts → chat-CcKc6OAR.d.ts} +11 -2
- package/dist/{hooks-BGs8-4GK.d.ts → hooks-BLeiVk-x.d.ts} +22 -5
- package/dist/{hooks-DLfF18IU.d.cts → hooks-CSGGLd7j.d.cts} +22 -5
- package/dist/hooks.cjs +160 -85
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +3 -3
- package/dist/hooks.d.ts +3 -3
- package/dist/hooks.js +160 -85
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +794 -283
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +758 -247
- package/dist/index.js.map +1 -1
- package/dist/layouts/index.cjs +754 -243
- package/dist/layouts/index.cjs.map +1 -1
- package/dist/layouts/index.d.cts +1 -1
- package/dist/layouts/index.d.ts +1 -1
- package/dist/layouts/index.js +733 -222
- package/dist/layouts/index.js.map +1 -1
- package/dist/mcp/index.cjs +1 -1
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +2 -2
- package/dist/mcp/index.js.map +1 -1
- package/dist/response/index.cjs +100 -15
- package/dist/response/index.cjs.map +1 -1
- package/dist/response/index.d.cts +2 -2
- package/dist/response/index.d.ts +2 -2
- package/dist/response/index.js +99 -14
- package/dist/response/index.js.map +1 -1
- package/dist/sources/index.cjs +30 -1
- package/dist/sources/index.cjs.map +1 -1
- package/dist/sources/index.js +30 -1
- package/dist/sources/index.js.map +1 -1
- package/dist/streaming/index.cjs +213 -93
- package/dist/streaming/index.cjs.map +1 -1
- package/dist/streaming/index.d.cts +4 -3
- package/dist/streaming/index.d.ts +4 -3
- package/dist/streaming/index.js +183 -73
- package/dist/streaming/index.js.map +1 -1
- package/dist/{streaming-DfT22A0z.d.cts → streaming-BHPXnwwo.d.cts} +3 -1
- package/dist/{streaming-DbQxScpi.d.ts → streaming-C6mbU7My.d.ts} +3 -1
- package/package.json +17 -5
package/dist/hooks.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks.ts","../src/hooks/useAgentChat.ts","../src/hooks/useStreaming.ts","../src/hooks/useConversation.ts","../src/hooks/useFeedback.ts","../src/hooks/useAgentTheme.ts","../src/hooks/useCharacterDrain.ts"],"sourcesContent":["export { useAgentChat } from './hooks/useAgentChat'\nexport type { AgentChatState, AgentChatActions } from './hooks/useAgentChat'\n\nexport { useStreaming } from './hooks/useStreaming'\nexport type { UseStreamingOptions } from './hooks/useStreaming'\n\nexport { useConversation } from './hooks/useConversation'\nexport type { Conversation, UseConversationOptions } from './hooks/useConversation'\n\nexport { useFeedback } from './hooks/useFeedback'\nexport type { FeedbackState, UseFeedbackOptions, FeedbackPayload } from './hooks/useFeedback'\n\nexport { useAgentTheme } from './hooks/useAgentTheme'\nexport type { AgentThemeResult } from './hooks/useAgentTheme'\n\nexport { useCharacterDrain } from './hooks/useCharacterDrain'\nexport type { CharacterDrainResult } from './hooks/useCharacterDrain'\n","'use client'\n\nimport { useReducer, useCallback, useRef } from 'react'\nimport type { ChatMessage, ChatError } from '../types/chat'\nimport type { AgentResponse } from '../types/agent'\nimport type { StreamState } from '../types/streaming'\nimport type { AgentChatConfig } from '../types/config'\n\n// ── State ──────────────────────────────────────────────────────────────\n\nexport interface AgentChatState {\n messages: ChatMessage[]\n conversationId: string | null\n isLoading: boolean\n error: ChatError | null\n inputValue: string\n streamPhase: StreamState['phase']\n streamingContent: string\n}\n\nconst initialState: AgentChatState = {\n messages: [],\n conversationId: null,\n isLoading: false,\n error: null,\n inputValue: '',\n streamPhase: 'idle',\n streamingContent: '',\n}\n\n// ── Actions ────────────────────────────────────────────────────────────\n\ntype Action =\n | { type: 'SET_INPUT'; value: string }\n | { type: 'SEND_START'; message: ChatMessage }\n | { type: 'STREAM_PHASE'; phase: StreamState['phase'] }\n | { type: 'STREAM_CONTENT'; content: string }\n | { type: 'SEND_SUCCESS'; message: ChatMessage; streamingContent: string; conversationId: string | null }\n | { type: 'SEND_ERROR'; error: ChatError }\n | { type: 'LOAD_CONVERSATION'; conversationId: string; messages: ChatMessage[] }\n | { type: 'RESET' }\n | { type: 'CLEAR_ERROR' }\n\nfunction reducer(state: AgentChatState, action: Action): AgentChatState {\n switch (action.type) {\n case 'SET_INPUT':\n return { ...state, inputValue: action.value }\n\n case 'SEND_START':\n return {\n ...state,\n messages: [...state.messages, action.message],\n isLoading: true,\n error: null,\n inputValue: '',\n streamPhase: 'thinking',\n streamingContent: '',\n }\n\n case 'STREAM_PHASE':\n return { ...state, streamPhase: action.phase }\n\n case 'STREAM_CONTENT':\n return { ...state, streamingContent: state.streamingContent + action.content }\n\n case 'SEND_SUCCESS':\n return {\n ...state,\n messages: [...state.messages, action.message],\n conversationId: action.conversationId ?? state.conversationId,\n isLoading: false,\n streamPhase: 'idle',\n streamingContent: '',\n }\n\n case 'SEND_ERROR':\n return {\n ...state,\n isLoading: false,\n error: action.error,\n streamPhase: 'idle',\n streamingContent: '',\n }\n\n case 'LOAD_CONVERSATION':\n return {\n ...state,\n conversationId: action.conversationId,\n messages: action.messages,\n error: null,\n }\n\n case 'RESET':\n return { ...initialState }\n\n case 'CLEAR_ERROR':\n return { ...state, error: null }\n\n default:\n return state\n }\n}\n\n// ── Hook ───────────────────────────────────────────────────────────────\n\nlet msgIdCounter = 0\nfunction generateMessageId(): string {\n return `msg-${Date.now()}-${++msgIdCounter}`\n}\n\nexport interface AgentChatActions {\n sendMessage: (content: string) => Promise<void>\n setInputValue: (value: string) => void\n loadConversation: (conversationId: string, messages: ChatMessage[]) => void\n submitFeedback: (messageId: string, rating: 'positive' | 'negative', comment?: string) => Promise<void>\n retry: () => Promise<void>\n reset: () => void\n}\n\nexport function useAgentChat(config: AgentChatConfig) {\n const [state, dispatch] = useReducer(reducer, initialState)\n const configRef = useRef(config)\n configRef.current = config\n const lastUserMessageRef = useRef<string | null>(null)\n\n const sendMessage = useCallback(\n async (content: string) => {\n const { apiUrl, streamPath = '/chat/stream', headers = {}, timeout = 30000 } = configRef.current\n\n lastUserMessageRef.current = content\n\n const userMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'user',\n content,\n timestamp: new Date(),\n }\n\n dispatch({ type: 'SEND_START', message: userMessage })\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n try {\n const response = await fetch(`${apiUrl}${streamPath}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers,\n },\n body: JSON.stringify({\n message: content,\n conversation_id: state.conversationId,\n }),\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n dispatch({\n type: 'SEND_ERROR',\n error: {\n code: 'API_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n retryable: response.status >= 500,\n },\n })\n return\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n dispatch({\n type: 'SEND_ERROR',\n error: { code: 'STREAM_ERROR', message: 'No response body', retryable: true },\n })\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n let accumulatedContent = ''\n let agentResponse: AgentResponse | null = null\n let capturedAgent: string | null = null\n let capturedConversationId: string | null = null\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data)\n switch (event.type) {\n case 'agent':\n capturedAgent = event.agent as string\n break\n case 'phase':\n dispatch({ type: 'STREAM_PHASE', phase: event.phase })\n break\n case 'delta':\n accumulatedContent += event.content\n dispatch({ type: 'STREAM_CONTENT', content: event.content as string })\n break\n case 'done':\n agentResponse = event.response\n capturedConversationId = (event.conversation_id as string) ?? null\n break\n case 'error':\n dispatch({ type: 'SEND_ERROR', error: event.error })\n return\n }\n } catch {\n // Skip malformed events\n }\n }\n }\n\n const assistantMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'assistant',\n content: agentResponse?.message ?? accumulatedContent,\n response: agentResponse ?? undefined,\n agent: capturedAgent ?? undefined,\n timestamp: new Date(),\n }\n\n dispatch({\n type: 'SEND_SUCCESS',\n message: assistantMessage,\n streamingContent: accumulatedContent,\n conversationId: capturedConversationId,\n })\n } catch (err: unknown) {\n clearTimeout(timeoutId)\n if ((err as Error).name === 'AbortError') {\n dispatch({\n type: 'SEND_ERROR',\n error: { code: 'TIMEOUT', message: 'Request timed out', retryable: true },\n })\n } else {\n dispatch({\n type: 'SEND_ERROR',\n error: {\n code: 'NETWORK_ERROR',\n message: (err as Error).message ?? 'Network error',\n retryable: true,\n },\n })\n }\n }\n },\n [state.conversationId],\n )\n\n const setInputValue = useCallback((value: string) => {\n dispatch({ type: 'SET_INPUT', value })\n }, [])\n\n const loadConversation = useCallback((conversationId: string, messages: ChatMessage[]) => {\n dispatch({ type: 'LOAD_CONVERSATION', conversationId, messages })\n }, [])\n\n const submitFeedback = useCallback(\n async (messageId: string, rating: 'positive' | 'negative', comment?: string) => {\n const { apiUrl, feedbackPath = '/feedback', headers = {} } = configRef.current\n await fetch(`${apiUrl}${feedbackPath}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify({ messageId, rating, comment }),\n })\n },\n [],\n )\n\n const retry = useCallback(async () => {\n if (lastUserMessageRef.current) {\n await sendMessage(lastUserMessageRef.current)\n }\n }, [sendMessage])\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' })\n lastUserMessageRef.current = null\n }, [])\n\n const actions: AgentChatActions = {\n sendMessage,\n setInputValue,\n loadConversation,\n submitFeedback,\n retry,\n reset,\n }\n\n return { state, actions }\n}\n","'use client'\n\nimport { useState, useCallback, useRef, useEffect } from 'react'\nimport type { StreamEvent, StreamState } from '../types/streaming'\nimport type { Source } from '../types/agent'\n\nexport interface UseStreamingOptions {\n /** SSE endpoint URL */\n url: string\n /** Additional headers for fetch-based SSE (not used with native EventSource) */\n headers?: Record<string, string>\n /** Called when a complete response is received */\n onDone?: (event: Extract<StreamEvent, { type: 'done' }>) => void\n /** Called on error */\n onError?: (event: Extract<StreamEvent, { type: 'error' }>) => void\n}\n\nconst initialState: StreamState = {\n active: false,\n phase: 'idle',\n content: '',\n sources: [],\n agent: null,\n agentLabel: null,\n}\n\nexport function useStreaming(options: UseStreamingOptions) {\n const { url, headers, onDone, onError } = options\n const [state, setState] = useState<StreamState>(initialState)\n const abortRef = useRef<AbortController | null>(null)\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const stop = useCallback(() => {\n if (abortRef.current) {\n abortRef.current.abort()\n abortRef.current = null\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n }, [])\n\n const start = useCallback(\n async (body: Record<string, unknown>) => {\n // Reset state\n setState({ ...initialState, active: true, phase: 'thinking' })\n\n const controller = new AbortController()\n abortRef.current = controller\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const errorEvent: StreamEvent = {\n type: 'error',\n error: {\n code: 'API_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n retryable: response.status >= 500,\n },\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(errorEvent as Extract<StreamEvent, { type: 'error' }>)\n return\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data) as StreamEvent\n processEvent(event, setState, optionsRef)\n } catch {\n // Skip malformed events\n }\n }\n }\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return\n const errorEvent: StreamEvent = {\n type: 'error',\n error: {\n code: 'NETWORK_ERROR',\n message: (err as Error).message ?? 'Network error',\n retryable: true,\n },\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(errorEvent as Extract<StreamEvent, { type: 'error' }>)\n }\n },\n [url, headers],\n )\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (abortRef.current) {\n abortRef.current.abort()\n }\n }\n }, [])\n\n return { state, start, stop }\n}\n\nfunction processEvent(\n event: StreamEvent,\n setState: React.Dispatch<React.SetStateAction<StreamState>>,\n optionsRef: React.MutableRefObject<UseStreamingOptions>,\n) {\n switch (event.type) {\n case 'phase':\n setState((prev) => ({ ...prev, phase: event.phase }))\n break\n case 'delta':\n setState((prev) => ({ ...prev, content: prev.content + event.content }))\n break\n case 'source':\n setState((prev) => ({ ...prev, sources: [...prev.sources, event.source] }))\n break\n case 'agent':\n setState((prev) => ({ ...prev, agent: event.agent }))\n break\n case 'done':\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onDone?.(event)\n break\n case 'error':\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(event)\n break\n }\n}\n","'use client'\n\nimport { useState, useCallback } from 'react'\nimport type { ChatMessage } from '../types/chat'\nimport type { ConversationSummary } from '../types/chat'\n\nexport interface Conversation {\n id: string\n title: string\n messages: ChatMessage[]\n createdAt: Date\n updatedAt: Date\n}\n\nexport interface UseConversationOptions {\n /** Enable localStorage persistence */\n persist?: boolean\n /** localStorage key prefix */\n storageKey?: string\n}\n\nconst STORAGE_PREFIX = 'surf-kit-conversations'\n\nfunction getStorageKey(prefix: string) {\n return `${prefix}:list`\n}\n\nfunction loadFromStorage(storageKey: string): Conversation[] {\n if (typeof window === 'undefined') return []\n try {\n const raw = window.localStorage.getItem(getStorageKey(storageKey))\n if (!raw) return []\n const parsed = JSON.parse(raw) as Array<Conversation & { createdAt: string; updatedAt: string }>\n return parsed.map((c) => ({\n ...c,\n createdAt: new Date(c.createdAt),\n updatedAt: new Date(c.updatedAt),\n messages: c.messages.map((m) => ({ ...m, timestamp: new Date(m.timestamp as unknown as string) })),\n }))\n } catch {\n return []\n }\n}\n\nfunction saveToStorage(storageKey: string, conversations: Conversation[]) {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(getStorageKey(storageKey), JSON.stringify(conversations))\n } catch {\n // Storage full or unavailable\n }\n}\n\nlet idCounter = 0\nfunction generateId(): string {\n return `conv-${Date.now()}-${++idCounter}`\n}\n\nexport function useConversation(options: UseConversationOptions = {}) {\n const { persist = false, storageKey = STORAGE_PREFIX } = options\n\n const [conversations, setConversations] = useState<Conversation[]>(() =>\n persist ? loadFromStorage(storageKey) : [],\n )\n const [current, setCurrent] = useState<Conversation | null>(null)\n\n const persistIfNeeded = useCallback(\n (convs: Conversation[]) => {\n if (persist) saveToStorage(storageKey, convs)\n },\n [persist, storageKey],\n )\n\n const create = useCallback(\n (title?: string): Conversation => {\n const now = new Date()\n const conv: Conversation = {\n id: generateId(),\n title: title ?? 'New Conversation',\n messages: [],\n createdAt: now,\n updatedAt: now,\n }\n setConversations((prev) => {\n const next = [conv, ...prev]\n persistIfNeeded(next)\n return next\n })\n setCurrent(conv)\n return conv\n },\n [persistIfNeeded],\n )\n\n const list = useCallback((): ConversationSummary[] => {\n return conversations.map((c) => ({\n id: c.id,\n title: c.title,\n lastMessage: c.messages.length > 0 ? c.messages[c.messages.length - 1].content : '',\n updatedAt: c.updatedAt,\n messageCount: c.messages.length,\n }))\n }, [conversations])\n\n const load = useCallback(\n (id: string): Conversation | null => {\n const conv = conversations.find((c) => c.id === id) ?? null\n setCurrent(conv)\n return conv\n },\n [conversations],\n )\n\n const remove = useCallback(\n (id: string) => {\n setConversations((prev) => {\n const next = prev.filter((c) => c.id !== id)\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) => (prev?.id === id ? null : prev))\n },\n [persistIfNeeded],\n )\n\n const rename = useCallback(\n (id: string, title: string) => {\n setConversations((prev) => {\n const next = prev.map((c) => (c.id === id ? { ...c, title, updatedAt: new Date() } : c))\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) => (prev?.id === id ? { ...prev, title, updatedAt: new Date() } : prev))\n },\n [persistIfNeeded],\n )\n\n const addMessage = useCallback(\n (conversationId: string, message: ChatMessage) => {\n setConversations((prev) => {\n const next = prev.map((c) =>\n c.id === conversationId\n ? { ...c, messages: [...c.messages, message], updatedAt: new Date() }\n : c,\n )\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) =>\n prev?.id === conversationId\n ? { ...prev, messages: [...prev.messages, message], updatedAt: new Date() }\n : prev,\n )\n },\n [persistIfNeeded],\n )\n\n return {\n conversations,\n current,\n create,\n list,\n load,\n delete: remove,\n rename,\n addMessage,\n }\n}\n","'use client'\n\nimport { useState, useCallback, useRef } from 'react'\n\nexport type FeedbackState = 'idle' | 'submitting' | 'submitted' | 'error'\n\nexport interface UseFeedbackOptions {\n /** API endpoint URL for feedback submission */\n url: string\n /** Additional request headers */\n headers?: Record<string, string>\n}\n\nexport interface FeedbackPayload {\n messageId: string\n rating: 'positive' | 'negative'\n comment?: string\n}\n\nexport function useFeedback(options: UseFeedbackOptions) {\n const { url, headers } = options\n const [state, setState] = useState<FeedbackState>('idle')\n const [error, setError] = useState<string | null>(null)\n const abortRef = useRef<AbortController | null>(null)\n\n const submit = useCallback(\n async (messageId: string, rating: 'positive' | 'negative', comment?: string) => {\n setState('submitting')\n setError(null)\n\n const controller = new AbortController()\n abortRef.current = controller\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: JSON.stringify({ messageId, rating, comment }),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n setState('submitted')\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return\n setError((err as Error).message ?? 'Failed to submit feedback')\n setState('error')\n }\n },\n [url, headers],\n )\n\n const reset = useCallback(() => {\n setState('idle')\n setError(null)\n }, [])\n\n return { state, error, submit, reset }\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport type { AgentInfo } from '../types/agent'\n\nexport interface AgentThemeResult {\n accent: string\n icon: AgentInfo['icon'] | null\n label: string\n}\n\nconst DEFAULT_ACCENT = '#6366f1'\nconst DEFAULT_LABEL = 'Agent'\n\nexport function useAgentTheme(\n agentId: string | null | undefined,\n agentThemes?: Record<string, AgentInfo>,\n): AgentThemeResult {\n return useMemo(() => {\n if (!agentId) {\n return { accent: DEFAULT_ACCENT, icon: null, label: DEFAULT_LABEL }\n }\n\n const theme = agentThemes?.[agentId]\n if (!theme) {\n return { accent: DEFAULT_ACCENT, icon: null, label: agentId }\n }\n\n return {\n accent: theme.accent ?? DEFAULT_ACCENT,\n icon: theme.icon ?? null,\n label: theme.label,\n }\n }, [agentId, agentThemes])\n}\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport interface CharacterDrainResult {\n displayed: string\n isDraining: boolean\n}\n\n/**\n * Smoothly drains a growing `target` string character-by-character using\n * `requestAnimationFrame`, decoupling visual rendering from network packet\n * timing so text appears to type out instead of arriving in chunks.\n *\n * When `target` resets to empty (e.g. stream finished), the hook continues\n * draining the previous content to completion before resetting, so the\n * typing animation isn't cut short.\n *\n * Design: the RAF loop is long-lived — it does NOT restart on every delta.\n * The tick function is stored in a ref (updated each render) so the loop\n * always reads the latest drainTarget without being cancelled/restarted.\n * A separate kick-start effect re-fires the loop when it was idle and new\n * content arrives.\n */\nexport function useCharacterDrain(target: string, msPerChar = 15): CharacterDrainResult {\n const [displayed, setDisplayed] = useState('')\n const indexRef = useRef(0)\n const lastTimeRef = useRef(0)\n const rafRef = useRef<number | null>(null)\n // Holds the last non-empty target so we can finish draining after source resets\n const drainTargetRef = useRef('')\n const msPerCharRef = useRef(msPerChar)\n\n msPerCharRef.current = msPerChar\n\n // Update drain target when new content arrives; preserve old value on reset\n if (target !== '') {\n drainTargetRef.current = target\n }\n\n const drainTarget = drainTargetRef.current\n const isDraining = displayed.length < drainTarget.length\n\n // Tick function stored in ref so the long-lived RAF loop always reads the\n // latest drainTarget and msPerChar without being cancelled/recreated.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const tickRef = useRef<(now: number) => void>(() => {})\n tickRef.current = (now: number) => {\n const currentTarget = drainTargetRef.current\n if (currentTarget === '') {\n rafRef.current = null\n return\n }\n\n if (lastTimeRef.current === 0) lastTimeRef.current = now\n const elapsed = now - lastTimeRef.current\n const charsToAdvance = Math.floor(elapsed / msPerCharRef.current)\n\n if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {\n const nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\n indexRef.current = nextIndex\n lastTimeRef.current = now\n setDisplayed(currentTarget.slice(0, nextIndex))\n }\n\n if (indexRef.current < currentTarget.length) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n } else {\n rafRef.current = null\n }\n }\n\n // Kick-start the RAF loop when new content arrives and the loop is idle.\n // No cleanup here — we intentionally do NOT cancel the running loop when\n // drainTarget grows; the long-lived tick will pick up new chars automatically.\n useEffect(() => {\n if (\n drainTargetRef.current !== '' &&\n indexRef.current < drainTargetRef.current.length &&\n rafRef.current === null\n ) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n }\n }, [drainTarget]) // drainTarget change = new content; check if loop needs kicking\n\n // Once drain completes and source stream is already done, reset all state\n useEffect(() => {\n if (target === '' && !isDraining && displayed !== '') {\n indexRef.current = 0\n lastTimeRef.current = 0\n drainTargetRef.current = ''\n setDisplayed('')\n }\n }, [target, isDraining, displayed])\n\n // Cancel any pending RAF on unmount\n useEffect(() => {\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n }\n }, [])\n\n return { displayed, isDraining }\n}\n"],"mappings":"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","names":["import_react","initialState","import_react","import_react","import_react","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks.ts","../src/hooks/useAgentChat.ts","../src/hooks/useStreaming.ts","../src/hooks/useConversation.ts","../src/hooks/useFeedback.ts","../src/hooks/useAgentTheme.ts","../src/hooks/useCharacterDrain.ts"],"sourcesContent":["export { useAgentChat } from './hooks/useAgentChat'\nexport type { AgentChatState, AgentChatActions } from './hooks/useAgentChat'\n\nexport { useStreaming } from './hooks/useStreaming'\nexport type { UseStreamingOptions } from './hooks/useStreaming'\n\nexport { useConversation } from './hooks/useConversation'\nexport type { Conversation, UseConversationOptions } from './hooks/useConversation'\n\nexport { useFeedback } from './hooks/useFeedback'\nexport type { FeedbackState, UseFeedbackOptions, FeedbackPayload } from './hooks/useFeedback'\n\nexport { useAgentTheme } from './hooks/useAgentTheme'\nexport type { AgentThemeResult } from './hooks/useAgentTheme'\n\nexport { useCharacterDrain } from './hooks/useCharacterDrain'\nexport type { CharacterDrainResult } from './hooks/useCharacterDrain'\n","'use client'\n\nimport { useReducer, useCallback, useRef } from 'react'\nimport type { ChatMessage, ChatError, Attachment } from '../types/chat'\nimport type { AgentResponse } from '../types/agent'\nimport type { StreamState } from '../types/streaming'\nimport type { AgentChatConfig } from '../types/config'\n\n// ── State ──────────────────────────────────────────────────────────────\n\nexport interface AgentChatState {\n messages: ChatMessage[]\n conversationId: string | null\n isLoading: boolean\n error: ChatError | null\n inputValue: string\n streamPhase: StreamState['phase']\n streamingContent: string\n streamingAgent: string | null\n}\n\nconst initialState: AgentChatState = {\n messages: [],\n conversationId: null,\n isLoading: false,\n error: null,\n inputValue: '',\n streamPhase: 'idle',\n streamingContent: '',\n streamingAgent: null,\n}\n\n// ── Actions ────────────────────────────────────────────────────────────\n\ntype Action =\n | { type: 'SET_INPUT'; value: string }\n | { type: 'SEND_START'; message: ChatMessage }\n | { type: 'STREAM_PHASE'; phase: StreamState['phase'] }\n | { type: 'STREAM_CONTENT'; content: string }\n | { type: 'STREAM_CONTENT_RESET' }\n | { type: 'STREAM_AGENT'; agent: string }\n | { type: 'SEND_SUCCESS'; message: ChatMessage; streamingContent: string; conversationId: string | null }\n | { type: 'SEND_ERROR'; error: ChatError }\n | { type: 'LOAD_CONVERSATION'; conversationId: string; messages: ChatMessage[] }\n | { type: 'RESET' }\n | { type: 'CLEAR_ERROR' }\n\nfunction reducer(state: AgentChatState, action: Action): AgentChatState {\n switch (action.type) {\n case 'SET_INPUT':\n return { ...state, inputValue: action.value }\n\n case 'SEND_START':\n return {\n ...state,\n messages: [...state.messages, action.message],\n isLoading: true,\n error: null,\n inputValue: '',\n streamPhase: 'thinking',\n streamingContent: '',\n streamingAgent: null,\n }\n\n case 'STREAM_PHASE':\n return { ...state, streamPhase: action.phase }\n\n case 'STREAM_CONTENT':\n return { ...state, streamingContent: state.streamingContent + action.content }\n\n case 'STREAM_CONTENT_RESET':\n return { ...state, streamingContent: '' }\n\n case 'STREAM_AGENT':\n return { ...state, streamingAgent: action.agent }\n\n case 'SEND_SUCCESS':\n return {\n ...state,\n messages: [...state.messages, action.message],\n conversationId: action.conversationId ?? state.conversationId,\n isLoading: false,\n streamPhase: 'idle',\n streamingContent: '',\n }\n\n case 'SEND_ERROR':\n return {\n ...state,\n isLoading: false,\n error: action.error,\n streamPhase: 'idle',\n streamingContent: '',\n streamingAgent: null,\n }\n\n case 'LOAD_CONVERSATION':\n return {\n ...state,\n conversationId: action.conversationId,\n messages: action.messages,\n error: null,\n }\n\n case 'RESET':\n return { ...initialState }\n\n case 'CLEAR_ERROR':\n return { ...state, error: null }\n\n default:\n return state\n }\n}\n\n// ── Hook ───────────────────────────────────────────────────────────────\n\nlet msgIdCounter = 0\nfunction generateMessageId(): string {\n return `msg-${Date.now()}-${++msgIdCounter}`\n}\n\nexport interface AgentChatActions {\n sendMessage: (content: string, attachments?: Attachment[]) => Promise<void>\n setInputValue: (value: string) => void\n loadConversation: (conversationId: string, messages: ChatMessage[]) => void\n submitFeedback: (messageId: string, rating: 'positive' | 'negative', comment?: string) => Promise<void>\n retry: () => Promise<void>\n stop: () => void\n reset: () => void\n}\n\nexport function useAgentChat(config: AgentChatConfig) {\n const [state, dispatch] = useReducer(reducer, initialState)\n const configRef = useRef(config)\n configRef.current = config\n const lastUserMessageRef = useRef<string | null>(null)\n const lastUserAttachmentsRef = useRef<Attachment[] | undefined>(undefined)\n const abortControllerRef = useRef<AbortController | null>(null)\n\n const sendMessage = useCallback(\n async (content: string, attachments?: Attachment[]) => {\n const { apiUrl, streamPath = '/chat/stream', headers: headersOrFn, timeout = 30000, bodyExtra } = configRef.current\n const headers = typeof headersOrFn === 'function' ? await headersOrFn() : (headersOrFn ?? {})\n\n lastUserMessageRef.current = content\n lastUserAttachmentsRef.current = attachments\n\n const userMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'user',\n content,\n attachments,\n timestamp: new Date(),\n }\n\n dispatch({ type: 'SEND_START', message: userMessage })\n\n const controller = new AbortController()\n abortControllerRef.current = controller\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n // These variables are mutated inside handleEvent (called from async stream processing).\n // TypeScript can't track mutations through closures, so we use a mutable context object.\n // Defined outside try so the catch block can access accumulated content for user-initiated stop.\n const ctx = {\n accumulatedContent: '',\n agentResponse: null as AgentResponse | null,\n capturedAgent: null as string | null,\n capturedConversationId: null as string | null,\n hadStreamError: false,\n }\n\n try {\n const url = `${apiUrl}${streamPath}`\n const mergedHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers,\n }\n\n // Build request body — include attachments if present\n const requestBody: Record<string, unknown> = {\n message: content,\n conversation_id: state.conversationId,\n ...bodyExtra,\n }\n if (attachments && attachments.length > 0) {\n requestBody.attachments = attachments.map(a => ({\n filename: a.filename,\n content_type: a.content_type,\n data: a.data,\n }))\n }\n const body = JSON.stringify(requestBody)\n\n // Shared handler for parsed SSE events (used by both adapter and default paths)\n const handleEvent = (event: { type: string; [key: string]: unknown }) => {\n switch (event.type) {\n case 'agent':\n ctx.capturedAgent = event.agent as string\n dispatch({ type: 'STREAM_AGENT', agent: ctx.capturedAgent })\n break\n case 'phase':\n dispatch({ type: 'STREAM_PHASE', phase: event.phase as StreamState['phase'] })\n break\n case 'delta':\n ctx.accumulatedContent += event.content\n dispatch({ type: 'STREAM_CONTENT', content: event.content as string })\n break\n case 'delta_reset':\n ctx.accumulatedContent = ''\n dispatch({ type: 'STREAM_CONTENT_RESET' })\n break\n case 'done':\n ctx.agentResponse = event.response as AgentResponse\n ctx.capturedConversationId = (event.conversation_id as string) ?? null\n break\n case 'error':\n ctx.hadStreamError = true\n dispatch({ type: 'SEND_ERROR', error: event.error as ChatError })\n break\n }\n }\n\n const { streamAdapter } = configRef.current\n\n if (streamAdapter) {\n // Use the custom stream adapter (e.g. React Native XHR-based SSE)\n await streamAdapter(\n url,\n { method: 'POST', headers: mergedHeaders, body, signal: controller.signal },\n handleEvent,\n )\n clearTimeout(timeoutId)\n } else {\n // Default path: fetch + ReadableStream getReader()\n const response = await fetch(url, {\n method: 'POST',\n headers: mergedHeaders,\n body,\n signal: controller.signal,\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n dispatch({\n type: 'SEND_ERROR',\n error: {\n code: 'API_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n retryable: response.status >= 500,\n },\n })\n return\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n dispatch({\n type: 'SEND_ERROR',\n error: { code: 'STREAM_ERROR', message: 'No response body', retryable: true },\n })\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data)\n handleEvent(event)\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n\n // If an error event was dispatched during streaming, don't dispatch success\n if (ctx.hadStreamError) return\n\n const assistantMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'assistant',\n content: ctx.agentResponse?.message ?? ctx.accumulatedContent,\n response: ctx.agentResponse ?? undefined,\n agent: ctx.capturedAgent ?? undefined,\n timestamp: new Date(),\n }\n\n dispatch({\n type: 'SEND_SUCCESS',\n message: assistantMessage,\n streamingContent: ctx.accumulatedContent,\n conversationId: ctx.capturedConversationId,\n })\n } catch (err: unknown) {\n clearTimeout(timeoutId)\n if ((err as Error).name === 'AbortError') {\n // User-initiated stop: commit whatever content we have so far\n if (ctx.accumulatedContent) {\n const partialMessage: ChatMessage = {\n id: generateMessageId(),\n role: 'assistant',\n content: ctx.accumulatedContent,\n agent: ctx.capturedAgent ?? undefined,\n timestamp: new Date(),\n }\n dispatch({\n type: 'SEND_SUCCESS',\n message: partialMessage,\n streamingContent: ctx.accumulatedContent,\n conversationId: ctx.capturedConversationId,\n })\n } else {\n dispatch({\n type: 'SEND_ERROR',\n error: { code: 'ABORTED', message: 'Request stopped', retryable: true },\n })\n }\n } else {\n dispatch({\n type: 'SEND_ERROR',\n error: {\n code: 'NETWORK_ERROR',\n message: (err as Error).message ?? 'Network error',\n retryable: true,\n },\n })\n }\n } finally {\n abortControllerRef.current = null\n }\n },\n [state.conversationId],\n )\n\n const setInputValue = useCallback((value: string) => {\n dispatch({ type: 'SET_INPUT', value })\n }, [])\n\n const loadConversation = useCallback((conversationId: string, messages: ChatMessage[]) => {\n dispatch({ type: 'LOAD_CONVERSATION', conversationId, messages })\n }, [])\n\n const submitFeedback = useCallback(\n async (messageId: string, rating: 'positive' | 'negative', comment?: string) => {\n const { apiUrl, feedbackPath = '/feedback', headers: headersOrFn } = configRef.current\n const headers = typeof headersOrFn === 'function' ? await headersOrFn() : (headersOrFn ?? {})\n await fetch(`${apiUrl}${feedbackPath}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...headers },\n body: JSON.stringify({ messageId, rating, comment }),\n })\n },\n [],\n )\n\n const retry = useCallback(async () => {\n if (lastUserMessageRef.current) {\n await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current)\n }\n }, [sendMessage])\n\n const stop = useCallback(() => {\n abortControllerRef.current?.abort()\n }, [])\n\n const reset = useCallback(() => {\n dispatch({ type: 'RESET' })\n lastUserMessageRef.current = null\n lastUserAttachmentsRef.current = undefined\n }, [])\n\n const actions: AgentChatActions = {\n sendMessage,\n setInputValue,\n loadConversation,\n submitFeedback,\n retry,\n stop,\n reset,\n }\n\n return { state, actions }\n}\n","'use client'\n\nimport { useState, useCallback, useRef, useEffect } from 'react'\nimport type { StreamEvent, StreamState } from '../types/streaming'\nimport type { Source } from '../types/agent'\n\nexport interface UseStreamingOptions {\n /** SSE endpoint URL */\n url: string\n /** Additional headers for fetch-based SSE (not used with native EventSource) */\n headers?: Record<string, string>\n /** Called when a complete response is received */\n onDone?: (event: Extract<StreamEvent, { type: 'done' }>) => void\n /** Called on error */\n onError?: (event: Extract<StreamEvent, { type: 'error' }>) => void\n}\n\nconst initialState: StreamState = {\n active: false,\n phase: 'idle',\n content: '',\n sources: [],\n agent: null,\n agentLabel: null,\n}\n\nexport function useStreaming(options: UseStreamingOptions) {\n const { url, headers, onDone, onError } = options\n const [state, setState] = useState<StreamState>(initialState)\n const abortRef = useRef<AbortController | null>(null)\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const stop = useCallback(() => {\n if (abortRef.current) {\n abortRef.current.abort()\n abortRef.current = null\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n }, [])\n\n const start = useCallback(\n async (body: Record<string, unknown>) => {\n // Reset state\n setState({ ...initialState, active: true, phase: 'thinking' })\n\n const controller = new AbortController()\n abortRef.current = controller\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n ...headers,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n const errorEvent: StreamEvent = {\n type: 'error',\n error: {\n code: 'API_ERROR',\n message: `HTTP ${response.status}: ${response.statusText}`,\n retryable: response.status >= 500,\n },\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(errorEvent as Extract<StreamEvent, { type: 'error' }>)\n return\n }\n\n const reader = response.body?.getReader()\n if (!reader) {\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n return\n }\n\n const decoder = new TextDecoder()\n let buffer = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6).trim()\n if (data === '[DONE]') continue\n\n try {\n const event = JSON.parse(data) as StreamEvent\n processEvent(event, setState, optionsRef)\n } catch {\n // Skip malformed events\n }\n }\n }\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return\n const errorEvent: StreamEvent = {\n type: 'error',\n error: {\n code: 'NETWORK_ERROR',\n message: (err as Error).message ?? 'Network error',\n retryable: true,\n },\n }\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(errorEvent as Extract<StreamEvent, { type: 'error' }>)\n }\n },\n [url, headers],\n )\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (abortRef.current) {\n abortRef.current.abort()\n }\n }\n }, [])\n\n return { state, start, stop }\n}\n\nfunction processEvent(\n event: StreamEvent,\n setState: React.Dispatch<React.SetStateAction<StreamState>>,\n optionsRef: React.MutableRefObject<UseStreamingOptions>,\n) {\n switch (event.type) {\n case 'phase':\n setState((prev) => ({ ...prev, phase: event.phase }))\n break\n case 'delta':\n setState((prev) => ({ ...prev, content: prev.content + event.content }))\n break\n case 'source':\n setState((prev) => ({ ...prev, sources: [...prev.sources, event.source] }))\n break\n case 'agent':\n setState((prev) => ({ ...prev, agent: event.agent }))\n break\n case 'done':\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onDone?.(event)\n break\n case 'error':\n setState((prev) => ({ ...prev, active: false, phase: 'idle' }))\n optionsRef.current.onError?.(event)\n break\n }\n}\n","'use client'\n\nimport { useState, useCallback } from 'react'\nimport type { ChatMessage } from '../types/chat'\nimport type { ConversationSummary } from '../types/chat'\n\nexport interface Conversation {\n id: string\n title: string\n messages: ChatMessage[]\n createdAt: Date\n updatedAt: Date\n}\n\nexport interface UseConversationOptions {\n /** Enable localStorage persistence */\n persist?: boolean\n /** localStorage key prefix */\n storageKey?: string\n}\n\nconst STORAGE_PREFIX = 'surf-kit-conversations'\n\nfunction getStorageKey(prefix: string) {\n return `${prefix}:list`\n}\n\nfunction loadFromStorage(storageKey: string): Conversation[] {\n if (typeof window === 'undefined') return []\n try {\n const raw = window.localStorage.getItem(getStorageKey(storageKey))\n if (!raw) return []\n const parsed = JSON.parse(raw) as Array<Conversation & { createdAt: string; updatedAt: string }>\n return parsed.map((c) => ({\n ...c,\n createdAt: new Date(c.createdAt),\n updatedAt: new Date(c.updatedAt),\n messages: c.messages.map((m) => ({ ...m, timestamp: new Date(m.timestamp as unknown as string) })),\n }))\n } catch {\n return []\n }\n}\n\nfunction saveToStorage(storageKey: string, conversations: Conversation[]) {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(getStorageKey(storageKey), JSON.stringify(conversations))\n } catch {\n // Storage full or unavailable\n }\n}\n\nlet idCounter = 0\nfunction generateId(): string {\n return `conv-${Date.now()}-${++idCounter}`\n}\n\nexport function useConversation(options: UseConversationOptions = {}) {\n const { persist = false, storageKey = STORAGE_PREFIX } = options\n\n const [conversations, setConversations] = useState<Conversation[]>(() =>\n persist ? loadFromStorage(storageKey) : [],\n )\n const [current, setCurrent] = useState<Conversation | null>(null)\n\n const persistIfNeeded = useCallback(\n (convs: Conversation[]) => {\n if (persist) saveToStorage(storageKey, convs)\n },\n [persist, storageKey],\n )\n\n const create = useCallback(\n (title?: string): Conversation => {\n const now = new Date()\n const conv: Conversation = {\n id: generateId(),\n title: title ?? 'New Conversation',\n messages: [],\n createdAt: now,\n updatedAt: now,\n }\n setConversations((prev) => {\n const next = [conv, ...prev]\n persistIfNeeded(next)\n return next\n })\n setCurrent(conv)\n return conv\n },\n [persistIfNeeded],\n )\n\n const list = useCallback((): ConversationSummary[] => {\n return conversations.map((c) => ({\n id: c.id,\n title: c.title,\n lastMessage: c.messages.length > 0 ? c.messages[c.messages.length - 1].content : '',\n updatedAt: c.updatedAt,\n messageCount: c.messages.length,\n }))\n }, [conversations])\n\n const load = useCallback(\n (id: string): Conversation | null => {\n const conv = conversations.find((c) => c.id === id) ?? null\n setCurrent(conv)\n return conv\n },\n [conversations],\n )\n\n const remove = useCallback(\n (id: string) => {\n setConversations((prev) => {\n const next = prev.filter((c) => c.id !== id)\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) => (prev?.id === id ? null : prev))\n },\n [persistIfNeeded],\n )\n\n const rename = useCallback(\n (id: string, title: string) => {\n setConversations((prev) => {\n const next = prev.map((c) => (c.id === id ? { ...c, title, updatedAt: new Date() } : c))\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) => (prev?.id === id ? { ...prev, title, updatedAt: new Date() } : prev))\n },\n [persistIfNeeded],\n )\n\n const addMessage = useCallback(\n (conversationId: string, message: ChatMessage) => {\n setConversations((prev) => {\n const next = prev.map((c) =>\n c.id === conversationId\n ? { ...c, messages: [...c.messages, message], updatedAt: new Date() }\n : c,\n )\n persistIfNeeded(next)\n return next\n })\n setCurrent((prev) =>\n prev?.id === conversationId\n ? { ...prev, messages: [...prev.messages, message], updatedAt: new Date() }\n : prev,\n )\n },\n [persistIfNeeded],\n )\n\n return {\n conversations,\n current,\n create,\n list,\n load,\n delete: remove,\n rename,\n addMessage,\n }\n}\n","'use client'\n\nimport { useState, useCallback, useRef } from 'react'\n\nexport type FeedbackState = 'idle' | 'submitting' | 'submitted' | 'error'\n\nexport interface UseFeedbackOptions {\n /** API endpoint URL for feedback submission */\n url: string\n /** Additional request headers */\n headers?: Record<string, string>\n}\n\nexport interface FeedbackPayload {\n messageId: string\n rating: 'positive' | 'negative'\n comment?: string\n}\n\nexport function useFeedback(options: UseFeedbackOptions) {\n const { url, headers } = options\n const [state, setState] = useState<FeedbackState>('idle')\n const [error, setError] = useState<string | null>(null)\n const abortRef = useRef<AbortController | null>(null)\n\n const submit = useCallback(\n async (messageId: string, rating: 'positive' | 'negative', comment?: string) => {\n setState('submitting')\n setError(null)\n\n const controller = new AbortController()\n abortRef.current = controller\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: JSON.stringify({ messageId, rating, comment }),\n signal: controller.signal,\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`)\n }\n\n setState('submitted')\n } catch (err: unknown) {\n if ((err as Error).name === 'AbortError') return\n setError((err as Error).message ?? 'Failed to submit feedback')\n setState('error')\n }\n },\n [url, headers],\n )\n\n const reset = useCallback(() => {\n setState('idle')\n setError(null)\n }, [])\n\n return { state, error, submit, reset }\n}\n","'use client'\n\nimport { useMemo } from 'react'\nimport type { AgentInfo } from '../types/agent'\n\nexport interface AgentThemeResult {\n accent: string\n icon: AgentInfo['icon'] | null\n label: string\n}\n\nconst DEFAULT_ACCENT = '#6366f1'\nconst DEFAULT_LABEL = 'Agent'\n\nexport function useAgentTheme(\n agentId: string | null | undefined,\n agentThemes?: Record<string, AgentInfo>,\n): AgentThemeResult {\n return useMemo(() => {\n if (!agentId) {\n return { accent: DEFAULT_ACCENT, icon: null, label: DEFAULT_LABEL }\n }\n\n const theme = agentThemes?.[agentId]\n if (!theme) {\n return { accent: DEFAULT_ACCENT, icon: null, label: agentId }\n }\n\n return {\n accent: theme.accent ?? DEFAULT_ACCENT,\n icon: theme.icon ?? null,\n label: theme.label,\n }\n }, [agentId, agentThemes])\n}\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport interface CharacterDrainResult {\n displayed: string\n isDraining: boolean\n}\n\n/**\n * Smoothly drains a growing `target` string character-by-character using\n * `requestAnimationFrame`, decoupling visual rendering from network packet\n * timing so text appears to type out instead of arriving in chunks.\n *\n * When `target` resets to empty (e.g. stream finished), the hook continues\n * draining the previous content to completion before resetting, so the\n * typing animation isn't cut short.\n *\n * Design: the RAF loop is long-lived — it does NOT restart on every delta.\n * The tick function is stored in a ref (updated each render) so the loop\n * always reads the latest drainTarget without being cancelled/restarted.\n * A separate kick-start effect re-fires the loop when it was idle and new\n * content arrives.\n */\nexport function useCharacterDrain(target: string, msPerChar = 15): CharacterDrainResult {\n const [displayed, setDisplayed] = useState('')\n const indexRef = useRef(0)\n const lastTimeRef = useRef(0)\n const rafRef = useRef<number | null>(null)\n // Holds the last non-empty target so we can finish draining after source resets\n const drainTargetRef = useRef('')\n const msPerCharRef = useRef(msPerChar)\n\n msPerCharRef.current = msPerChar\n\n // Update drain target when new content arrives; preserve old value on reset\n if (target !== '') {\n drainTargetRef.current = target\n }\n\n const drainTarget = drainTargetRef.current\n const isDraining = displayed.length < drainTarget.length\n\n // Tick function stored in ref so the long-lived RAF loop always reads the\n // latest drainTarget and msPerChar without being cancelled/recreated.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const tickRef = useRef<(now: number) => void>(() => {})\n tickRef.current = (now: number) => {\n const currentTarget = drainTargetRef.current\n if (currentTarget === '') {\n rafRef.current = null\n return\n }\n\n if (lastTimeRef.current === 0) lastTimeRef.current = now\n const elapsed = now - lastTimeRef.current\n const charsToAdvance = Math.floor(elapsed / msPerCharRef.current)\n\n if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {\n let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\n // When the slice would end with whitespace, advance past it to include\n // the next visible character. This prevents trimEnd() in the streaming\n // UI from stripping trailing newlines and causing ReactMarkdown to\n // \"jump\" between block structures in a single frame (e.g. a heading\n // suddenly gaining a following paragraph with no transition).\n while (\n nextIndex < currentTarget.length &&\n currentTarget[nextIndex - 1].trim() === ''\n ) {\n nextIndex++\n }\n indexRef.current = nextIndex\n lastTimeRef.current = now\n setDisplayed(currentTarget.slice(0, nextIndex))\n }\n\n if (indexRef.current < currentTarget.length) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n } else {\n rafRef.current = null\n }\n }\n\n // Kick-start the RAF loop when new content arrives and the loop is idle.\n // No cleanup here — we intentionally do NOT cancel the running loop when\n // drainTarget grows; the long-lived tick will pick up new chars automatically.\n useEffect(() => {\n if (\n drainTargetRef.current !== '' &&\n indexRef.current < drainTargetRef.current.length &&\n rafRef.current === null\n ) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n }\n }, [drainTarget]) // drainTarget change = new content; check if loop needs kicking\n\n // Once drain completes and source stream is already done, reset all state\n useEffect(() => {\n if (target === '' && !isDraining && displayed !== '') {\n indexRef.current = 0\n lastTimeRef.current = 0\n drainTargetRef.current = ''\n setDisplayed('')\n }\n }, [target, isDraining, displayed])\n\n // Cancel any pending RAF on unmount\n useEffect(() => {\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n }\n }, [])\n\n return { displayed, isDraining }\n}\n"],"mappings":"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","names":["import_react","initialState","import_react","import_react","import_react","import_react"]}
|
package/dist/hooks.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { A as AgentChatActions, b as AgentChatState, c as AgentThemeResult, C as CharacterDrainResult, d as Conversation, F as FeedbackPayload, e as FeedbackState, U as UseConversationOptions, f as UseFeedbackOptions, g as UseStreamingOptions, u as useAgentChat, h as useAgentTheme, i as useCharacterDrain, j as useConversation, k as useFeedback, l as useStreaming } from './hooks-
|
|
2
|
-
import './chat-
|
|
1
|
+
export { A as AgentChatActions, b as AgentChatState, c as AgentThemeResult, C as CharacterDrainResult, d as Conversation, F as FeedbackPayload, e as FeedbackState, U as UseConversationOptions, f as UseFeedbackOptions, g as UseStreamingOptions, u as useAgentChat, h as useAgentTheme, i as useCharacterDrain, j as useConversation, k as useFeedback, l as useStreaming } from './hooks-CSGGLd7j.cjs';
|
|
2
|
+
import './chat-BRY3xGg_.cjs';
|
|
3
3
|
import './agent-BNSmiexZ.cjs';
|
|
4
4
|
import 'react';
|
|
5
|
-
import './streaming-
|
|
5
|
+
import './streaming-BHPXnwwo.cjs';
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { A as AgentChatActions, b as AgentChatState, c as AgentThemeResult, C as CharacterDrainResult, d as Conversation, F as FeedbackPayload, e as FeedbackState, U as UseConversationOptions, f as UseFeedbackOptions, g as UseStreamingOptions, u as useAgentChat, h as useAgentTheme, i as useCharacterDrain, j as useConversation, k as useFeedback, l as useStreaming } from './hooks-
|
|
2
|
-
import './chat
|
|
1
|
+
export { A as AgentChatActions, b as AgentChatState, c as AgentThemeResult, C as CharacterDrainResult, d as Conversation, F as FeedbackPayload, e as FeedbackState, U as UseConversationOptions, f as UseFeedbackOptions, g as UseStreamingOptions, u as useAgentChat, h as useAgentTheme, i as useCharacterDrain, j as useConversation, k as useFeedback, l as useStreaming } from './hooks-BLeiVk-x.js';
|
|
2
|
+
import './chat-CcKc6OAR.js';
|
|
3
3
|
import './agent-BNSmiexZ.js';
|
|
4
4
|
import 'react';
|
|
5
|
-
import './streaming-
|
|
5
|
+
import './streaming-C6mbU7My.js';
|
package/dist/hooks.js
CHANGED
|
@@ -9,7 +9,8 @@ var initialState = {
|
|
|
9
9
|
error: null,
|
|
10
10
|
inputValue: "",
|
|
11
11
|
streamPhase: "idle",
|
|
12
|
-
streamingContent: ""
|
|
12
|
+
streamingContent: "",
|
|
13
|
+
streamingAgent: null
|
|
13
14
|
};
|
|
14
15
|
function reducer(state, action) {
|
|
15
16
|
switch (action.type) {
|
|
@@ -23,12 +24,17 @@ function reducer(state, action) {
|
|
|
23
24
|
error: null,
|
|
24
25
|
inputValue: "",
|
|
25
26
|
streamPhase: "thinking",
|
|
26
|
-
streamingContent: ""
|
|
27
|
+
streamingContent: "",
|
|
28
|
+
streamingAgent: null
|
|
27
29
|
};
|
|
28
30
|
case "STREAM_PHASE":
|
|
29
31
|
return { ...state, streamPhase: action.phase };
|
|
30
32
|
case "STREAM_CONTENT":
|
|
31
33
|
return { ...state, streamingContent: state.streamingContent + action.content };
|
|
34
|
+
case "STREAM_CONTENT_RESET":
|
|
35
|
+
return { ...state, streamingContent: "" };
|
|
36
|
+
case "STREAM_AGENT":
|
|
37
|
+
return { ...state, streamingAgent: action.agent };
|
|
32
38
|
case "SEND_SUCCESS":
|
|
33
39
|
return {
|
|
34
40
|
...state,
|
|
@@ -44,7 +50,8 @@ function reducer(state, action) {
|
|
|
44
50
|
isLoading: false,
|
|
45
51
|
error: action.error,
|
|
46
52
|
streamPhase: "idle",
|
|
47
|
-
streamingContent: ""
|
|
53
|
+
streamingContent: "",
|
|
54
|
+
streamingAgent: null
|
|
48
55
|
};
|
|
49
56
|
case "LOAD_CONVERSATION":
|
|
50
57
|
return {
|
|
@@ -70,115 +77,172 @@ function useAgentChat(config) {
|
|
|
70
77
|
const configRef = useRef(config);
|
|
71
78
|
configRef.current = config;
|
|
72
79
|
const lastUserMessageRef = useRef(null);
|
|
80
|
+
const lastUserAttachmentsRef = useRef(void 0);
|
|
81
|
+
const abortControllerRef = useRef(null);
|
|
73
82
|
const sendMessage = useCallback(
|
|
74
|
-
async (content) => {
|
|
75
|
-
const { apiUrl, streamPath = "/chat/stream", headers
|
|
83
|
+
async (content, attachments) => {
|
|
84
|
+
const { apiUrl, streamPath = "/chat/stream", headers: headersOrFn, timeout = 3e4, bodyExtra } = configRef.current;
|
|
85
|
+
const headers = typeof headersOrFn === "function" ? await headersOrFn() : headersOrFn ?? {};
|
|
76
86
|
lastUserMessageRef.current = content;
|
|
87
|
+
lastUserAttachmentsRef.current = attachments;
|
|
77
88
|
const userMessage = {
|
|
78
89
|
id: generateMessageId(),
|
|
79
90
|
role: "user",
|
|
80
91
|
content,
|
|
92
|
+
attachments,
|
|
81
93
|
timestamp: /* @__PURE__ */ new Date()
|
|
82
94
|
};
|
|
83
95
|
dispatch({ type: "SEND_START", message: userMessage });
|
|
84
96
|
const controller = new AbortController();
|
|
97
|
+
abortControllerRef.current = controller;
|
|
85
98
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
99
|
+
const ctx = {
|
|
100
|
+
accumulatedContent: "",
|
|
101
|
+
agentResponse: null,
|
|
102
|
+
capturedAgent: null,
|
|
103
|
+
capturedConversationId: null,
|
|
104
|
+
hadStreamError: false
|
|
105
|
+
};
|
|
86
106
|
try {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
error: {
|
|
105
|
-
code: "API_ERROR",
|
|
106
|
-
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
107
|
-
retryable: response.status >= 500
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
return;
|
|
107
|
+
const url = `${apiUrl}${streamPath}`;
|
|
108
|
+
const mergedHeaders = {
|
|
109
|
+
"Content-Type": "application/json",
|
|
110
|
+
Accept: "text/event-stream",
|
|
111
|
+
...headers
|
|
112
|
+
};
|
|
113
|
+
const requestBody = {
|
|
114
|
+
message: content,
|
|
115
|
+
conversation_id: state.conversationId,
|
|
116
|
+
...bodyExtra
|
|
117
|
+
};
|
|
118
|
+
if (attachments && attachments.length > 0) {
|
|
119
|
+
requestBody.attachments = attachments.map((a) => ({
|
|
120
|
+
filename: a.filename,
|
|
121
|
+
content_type: a.content_type,
|
|
122
|
+
data: a.data
|
|
123
|
+
}));
|
|
111
124
|
}
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
const body = JSON.stringify(requestBody);
|
|
126
|
+
const handleEvent = (event) => {
|
|
127
|
+
switch (event.type) {
|
|
128
|
+
case "agent":
|
|
129
|
+
ctx.capturedAgent = event.agent;
|
|
130
|
+
dispatch({ type: "STREAM_AGENT", agent: ctx.capturedAgent });
|
|
131
|
+
break;
|
|
132
|
+
case "phase":
|
|
133
|
+
dispatch({ type: "STREAM_PHASE", phase: event.phase });
|
|
134
|
+
break;
|
|
135
|
+
case "delta":
|
|
136
|
+
ctx.accumulatedContent += event.content;
|
|
137
|
+
dispatch({ type: "STREAM_CONTENT", content: event.content });
|
|
138
|
+
break;
|
|
139
|
+
case "delta_reset":
|
|
140
|
+
ctx.accumulatedContent = "";
|
|
141
|
+
dispatch({ type: "STREAM_CONTENT_RESET" });
|
|
142
|
+
break;
|
|
143
|
+
case "done":
|
|
144
|
+
ctx.agentResponse = event.response;
|
|
145
|
+
ctx.capturedConversationId = event.conversation_id ?? null;
|
|
146
|
+
break;
|
|
147
|
+
case "error":
|
|
148
|
+
ctx.hadStreamError = true;
|
|
149
|
+
dispatch({ type: "SEND_ERROR", error: event.error });
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
const { streamAdapter } = configRef.current;
|
|
154
|
+
if (streamAdapter) {
|
|
155
|
+
await streamAdapter(
|
|
156
|
+
url,
|
|
157
|
+
{ method: "POST", headers: mergedHeaders, body, signal: controller.signal },
|
|
158
|
+
handleEvent
|
|
159
|
+
);
|
|
160
|
+
clearTimeout(timeoutId);
|
|
161
|
+
} else {
|
|
162
|
+
const response = await fetch(url, {
|
|
163
|
+
method: "POST",
|
|
164
|
+
headers: mergedHeaders,
|
|
165
|
+
body,
|
|
166
|
+
signal: controller.signal
|
|
117
167
|
});
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
dispatch({ type: "SEND_ERROR", error: event.error });
|
|
155
|
-
return;
|
|
168
|
+
clearTimeout(timeoutId);
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
dispatch({
|
|
171
|
+
type: "SEND_ERROR",
|
|
172
|
+
error: {
|
|
173
|
+
code: "API_ERROR",
|
|
174
|
+
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
175
|
+
retryable: response.status >= 500
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const reader = response.body?.getReader();
|
|
181
|
+
if (!reader) {
|
|
182
|
+
dispatch({
|
|
183
|
+
type: "SEND_ERROR",
|
|
184
|
+
error: { code: "STREAM_ERROR", message: "No response body", retryable: true }
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const decoder = new TextDecoder();
|
|
189
|
+
let buffer = "";
|
|
190
|
+
while (true) {
|
|
191
|
+
const { done, value } = await reader.read();
|
|
192
|
+
if (done) break;
|
|
193
|
+
buffer += decoder.decode(value, { stream: true });
|
|
194
|
+
const lines = buffer.split("\n");
|
|
195
|
+
buffer = lines.pop() ?? "";
|
|
196
|
+
for (const line of lines) {
|
|
197
|
+
if (!line.startsWith("data: ")) continue;
|
|
198
|
+
const data = line.slice(6).trim();
|
|
199
|
+
if (data === "[DONE]") continue;
|
|
200
|
+
try {
|
|
201
|
+
const event = JSON.parse(data);
|
|
202
|
+
handleEvent(event);
|
|
203
|
+
} catch {
|
|
156
204
|
}
|
|
157
|
-
} catch {
|
|
158
205
|
}
|
|
159
206
|
}
|
|
160
207
|
}
|
|
208
|
+
if (ctx.hadStreamError) return;
|
|
161
209
|
const assistantMessage = {
|
|
162
210
|
id: generateMessageId(),
|
|
163
211
|
role: "assistant",
|
|
164
|
-
content: agentResponse?.message ?? accumulatedContent,
|
|
165
|
-
response: agentResponse ?? void 0,
|
|
166
|
-
agent: capturedAgent ?? void 0,
|
|
212
|
+
content: ctx.agentResponse?.message ?? ctx.accumulatedContent,
|
|
213
|
+
response: ctx.agentResponse ?? void 0,
|
|
214
|
+
agent: ctx.capturedAgent ?? void 0,
|
|
167
215
|
timestamp: /* @__PURE__ */ new Date()
|
|
168
216
|
};
|
|
169
217
|
dispatch({
|
|
170
218
|
type: "SEND_SUCCESS",
|
|
171
219
|
message: assistantMessage,
|
|
172
|
-
streamingContent: accumulatedContent,
|
|
173
|
-
conversationId: capturedConversationId
|
|
220
|
+
streamingContent: ctx.accumulatedContent,
|
|
221
|
+
conversationId: ctx.capturedConversationId
|
|
174
222
|
});
|
|
175
223
|
} catch (err) {
|
|
176
224
|
clearTimeout(timeoutId);
|
|
177
225
|
if (err.name === "AbortError") {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
226
|
+
if (ctx.accumulatedContent) {
|
|
227
|
+
const partialMessage = {
|
|
228
|
+
id: generateMessageId(),
|
|
229
|
+
role: "assistant",
|
|
230
|
+
content: ctx.accumulatedContent,
|
|
231
|
+
agent: ctx.capturedAgent ?? void 0,
|
|
232
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
233
|
+
};
|
|
234
|
+
dispatch({
|
|
235
|
+
type: "SEND_SUCCESS",
|
|
236
|
+
message: partialMessage,
|
|
237
|
+
streamingContent: ctx.accumulatedContent,
|
|
238
|
+
conversationId: ctx.capturedConversationId
|
|
239
|
+
});
|
|
240
|
+
} else {
|
|
241
|
+
dispatch({
|
|
242
|
+
type: "SEND_ERROR",
|
|
243
|
+
error: { code: "ABORTED", message: "Request stopped", retryable: true }
|
|
244
|
+
});
|
|
245
|
+
}
|
|
182
246
|
} else {
|
|
183
247
|
dispatch({
|
|
184
248
|
type: "SEND_ERROR",
|
|
@@ -189,6 +253,8 @@ function useAgentChat(config) {
|
|
|
189
253
|
}
|
|
190
254
|
});
|
|
191
255
|
}
|
|
256
|
+
} finally {
|
|
257
|
+
abortControllerRef.current = null;
|
|
192
258
|
}
|
|
193
259
|
},
|
|
194
260
|
[state.conversationId]
|
|
@@ -201,7 +267,8 @@ function useAgentChat(config) {
|
|
|
201
267
|
}, []);
|
|
202
268
|
const submitFeedback = useCallback(
|
|
203
269
|
async (messageId, rating, comment) => {
|
|
204
|
-
const { apiUrl, feedbackPath = "/feedback", headers
|
|
270
|
+
const { apiUrl, feedbackPath = "/feedback", headers: headersOrFn } = configRef.current;
|
|
271
|
+
const headers = typeof headersOrFn === "function" ? await headersOrFn() : headersOrFn ?? {};
|
|
205
272
|
await fetch(`${apiUrl}${feedbackPath}`, {
|
|
206
273
|
method: "POST",
|
|
207
274
|
headers: { "Content-Type": "application/json", ...headers },
|
|
@@ -212,12 +279,16 @@ function useAgentChat(config) {
|
|
|
212
279
|
);
|
|
213
280
|
const retry = useCallback(async () => {
|
|
214
281
|
if (lastUserMessageRef.current) {
|
|
215
|
-
await sendMessage(lastUserMessageRef.current);
|
|
282
|
+
await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current);
|
|
216
283
|
}
|
|
217
284
|
}, [sendMessage]);
|
|
285
|
+
const stop = useCallback(() => {
|
|
286
|
+
abortControllerRef.current?.abort();
|
|
287
|
+
}, []);
|
|
218
288
|
const reset = useCallback(() => {
|
|
219
289
|
dispatch({ type: "RESET" });
|
|
220
290
|
lastUserMessageRef.current = null;
|
|
291
|
+
lastUserAttachmentsRef.current = void 0;
|
|
221
292
|
}, []);
|
|
222
293
|
const actions = {
|
|
223
294
|
sendMessage,
|
|
@@ -225,6 +296,7 @@ function useAgentChat(config) {
|
|
|
225
296
|
loadConversation,
|
|
226
297
|
submitFeedback,
|
|
227
298
|
retry,
|
|
299
|
+
stop,
|
|
228
300
|
reset
|
|
229
301
|
};
|
|
230
302
|
return { state, actions };
|
|
@@ -577,7 +649,10 @@ function useCharacterDrain(target, msPerChar = 15) {
|
|
|
577
649
|
const elapsed = now - lastTimeRef.current;
|
|
578
650
|
const charsToAdvance = Math.floor(elapsed / msPerCharRef.current);
|
|
579
651
|
if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {
|
|
580
|
-
|
|
652
|
+
let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length);
|
|
653
|
+
while (nextIndex < currentTarget.length && currentTarget[nextIndex - 1].trim() === "") {
|
|
654
|
+
nextIndex++;
|
|
655
|
+
}
|
|
581
656
|
indexRef.current = nextIndex;
|
|
582
657
|
lastTimeRef.current = now;
|
|
583
658
|
setDisplayed(currentTarget.slice(0, nextIndex));
|