ai-schadcn-chat 0.1.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.
Files changed (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +307 -0
  3. package/dist/ChatPanel-Cyq7boWQ.js +3468 -0
  4. package/dist/ChatPanel-Cyq7boWQ.js.map +1 -0
  5. package/dist/ChatPanel-D3VL5Vdq.cjs +10 -0
  6. package/dist/ChatPanel-D3VL5Vdq.cjs.map +1 -0
  7. package/dist/ChatProvider-DcrNbVMh.cjs +2 -0
  8. package/dist/ChatProvider-DcrNbVMh.cjs.map +1 -0
  9. package/dist/ChatProvider-DoD4jj4M.js +128 -0
  10. package/dist/ChatProvider-DoD4jj4M.js.map +1 -0
  11. package/dist/chat-engine-BaFdimfx.cjs +8 -0
  12. package/dist/chat-engine-BaFdimfx.cjs.map +1 -0
  13. package/dist/chat-engine-Cja0KtWE.js +520 -0
  14. package/dist/chat-engine-Cja0KtWE.js.map +1 -0
  15. package/dist/components/chat/AgentManager.d.ts +11 -0
  16. package/dist/components/chat/AgentManager.d.ts.map +1 -0
  17. package/dist/components/chat/AgentManager.js +110 -0
  18. package/dist/components/chat/Attachment.d.ts +34 -0
  19. package/dist/components/chat/Attachment.d.ts.map +1 -0
  20. package/dist/components/chat/Attachment.js +59 -0
  21. package/dist/components/chat/AttachmentPreview.d.ts +8 -0
  22. package/dist/components/chat/AttachmentPreview.d.ts.map +1 -0
  23. package/dist/components/chat/AttachmentPreview.js +43 -0
  24. package/dist/components/chat/ChatComposer.d.ts +11 -0
  25. package/dist/components/chat/ChatComposer.d.ts.map +1 -0
  26. package/dist/components/chat/ChatComposer.js +10 -0
  27. package/dist/components/chat/ChatHeader.d.ts +5 -0
  28. package/dist/components/chat/ChatHeader.d.ts.map +1 -0
  29. package/dist/components/chat/ChatHeader.js +132 -0
  30. package/dist/components/chat/ChatPanel.d.ts +36 -0
  31. package/dist/components/chat/ChatPanel.d.ts.map +1 -0
  32. package/dist/components/chat/ChatPanel.js +55 -0
  33. package/dist/components/chat/ChatProvider.d.ts +58 -0
  34. package/dist/components/chat/ChatProvider.d.ts.map +1 -0
  35. package/dist/components/chat/ChatProvider.js +145 -0
  36. package/dist/components/chat/Chatbubble.d.ts +30 -0
  37. package/dist/components/chat/Chatbubble.d.ts.map +1 -0
  38. package/dist/components/chat/Chatbubble.js +50 -0
  39. package/dist/components/chat/Markdown.d.ts +35 -0
  40. package/dist/components/chat/Markdown.d.ts.map +1 -0
  41. package/dist/components/chat/Markdown.js +130 -0
  42. package/dist/components/chat/Message.d.ts +22 -0
  43. package/dist/components/chat/Message.d.ts.map +1 -0
  44. package/dist/components/chat/Message.js +21 -0
  45. package/dist/components/chat/MessageInput.d.ts +24 -0
  46. package/dist/components/chat/MessageInput.d.ts.map +1 -0
  47. package/dist/components/chat/MessageInput.js +255 -0
  48. package/dist/components/chat/MessageList.d.ts +9 -0
  49. package/dist/components/chat/MessageList.d.ts.map +1 -0
  50. package/dist/components/chat/MessageList.js +249 -0
  51. package/dist/components/chat/MessageMarker.d.ts +25 -0
  52. package/dist/components/chat/MessageMarker.d.ts.map +1 -0
  53. package/dist/components/chat/MessageMarker.js +31 -0
  54. package/dist/components/chat/MessageScroller.d.ts +12 -0
  55. package/dist/components/chat/MessageScroller.d.ts.map +1 -0
  56. package/dist/components/chat/MessageScroller.js +48 -0
  57. package/dist/components/chat/MessageScrollerPrimitives.d.ts +76 -0
  58. package/dist/components/chat/MessageScrollerPrimitives.d.ts.map +1 -0
  59. package/dist/components/chat/MessageScrollerPrimitives.js +438 -0
  60. package/dist/components/chat/ProviderManager.d.ts +10 -0
  61. package/dist/components/chat/ProviderManager.d.ts.map +1 -0
  62. package/dist/components/chat/ProviderManager.js +110 -0
  63. package/dist/components/chat/ReasoningPanel.d.ts +15 -0
  64. package/dist/components/chat/ReasoningPanel.d.ts.map +1 -0
  65. package/dist/components/chat/ReasoningPanel.js +40 -0
  66. package/dist/components/chat/ThemeToggle.d.ts +4 -0
  67. package/dist/components/chat/ThemeToggle.d.ts.map +1 -0
  68. package/dist/components/chat/ThemeToggle.js +59 -0
  69. package/dist/components/index.d.ts +44 -0
  70. package/dist/components/index.d.ts.map +1 -0
  71. package/dist/components/index.js +49 -0
  72. package/dist/components/ui/avatar.d.ts +11 -0
  73. package/dist/components/ui/avatar.d.ts.map +1 -0
  74. package/dist/components/ui/avatar.js +33 -0
  75. package/dist/components/ui/badge.d.ts +10 -0
  76. package/dist/components/ui/badge.d.ts.map +1 -0
  77. package/dist/components/ui/badge.js +19 -0
  78. package/dist/components/ui/button.d.ts +12 -0
  79. package/dist/components/ui/button.d.ts.map +1 -0
  80. package/dist/components/ui/button.js +34 -0
  81. package/dist/components/ui/card.d.ts +8 -0
  82. package/dist/components/ui/card.d.ts.map +1 -0
  83. package/dist/components/ui/card.js +15 -0
  84. package/dist/components/ui/checkbox.d.ts +5 -0
  85. package/dist/components/ui/checkbox.d.ts.map +1 -0
  86. package/dist/components/ui/checkbox.js +8 -0
  87. package/dist/components/ui/collapsible.d.ts +6 -0
  88. package/dist/components/ui/collapsible.d.ts.map +1 -0
  89. package/dist/components/ui/collapsible.js +5 -0
  90. package/dist/components/ui/dialog.d.ts +24 -0
  91. package/dist/components/ui/dialog.d.ts.map +1 -0
  92. package/dist/components/ui/dialog.js +30 -0
  93. package/dist/components/ui/dropdown-menu.d.ts +17 -0
  94. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  95. package/dist/components/ui/dropdown-menu.js +20 -0
  96. package/dist/components/ui/input.d.ts +4 -0
  97. package/dist/components/ui/input.d.ts.map +1 -0
  98. package/dist/components/ui/input.js +7 -0
  99. package/dist/components/ui/label.d.ts +6 -0
  100. package/dist/components/ui/label.d.ts.map +1 -0
  101. package/dist/components/ui/label.js +10 -0
  102. package/dist/components/ui/popover.d.ts +6 -0
  103. package/dist/components/ui/popover.d.ts.map +1 -0
  104. package/dist/components/ui/popover.js +9 -0
  105. package/dist/components/ui/scroll-area.d.ts +3 -0
  106. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  107. package/dist/components/ui/scroll-area.js +8 -0
  108. package/dist/components/ui/select.d.ts +14 -0
  109. package/dist/components/ui/select.d.ts.map +1 -0
  110. package/dist/components/ui/select.js +26 -0
  111. package/dist/components/ui/separator.d.ts +3 -0
  112. package/dist/components/ui/separator.d.ts.map +1 -0
  113. package/dist/components/ui/separator.js +6 -0
  114. package/dist/components/ui/switch.d.ts +3 -0
  115. package/dist/components/ui/switch.d.ts.map +1 -0
  116. package/dist/components/ui/switch.js +6 -0
  117. package/dist/components/ui/tabs.d.ts +6 -0
  118. package/dist/components/ui/tabs.d.ts.map +1 -0
  119. package/dist/components/ui/tabs.js +11 -0
  120. package/dist/components/ui/tooltip.d.ts +6 -0
  121. package/dist/components/ui/tooltip.d.ts.map +1 -0
  122. package/dist/components/ui/tooltip.js +9 -0
  123. package/dist/components.cjs +2 -0
  124. package/dist/components.cjs.map +1 -0
  125. package/dist/components.js +129 -0
  126. package/dist/components.js.map +1 -0
  127. package/dist/hooks/index.d.ts +2 -0
  128. package/dist/hooks/index.d.ts.map +1 -0
  129. package/dist/hooks/index.js +3 -0
  130. package/dist/hooks/useChat.d.ts +6 -0
  131. package/dist/hooks/useChat.d.ts.map +1 -0
  132. package/dist/hooks/useChat.js +5 -0
  133. package/dist/hooks.cjs +2 -0
  134. package/dist/hooks.cjs.map +1 -0
  135. package/dist/hooks.js +8 -0
  136. package/dist/hooks.js.map +1 -0
  137. package/dist/index-Ci4qkGOp.cjs +20 -0
  138. package/dist/index-Ci4qkGOp.cjs.map +1 -0
  139. package/dist/index-NhYr-cEJ.js +567 -0
  140. package/dist/index-NhYr-cEJ.js.map +1 -0
  141. package/dist/index.cjs +2 -0
  142. package/dist/index.cjs.map +1 -0
  143. package/dist/index.d.ts +33 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +32 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/lib/agents.d.ts +29 -0
  148. package/dist/lib/agents.d.ts.map +1 -0
  149. package/dist/lib/agents.js +145 -0
  150. package/dist/lib/attachments.d.ts +40 -0
  151. package/dist/lib/attachments.d.ts.map +1 -0
  152. package/dist/lib/attachments.js +152 -0
  153. package/dist/lib/chat-engine.d.ts +109 -0
  154. package/dist/lib/chat-engine.d.ts.map +1 -0
  155. package/dist/lib/chat-engine.js +505 -0
  156. package/dist/lib/index.d.ts +8 -0
  157. package/dist/lib/index.d.ts.map +1 -0
  158. package/dist/lib/index.js +7 -0
  159. package/dist/lib/persistence.d.ts +30 -0
  160. package/dist/lib/persistence.d.ts.map +1 -0
  161. package/dist/lib/persistence.js +104 -0
  162. package/dist/lib/providers.d.ts +40 -0
  163. package/dist/lib/providers.d.ts.map +1 -0
  164. package/dist/lib/providers.js +0 -0
  165. package/dist/lib/tw-merge.d.ts +9 -0
  166. package/dist/lib/tw-merge.d.ts.map +1 -0
  167. package/dist/lib/tw-merge.js +8 -0
  168. package/dist/lib/utils.d.ts +69 -0
  169. package/dist/lib/utils.d.ts.map +1 -0
  170. package/dist/lib/utils.js +166 -0
  171. package/dist/lib/voice.d.ts +57 -0
  172. package/dist/lib/voice.d.ts.map +1 -0
  173. package/dist/lib/voice.js +103 -0
  174. package/dist/lib.cjs +2 -0
  175. package/dist/lib.cjs.map +1 -0
  176. package/dist/lib.js +59 -0
  177. package/dist/lib.js.map +1 -0
  178. package/dist/marker.css +68 -0
  179. package/dist/package.json +6 -0
  180. package/dist/presets-BxnbImSM.cjs +2 -0
  181. package/dist/presets-BxnbImSM.cjs.map +1 -0
  182. package/dist/presets-D8VQTD8n.js +193 -0
  183. package/dist/presets-D8VQTD8n.js.map +1 -0
  184. package/dist/providers/anthropic.d.ts +30 -0
  185. package/dist/providers/anthropic.d.ts.map +1 -0
  186. package/dist/providers/anthropic.js +251 -0
  187. package/dist/providers/base.d.ts +55 -0
  188. package/dist/providers/base.d.ts.map +1 -0
  189. package/dist/providers/base.js +272 -0
  190. package/dist/providers/index.d.ts +15 -0
  191. package/dist/providers/index.d.ts.map +1 -0
  192. package/dist/providers/index.js +24 -0
  193. package/dist/providers/openai.d.ts +31 -0
  194. package/dist/providers/openai.d.ts.map +1 -0
  195. package/dist/providers/openai.js +192 -0
  196. package/dist/providers-BuaGy2tX.cjs +4 -0
  197. package/dist/providers-BuaGy2tX.cjs.map +1 -0
  198. package/dist/providers-kp-avTNl.js +324 -0
  199. package/dist/providers-kp-avTNl.js.map +1 -0
  200. package/dist/providers.cjs +2 -0
  201. package/dist/providers.cjs.map +1 -0
  202. package/dist/providers.js +14 -0
  203. package/dist/providers.js.map +1 -0
  204. package/dist/scroller.css +278 -0
  205. package/dist/style.css +1 -0
  206. package/dist/types/chat.d.ts +342 -0
  207. package/dist/types/chat.d.ts.map +1 -0
  208. package/dist/types/chat.js +4 -0
  209. package/dist/types/index.d.ts +9 -0
  210. package/dist/types/index.d.ts.map +1 -0
  211. package/dist/types/index.js +14 -0
  212. package/dist/types/presets.d.ts +25 -0
  213. package/dist/types/presets.d.ts.map +1 -0
  214. package/dist/types/presets.js +207 -0
  215. package/dist/types.cjs +2 -0
  216. package/dist/types.cjs.map +1 -0
  217. package/dist/types.js +16 -0
  218. package/dist/types.js.map +1 -0
  219. package/dist/typeset-presets.css +56 -0
  220. package/dist/typeset.css +505 -0
  221. package/package.json +158 -0
@@ -0,0 +1,2 @@
1
+ "use strict";const q=require("react/jsx-runtime"),t=require("react"),U=require("./chat-engine-BaFdimfx.cjs"),j=require("./presets-BxnbImSM.cjs"),c=t.createContext(null);function F(n){const e=t.useRef(null);e.current===null&&(e.current=new U.ChatEngine(n.config));const o=t.useRef(n.config);D(o.current,n.config)||(o.current=n.config,e.current.updateConfig(n.config));const a=t.useCallback(s=>e.current.subscribe(s),[]),u=t.useCallback(()=>e.current.getMessagesSnapshot(),[]),i=t.useCallback(()=>e.current.getConfig(),[]),l=t.useCallback(()=>e.current.getActiveConversationId(),[]),C=t.useCallback(()=>e.current.getMessagesSnapshot().some(s=>s.status==="streaming"),[]),d=t.useSyncExternalStore(a,u,u),f=t.useSyncExternalStore(a,i,i),g=t.useSyncExternalStore(a,l,l),v=t.useSyncExternalStore(a,C,C),b=t.useCallback(s=>{e.current.updateConfig(s)},[]),k=t.useCallback(s=>{e.current.setActiveConversationId(s)},[]),h=t.useCallback(async(s,r)=>{await e.current.sendUserMessage(s,r)},[]),m=t.useCallback(async(s,r)=>{await e.current.sendFiles(s,r)},[]),S=t.useCallback(()=>e.current.abort(),[]),x=t.useCallback(()=>e.current.regenerate(),[]),M=t.useCallback((s,r)=>e.current.editAndResend(s,r),[]),y=t.useCallback(()=>e.current.clear(),[]),p=t.useCallback(()=>e.current.newConversation(),[]),w=t.useCallback(s=>{e.current.deleteMessage(s)},[]),E=t.useCallback(s=>{e.current.deleteConversation(s)},[]),R=t.useCallback(()=>e.current.listConversationIds(),[]),A=t.useCallback(()=>e.current.listConversationsMeta(),[]),I=t.useCallback(()=>e.current.getActiveToolCallCount(),[]),P=t.useMemo(()=>({engine:e.current,config:f,updateConfig:b,messages:d,isStreaming:v,conversationId:g,setConversationId:k,send:h,sendFiles:m,abort:S,regenerate:x,editAndResend:M,clear:y,newConversation:p,deleteMessage:w,deleteConversation:E,listConversations:R,listConversationsMeta:A,getActiveToolCallCount:I}),[f,d,v,g,b,k,h,m,S,x,M,y,p,w,E,R,A,I]);return q.jsx(c.Provider,{value:P,children:n.children})}function K(){const n=t.useContext(c);if(!n)throw new Error("useChat must be used inside <ChatProvider>");return n}function O(){return t.useContext(c)}function T(n={}){return j.buildDefaultMiniMaxConfig(n)}function D(n,e){return n===e?!0:!(n.model.id!==e.model.id||n.provider.baseUrl!==e.provider.baseUrl||n.provider.credentials.apiKey!==e.provider.credentials.apiKey||n.systemPrompt!==e.systemPrompt||n.temperature!==e.temperature)}exports.ChatProvider=F;exports.defaultConfig=T;exports.useChat=K;exports.useOptionalChat=O;
2
+ //# sourceMappingURL=ChatProvider-DcrNbVMh.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatProvider-DcrNbVMh.cjs","sources":["../src/components/chat/ChatProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\";\nimport type {\n AttachmentMeta,\n ChatConfig,\n ChatMessage,\n ConversationMeta,\n} from \"../../types/chat.js\";\nimport { ChatEngine as ChatEngineImpl } from \"../../lib/chat-engine.js\";\nimport { buildDefaultMiniMaxConfig } from \"../../types/presets.js\";\n\nexport interface ChatContextValue {\n engine: ChatEngineImpl;\n config: ChatConfig;\n /**\n * Update the active config. Two overloads:\n * - `updateConfig(partial)` — shallow-merge partial into the current config.\n * - `updateConfig(updater)` — functional form, receives the current\n * config and returns the partial to merge. Use this when the partial\n * needs to read the current config (avoids stale-closure bugs in\n * long-lived form components).\n */\n updateConfig(partial: Partial<ChatConfig>): void;\n updateConfig(\n updater: (current: ChatConfig) => Partial<ChatConfig>,\n ): void;\n messages: ChatMessage[];\n isStreaming: boolean;\n conversationId: string;\n setConversationId: (id: string) => void;\n send: (text: string, attachments?: AttachmentMeta[]) => Promise<void>;\n sendFiles: (text: string, files: File[]) => Promise<void>;\n abort: () => void;\n regenerate: () => Promise<void>;\n editAndResend: (messageId: string, newContent: string) => Promise<void>;\n clear: () => void;\n newConversation: () => string;\n deleteMessage: (messageId: string) => void;\n deleteConversation: (id: string) => void;\n listConversations: () => string[];\n listConversationsMeta: () => ConversationMeta[];\n /** Count of in-flight tool calls for the active conversation. */\n getActiveToolCallCount: () => number;\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null);\n\nexport interface ChatProviderProps {\n config: ChatConfig;\n children: ReactNode;\n}\n\n/**\n * Mount a chat engine for the lifetime of the React subtree. Multiple\n * ChatProviders can coexist; each gets its own engine. The provider\n * uses useSyncExternalStore for concurrent-mode-safe subscriptions.\n */\nexport function ChatProvider(props: ChatProviderProps): ReactNode {\n const engineRef = useRef<ChatEngineImpl | null>(null);\n if (engineRef.current === null) {\n engineRef.current = new ChatEngineImpl(props.config);\n }\n\n // Whenever the `config` prop changes, sync it into the engine.\n // We compare by JSON to avoid spurious updates that would still\n // trigger a re-render through the subscription.\n const configRef = useRef<ChatConfig>(props.config);\n if (!shallowEqual(configRef.current, props.config)) {\n configRef.current = props.config;\n engineRef.current.updateConfig(props.config);\n }\n\n // useSyncExternalStore requires the subscribe callback to be stable\n // across renders — otherwise React tears down + recreates the\n // subscription on every render and emits are lost. Memoize once\n // against the engineRef, which itself is stable for the provider's\n // lifetime.\n const subscribe = useCallback(\n (cb: () => void) => engineRef.current!.subscribe(cb),\n [],\n );\n const getMessages = useCallback(\n () => engineRef.current!.getMessagesSnapshot(),\n [],\n );\n const getConfigSnapshot = useCallback(\n () => engineRef.current!.getConfig(),\n [],\n );\n const getActiveConversationId = useCallback(\n () => engineRef.current!.getActiveConversationId(),\n [],\n );\n const getIsStreaming = useCallback(\n () =>\n engineRef.current!\n .getMessagesSnapshot()\n .some((m: ChatMessage) => m.status === \"streaming\"),\n [],\n );\n\n const messages = useSyncExternalStore(subscribe, getMessages, getMessages);\n const config = useSyncExternalStore(\n subscribe,\n getConfigSnapshot,\n getConfigSnapshot,\n );\n const conversationId = useSyncExternalStore(\n subscribe,\n getActiveConversationId,\n getActiveConversationId,\n );\n const isStreaming = useSyncExternalStore(\n subscribe,\n getIsStreaming,\n getIsStreaming,\n );\n\n const updateConfig = useCallback(\n (\n arg:\n | Partial<ChatConfig>\n | ((current: ChatConfig) => Partial<ChatConfig>),\n ) => {\n engineRef.current!.updateConfig(arg as never);\n },\n [],\n );\n\n const setConversationId = useCallback((id: string) => {\n engineRef.current!.setActiveConversationId(id);\n }, []);\n\n const send = useCallback(async (text: string, attachments?: AttachmentMeta[]) => {\n await engineRef.current!.sendUserMessage(text, attachments);\n }, []);\n\n const sendFiles = useCallback(async (text: string, files: File[]) => {\n await engineRef.current!.sendFiles(text, files);\n }, []);\n\n const abort = useCallback(() => engineRef.current!.abort(), []);\n const regenerate = useCallback(() => engineRef.current!.regenerate(), []);\n const editAndResend = useCallback(\n (id: string, content: string) => engineRef.current!.editAndResend(id, content),\n [],\n );\n const clear = useCallback(() => engineRef.current!.clear(), []);\n const newConversation = useCallback(() => engineRef.current!.newConversation(), []);\n const deleteMessage = useCallback((messageId: string) => {\n engineRef.current!.deleteMessage(messageId);\n }, []);\n const deleteConversation = useCallback((id: string) => {\n engineRef.current!.deleteConversation(id);\n }, []);\n const listConversations = useCallback(() => engineRef.current!.listConversationIds(), []);\n const listConversationsMeta = useCallback(\n () => engineRef.current!.listConversationsMeta(),\n [],\n );\n const getActiveToolCallCount = useCallback(\n () => engineRef.current!.getActiveToolCallCount(),\n [],\n );\n\n const value = useMemo<ChatContextValue>(\n () => ({\n engine: engineRef.current!,\n config,\n updateConfig,\n messages,\n isStreaming,\n conversationId,\n setConversationId,\n send,\n sendFiles,\n abort,\n regenerate,\n editAndResend,\n clear,\n newConversation,\n deleteMessage,\n deleteConversation,\n listConversations,\n listConversationsMeta,\n getActiveToolCallCount,\n }),\n [\n config,\n messages,\n isStreaming,\n conversationId,\n updateConfig,\n setConversationId,\n send,\n sendFiles,\n abort,\n regenerate,\n editAndResend,\n clear,\n newConversation,\n deleteMessage,\n deleteConversation,\n listConversations,\n listConversationsMeta,\n getActiveToolCallCount,\n ],\n );\n\n return <ChatContext.Provider value={value}>{props.children}</ChatContext.Provider>;\n}\n\n/**\n * Hook to access the chat engine. Throws when used outside a ChatProvider.\n */\nexport function useChat(): ChatContextValue {\n const value = useContext(ChatContext);\n if (!value) {\n throw new Error(\"useChat must be used inside <ChatProvider>\");\n }\n return value;\n}\n\n/**\n * Variant of useChat that returns null instead of throwing. Use in\n * components that may render both inside and outside a provider.\n */\nexport function useOptionalChat(): ChatContextValue | null {\n return useContext(ChatContext);\n}\n\n/**\n * Default config helper for the MiniMax provider.\n */\nexport function defaultConfig(overrides: Partial<ChatConfig> = {}): ChatConfig {\n return buildDefaultMiniMaxConfig(overrides);\n}\n\nfunction shallowEqual(a: ChatConfig, b: ChatConfig): boolean {\n if (a === b) return true;\n if (a.model.id !== b.model.id) return false;\n if (a.provider.baseUrl !== b.provider.baseUrl) return false;\n if (a.provider.credentials.apiKey !== b.provider.credentials.apiKey) return false;\n if (a.systemPrompt !== b.systemPrompt) return false;\n if (a.temperature !== b.temperature) return false;\n return true;\n}"],"names":["ChatContext","createContext","ChatProvider","props","engineRef","useRef","ChatEngineImpl","configRef","shallowEqual","subscribe","useCallback","cb","getMessages","getConfigSnapshot","getActiveConversationId","getIsStreaming","m","messages","useSyncExternalStore","config","conversationId","isStreaming","updateConfig","arg","setConversationId","id","send","text","attachments","sendFiles","files","abort","regenerate","editAndResend","content","clear","newConversation","deleteMessage","messageId","deleteConversation","listConversations","listConversationsMeta","getActiveToolCallCount","value","useMemo","useChat","useContext","useOptionalChat","defaultConfig","overrides","buildDefaultMiniMaxConfig","a","b"],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ import { jsx as j } from "react/jsx-runtime";
2
+ import { useRef as F, useCallback as t, useSyncExternalStore as i, useMemo as k, useContext as K, createContext as q } from "react";
3
+ import { C as D } from "./chat-engine-Cja0KtWE.js";
4
+ import { b as O } from "./presets-D8VQTD8n.js";
5
+ const c = q(null);
6
+ function L(n) {
7
+ const e = F(null);
8
+ e.current === null && (e.current = new D(n.config));
9
+ const a = F(n.config);
10
+ z(a.current, n.config) || (a.current = n.config, e.current.updateConfig(n.config));
11
+ const o = t(
12
+ (r) => e.current.subscribe(r),
13
+ []
14
+ ), u = t(
15
+ () => e.current.getMessagesSnapshot(),
16
+ []
17
+ ), l = t(
18
+ () => e.current.getConfig(),
19
+ []
20
+ ), d = t(
21
+ () => e.current.getActiveConversationId(),
22
+ []
23
+ ), f = t(
24
+ () => e.current.getMessagesSnapshot().some((r) => r.status === "streaming"),
25
+ []
26
+ ), g = i(o, u, u), C = i(
27
+ o,
28
+ l,
29
+ l
30
+ ), v = i(
31
+ o,
32
+ d,
33
+ d
34
+ ), m = i(
35
+ o,
36
+ f,
37
+ f
38
+ ), h = t(
39
+ (r) => {
40
+ e.current.updateConfig(r);
41
+ },
42
+ []
43
+ ), p = t((r) => {
44
+ e.current.setActiveConversationId(r);
45
+ }, []), M = t(async (r, s) => {
46
+ await e.current.sendUserMessage(r, s);
47
+ }, []), w = t(async (r, s) => {
48
+ await e.current.sendFiles(r, s);
49
+ }, []), b = t(() => e.current.abort(), []), x = t(() => e.current.regenerate(), []), y = t(
50
+ (r, s) => e.current.editAndResend(r, s),
51
+ []
52
+ ), A = t(() => e.current.clear(), []), I = t(() => e.current.newConversation(), []), S = t((r) => {
53
+ e.current.deleteMessage(r);
54
+ }, []), P = t((r) => {
55
+ e.current.deleteConversation(r);
56
+ }, []), R = t(() => e.current.listConversationIds(), []), E = t(
57
+ () => e.current.listConversationsMeta(),
58
+ []
59
+ ), U = t(
60
+ () => e.current.getActiveToolCallCount(),
61
+ []
62
+ ), T = k(
63
+ () => ({
64
+ engine: e.current,
65
+ config: C,
66
+ updateConfig: h,
67
+ messages: g,
68
+ isStreaming: m,
69
+ conversationId: v,
70
+ setConversationId: p,
71
+ send: M,
72
+ sendFiles: w,
73
+ abort: b,
74
+ regenerate: x,
75
+ editAndResend: y,
76
+ clear: A,
77
+ newConversation: I,
78
+ deleteMessage: S,
79
+ deleteConversation: P,
80
+ listConversations: R,
81
+ listConversationsMeta: E,
82
+ getActiveToolCallCount: U
83
+ }),
84
+ [
85
+ C,
86
+ g,
87
+ m,
88
+ v,
89
+ h,
90
+ p,
91
+ M,
92
+ w,
93
+ b,
94
+ x,
95
+ y,
96
+ A,
97
+ I,
98
+ S,
99
+ P,
100
+ R,
101
+ E,
102
+ U
103
+ ]
104
+ );
105
+ return /* @__PURE__ */ j(c.Provider, { value: T, children: n.children });
106
+ }
107
+ function N() {
108
+ const n = K(c);
109
+ if (!n)
110
+ throw new Error("useChat must be used inside <ChatProvider>");
111
+ return n;
112
+ }
113
+ function Q() {
114
+ return K(c);
115
+ }
116
+ function V(n = {}) {
117
+ return O(n);
118
+ }
119
+ function z(n, e) {
120
+ return n === e ? !0 : !(n.model.id !== e.model.id || n.provider.baseUrl !== e.provider.baseUrl || n.provider.credentials.apiKey !== e.provider.credentials.apiKey || n.systemPrompt !== e.systemPrompt || n.temperature !== e.temperature);
121
+ }
122
+ export {
123
+ L as C,
124
+ Q as a,
125
+ V as d,
126
+ N as u
127
+ };
128
+ //# sourceMappingURL=ChatProvider-DoD4jj4M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatProvider-DoD4jj4M.js","sources":["../src/components/chat/ChatProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\";\nimport type {\n AttachmentMeta,\n ChatConfig,\n ChatMessage,\n ConversationMeta,\n} from \"../../types/chat.js\";\nimport { ChatEngine as ChatEngineImpl } from \"../../lib/chat-engine.js\";\nimport { buildDefaultMiniMaxConfig } from \"../../types/presets.js\";\n\nexport interface ChatContextValue {\n engine: ChatEngineImpl;\n config: ChatConfig;\n /**\n * Update the active config. Two overloads:\n * - `updateConfig(partial)` — shallow-merge partial into the current config.\n * - `updateConfig(updater)` — functional form, receives the current\n * config and returns the partial to merge. Use this when the partial\n * needs to read the current config (avoids stale-closure bugs in\n * long-lived form components).\n */\n updateConfig(partial: Partial<ChatConfig>): void;\n updateConfig(\n updater: (current: ChatConfig) => Partial<ChatConfig>,\n ): void;\n messages: ChatMessage[];\n isStreaming: boolean;\n conversationId: string;\n setConversationId: (id: string) => void;\n send: (text: string, attachments?: AttachmentMeta[]) => Promise<void>;\n sendFiles: (text: string, files: File[]) => Promise<void>;\n abort: () => void;\n regenerate: () => Promise<void>;\n editAndResend: (messageId: string, newContent: string) => Promise<void>;\n clear: () => void;\n newConversation: () => string;\n deleteMessage: (messageId: string) => void;\n deleteConversation: (id: string) => void;\n listConversations: () => string[];\n listConversationsMeta: () => ConversationMeta[];\n /** Count of in-flight tool calls for the active conversation. */\n getActiveToolCallCount: () => number;\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null);\n\nexport interface ChatProviderProps {\n config: ChatConfig;\n children: ReactNode;\n}\n\n/**\n * Mount a chat engine for the lifetime of the React subtree. Multiple\n * ChatProviders can coexist; each gets its own engine. The provider\n * uses useSyncExternalStore for concurrent-mode-safe subscriptions.\n */\nexport function ChatProvider(props: ChatProviderProps): ReactNode {\n const engineRef = useRef<ChatEngineImpl | null>(null);\n if (engineRef.current === null) {\n engineRef.current = new ChatEngineImpl(props.config);\n }\n\n // Whenever the `config` prop changes, sync it into the engine.\n // We compare by JSON to avoid spurious updates that would still\n // trigger a re-render through the subscription.\n const configRef = useRef<ChatConfig>(props.config);\n if (!shallowEqual(configRef.current, props.config)) {\n configRef.current = props.config;\n engineRef.current.updateConfig(props.config);\n }\n\n // useSyncExternalStore requires the subscribe callback to be stable\n // across renders — otherwise React tears down + recreates the\n // subscription on every render and emits are lost. Memoize once\n // against the engineRef, which itself is stable for the provider's\n // lifetime.\n const subscribe = useCallback(\n (cb: () => void) => engineRef.current!.subscribe(cb),\n [],\n );\n const getMessages = useCallback(\n () => engineRef.current!.getMessagesSnapshot(),\n [],\n );\n const getConfigSnapshot = useCallback(\n () => engineRef.current!.getConfig(),\n [],\n );\n const getActiveConversationId = useCallback(\n () => engineRef.current!.getActiveConversationId(),\n [],\n );\n const getIsStreaming = useCallback(\n () =>\n engineRef.current!\n .getMessagesSnapshot()\n .some((m: ChatMessage) => m.status === \"streaming\"),\n [],\n );\n\n const messages = useSyncExternalStore(subscribe, getMessages, getMessages);\n const config = useSyncExternalStore(\n subscribe,\n getConfigSnapshot,\n getConfigSnapshot,\n );\n const conversationId = useSyncExternalStore(\n subscribe,\n getActiveConversationId,\n getActiveConversationId,\n );\n const isStreaming = useSyncExternalStore(\n subscribe,\n getIsStreaming,\n getIsStreaming,\n );\n\n const updateConfig = useCallback(\n (\n arg:\n | Partial<ChatConfig>\n | ((current: ChatConfig) => Partial<ChatConfig>),\n ) => {\n engineRef.current!.updateConfig(arg as never);\n },\n [],\n );\n\n const setConversationId = useCallback((id: string) => {\n engineRef.current!.setActiveConversationId(id);\n }, []);\n\n const send = useCallback(async (text: string, attachments?: AttachmentMeta[]) => {\n await engineRef.current!.sendUserMessage(text, attachments);\n }, []);\n\n const sendFiles = useCallback(async (text: string, files: File[]) => {\n await engineRef.current!.sendFiles(text, files);\n }, []);\n\n const abort = useCallback(() => engineRef.current!.abort(), []);\n const regenerate = useCallback(() => engineRef.current!.regenerate(), []);\n const editAndResend = useCallback(\n (id: string, content: string) => engineRef.current!.editAndResend(id, content),\n [],\n );\n const clear = useCallback(() => engineRef.current!.clear(), []);\n const newConversation = useCallback(() => engineRef.current!.newConversation(), []);\n const deleteMessage = useCallback((messageId: string) => {\n engineRef.current!.deleteMessage(messageId);\n }, []);\n const deleteConversation = useCallback((id: string) => {\n engineRef.current!.deleteConversation(id);\n }, []);\n const listConversations = useCallback(() => engineRef.current!.listConversationIds(), []);\n const listConversationsMeta = useCallback(\n () => engineRef.current!.listConversationsMeta(),\n [],\n );\n const getActiveToolCallCount = useCallback(\n () => engineRef.current!.getActiveToolCallCount(),\n [],\n );\n\n const value = useMemo<ChatContextValue>(\n () => ({\n engine: engineRef.current!,\n config,\n updateConfig,\n messages,\n isStreaming,\n conversationId,\n setConversationId,\n send,\n sendFiles,\n abort,\n regenerate,\n editAndResend,\n clear,\n newConversation,\n deleteMessage,\n deleteConversation,\n listConversations,\n listConversationsMeta,\n getActiveToolCallCount,\n }),\n [\n config,\n messages,\n isStreaming,\n conversationId,\n updateConfig,\n setConversationId,\n send,\n sendFiles,\n abort,\n regenerate,\n editAndResend,\n clear,\n newConversation,\n deleteMessage,\n deleteConversation,\n listConversations,\n listConversationsMeta,\n getActiveToolCallCount,\n ],\n );\n\n return <ChatContext.Provider value={value}>{props.children}</ChatContext.Provider>;\n}\n\n/**\n * Hook to access the chat engine. Throws when used outside a ChatProvider.\n */\nexport function useChat(): ChatContextValue {\n const value = useContext(ChatContext);\n if (!value) {\n throw new Error(\"useChat must be used inside <ChatProvider>\");\n }\n return value;\n}\n\n/**\n * Variant of useChat that returns null instead of throwing. Use in\n * components that may render both inside and outside a provider.\n */\nexport function useOptionalChat(): ChatContextValue | null {\n return useContext(ChatContext);\n}\n\n/**\n * Default config helper for the MiniMax provider.\n */\nexport function defaultConfig(overrides: Partial<ChatConfig> = {}): ChatConfig {\n return buildDefaultMiniMaxConfig(overrides);\n}\n\nfunction shallowEqual(a: ChatConfig, b: ChatConfig): boolean {\n if (a === b) return true;\n if (a.model.id !== b.model.id) return false;\n if (a.provider.baseUrl !== b.provider.baseUrl) return false;\n if (a.provider.credentials.apiKey !== b.provider.credentials.apiKey) return false;\n if (a.systemPrompt !== b.systemPrompt) return false;\n if (a.temperature !== b.temperature) return false;\n return true;\n}"],"names":["ChatContext","createContext","ChatProvider","props","engineRef","useRef","ChatEngineImpl","configRef","shallowEqual","subscribe","useCallback","cb","getMessages","getConfigSnapshot","getActiveConversationId","getIsStreaming","m","messages","useSyncExternalStore","config","conversationId","isStreaming","updateConfig","arg","setConversationId","id","send","text","attachments","sendFiles","files","abort","regenerate","editAndResend","content","clear","newConversation","deleteMessage","messageId","deleteConversation","listConversations","listConversationsMeta","getActiveToolCallCount","value","useMemo","useChat","useContext","useOptionalChat","defaultConfig","overrides","buildDefaultMiniMaxConfig","a","b"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ "use strict";const c=require("./index-Ci4qkGOp.cjs");async function k(i,t){const e=(t.ui?.maxFileSizeMb??10)*1024*1024,s=new Set(t.ui?.acceptedFileTypes??["image/png","image/jpeg","image/gif","image/webp","application/pdf","text/plain","text/markdown","text/csv","application/json"]),r=[];for(const o of i){if(o.size>e)throw new Error(`${o.name} is ${c.formatBytes(o.size)} — exceeds the ${c.formatBytes(e)} limit`);if(s.size>0&&!s.has(o.type))throw new Error(`${o.name} has unsupported type ${o.type||"unknown"}`);const l={id:c.uid("att"),name:o.name,mimeType:o.type||"application/octet-stream",size:o.size};o.type.startsWith("text/")||o.type==="application/json"?l.text=await c.readFileAsText(o):l.dataUrl=await c.readFileAsDataUrl(o),r.push(l)}return r}function S(i,t,e){if(!t?.length)return i;if(e==="anthropic"){const o=c.buildAnthropicUserContent({text:i,attachments:t});return o.length?JSON.stringify({__type:"ai-chat-blocks",blocks:o}):i}const r=c.buildOpenAIUserContent({text:i,attachments:t});return r.length?JSON.stringify({__type:"ai-chat-blocks",blocks:r}):i}function U(i){if(typeof document>"u")return;const t=i.dataUrl??i.url;if(!t)return;const e=document.createElement("a");e.href=t,e.download=i.name,e.rel="noopener",document.body.appendChild(e),e.click(),e.remove()}function D(i){return i.startsWith("image/")?"image":i.startsWith("video/")?"video":i.startsWith("audio/")?"audio":i==="application/pdf"?"pdf":i.startsWith("text/")||i==="application/json"?"text":"file"}function J(i){if(!i.startsWith("{"))return i;try{const t=JSON.parse(i);if(t?.__type==="ai-chat-blocks"&&Array.isArray(t.blocks))return t.blocks.map(e=>e.type==="text"&&typeof e.text=="string"?e.text:"").filter(Boolean).join(`
2
+ `).trim()}catch{}return i}function T(i){let t=i.content;if(t.startsWith("{"))try{const s=JSON.parse(t);s?.__type==="ai-chat-blocks"&&Array.isArray(s.blocks)&&(t=s.blocks.map(r=>r?.type==="text"&&typeof r.text=="string"?r.text:"").filter(Boolean).join(`
3
+ `))}catch{}const e=(i.attachments??[]).map(s=>s.name).join(", ");return e?`${t}
4
+
5
+ šŸ“Ž ${e}`:t}const v="ai-schadcn-chat:conversation:";function C(i){if(typeof window>"u")return null;try{const t=window.localStorage.getItem(v+i);if(!t)return null;const e=JSON.parse(t);return e?.messages?e:null}catch{return null}}function b(i){if(!(typeof window>"u"))try{const t=i.messages.map(e=>({...e,toolCalls:(e.toolCalls??[]).map(s=>({...s,result:s.result instanceof Object?$(s.result):s.result}))}));window.localStorage.setItem(v+i.id,JSON.stringify({...i,messages:t}))}catch{}}function x(){if(typeof window>"u")return[];const i=[];for(let t=0;t<window.localStorage.length;t++){const e=window.localStorage.key(t);if(!(!e||!e.startsWith(v)))try{const s=window.localStorage.getItem(e);if(!s)continue;const r=JSON.parse(s);r?.id&&i.push(r)}catch{}}return i.sort((t,e)=>e.updatedAt-t.updatedAt)}function _(i){if(!(typeof window>"u"))try{window.localStorage.removeItem(v+i)}catch{}}function W(){if(!(typeof window>"u"))try{const i=[];for(let t=0;t<window.localStorage.length;t++){const e=window.localStorage.key(t);e&&e.startsWith(v)&&i.push(e)}i.forEach(t=>window.localStorage.removeItem(t))}catch{}}function $(i){try{return JSON.parse(JSON.stringify(i))}catch{return}}function N(i,t=1e3){return i.messages.length<=t?i:{...i,messages:i.messages.slice(-t)}}class z{config;provider;listeners=new Set;conversations=new Map;activeId;constructor(t,e){if(this.config=t,this.provider=c.createProvider(t),this.activeId=e??c.uid("conv"),this.config.persistKey){const s=C(this.activeId);s?this.conversations.set(this.activeId,{id:this.activeId,messages:s.messages}):this.conversations.set(this.activeId,{id:this.activeId,messages:[]})}else this.conversations.set(this.activeId,{id:this.activeId,messages:[]})}getConfig(){return this.config}updateConfig(t){const e=typeof t=="function"?t(this.config):t;this.config={...this.config,...e},(e.provider||e.model)&&(this.provider=c.createProvider(this.config)),this.emit()}getActiveConversationId(){return this.activeId}getActiveToolCallCount(){return 0}setActiveConversationId(t){if(!this.conversations.has(t)){const e=this.config.persistKey?C(t):null;this.conversations.set(t,{id:t,messages:e?.messages??[]})}this.activeId=t,this.emit()}listConversationIds(){return Array.from(this.conversations.keys())}listConversationsMeta(){const t=new Map;for(const e of this.conversations.values())t.set(e.id,{id:e.id,title:R(e.messages),updatedAt:0,messageCount:e.messages.length});if(this.config.persistKey)for(const e of x()){const s=t.get(e.id);t.set(e.id,{id:e.id,title:e.title||s?.title||"New chat",updatedAt:e.updatedAt,messageCount:e.messages.length})}return Array.from(t.values()).sort((e,s)=>s.updatedAt-e.updatedAt)}getMessages(){return this.conversations.get(this.activeId)?.messages??[]}snapshotCache=[];snapshotDirty=!0;getMessagesSnapshot(){if(this.snapshotDirty){const t=this.conversations.get(this.activeId)?.messages;this.snapshotCache=t?[...t]:[],this.snapshotDirty=!1}return this.snapshotCache}subscribe(t){return this.listeners.add(t),()=>this.listeners.delete(t)}emit(){this.snapshotDirty=!0;for(const t of this.listeners)t()}persist(){if(!this.config.persistKey)return;const t=this.conversations.get(this.activeId);if(!t)return;const e=t.messages.find(r=>r.role==="user"),s=e?M(e.content.replace(/[\n\r]+/g," "),60):"New chat";b(N({id:t.id,title:s,messages:t.messages,updatedAt:Date.now(),config:{model:this.config.model.id,temperature:this.config.temperature}}))}abort(){this.provider.abort()}clear(){this.conversations.set(this.activeId,{id:this.activeId,messages:[]}),this.persist(),this.emit()}newConversation(){const t=c.uid("conv");return this.conversations.set(t,{id:t,messages:[]}),this.activeId=t,this.persist(),this.emit(),t}deleteMessage(t){const e=this.conversations.get(this.activeId);e&&(e.messages=e.messages.filter(s=>s.id!==t),this.persist(),this.emit())}deleteConversation(t){this.conversations.delete(t),this.activeId===t&&(this.activeId=this.conversations.keys().next().value??c.uid("conv"),this.conversations.set(this.activeId,{id:this.activeId,messages:[]})),this.persist(),this.emit()}async editAndResend(t,e){const s=this.conversations.get(this.activeId);if(!s)return;const r=s.messages.findIndex(o=>o.id===t);r<0||s.messages[r].role!=="user"||(s.messages[r]={...s.messages[r],content:e,status:"complete"},s.messages=s.messages.slice(0,r+1),this.emit(),this.persist(),await this.runCompletion())}async regenerate(){const t=this.conversations.get(this.activeId);if(t){for(let e=t.messages.length-1;e>=0;e--){if(t.messages[e].role==="assistant"){t.messages.splice(e,1);break}if(t.messages[e].role==="user")break}this.emit(),this.persist(),await this.runCompletion()}}async sendUserMessage(t,e){const s=this.conversations.get(this.activeId);if(!s||!t.trim()&&!e?.length)return;const r=S(t,e,this.config.model.provider??this.config.provider.kind),o={id:c.uid("msg"),role:"user",content:r,attachments:e,createdAt:Date.now(),status:"complete"};s.messages.push(o),this.emit(),this.persist(),await this.runCompletion()}async sendFiles(t,e){const s=await k(e,this.config);return this.sendUserMessage(t,s)}async runCompletion(){const t=this.conversations.get(this.activeId);if(!t)return;const e=c.uid("msg");let s={id:e,role:"assistant",content:"",createdAt:Date.now(),status:"streaming"};t.messages.push(s);const r=8;for(let o=0;o<r;o++)try{const{toolCalls:l,finished:h,finalUsage:f,model:g}=await this.streamAssistantTurn(s,t);if(l.length>0&&this.config.tools?.length){s.status="complete",s.toolCalls=l.map(a=>({id:a.id,name:a.name,arguments:a.arguments,status:a.error?"error":"complete",result:a.result,error:a.error}));for(const a of l){const I=this.config.tools?.find(p=>p.name===a.name),m={id:c.uid("msg"),role:"tool",content:a.error?`Error: ${a.error}`:typeof a.result=="string"?a.result:JSON.stringify(a.result??null),createdAt:Date.now(),status:"complete",toolCallId:a.id,name:a.name};t.messages.push(m),this.emit(),this.persist(),!a.error&&o<r-1&&(s={id:c.uid("msg"),role:"assistant",content:"",createdAt:Date.now(),status:"streaming"},t.messages.push(s))}if(!h)continue;break}if(h){s.status="complete",s.completedAt=Date.now(),f&&(s.usage=f),g&&(s.model=g);break}}catch(l){const h=c.toChatError(l);s.status="error",s.error=h,this.config.onError?.(h,{conversationId:this.activeId}),this.emit(),this.persist();return}this.config.onResponse?.(s,{conversationId:this.activeId}),this.emit(),this.persist()}async streamAssistantTurn(t,e){const s=this.config.maxContextTokens??32e3,r=c.trimMessagesByTokens(e.messages,s),o=new Map;let l="",h="",f,g,a;const I=this.provider.stream({messages:r,signal:void 0},this.config);let m;for await(const n of I){if(n.error){m=n.error,l+=n.error.message?`
6
+
7
+ [error] ${n.error.message}`:"",f="error";break}if(n.model&&(a=n.model),n.usage&&(g=n.usage),n.finishReason&&(f=n.finishReason),n.delta&&(t.startedAt||(t.startedAt=Date.now()),n.delta.startsWith("\0think:")?(h+=n.delta.slice(7),t.reasoning=h):(l+=n.delta,t.content=l),this.emit()),n.toolCall){const d=o.get(n.toolCall.id)??{id:n.toolCall.id,name:n.toolCall.name,arguments:"",raw:""};n.toolCall.name&&(d.name=n.toolCall.name),n.toolCall.arguments&&(d.raw+=n.toolCall.arguments),d.arguments=d.raw,o.set(n.toolCall.id,d),this.emit()}}t.content=l;const p=[];for(const n of o.values()){let d={};if(typeof n.raw=="string"&&n.raw.trim().length>0)try{d=JSON.parse(n.raw)}catch{d={raw:n.raw}}const A=this.config.tools?.find(u=>u.name===n.name);let w,y;if(A?.handler)try{w=await A.handler(d,{conversationId:this.activeId})}catch(u){y=(u instanceof Error?u.message:String(u))||"Tool failed"}else w={ok:!1,message:`No handler registered for tool "${n.name}".`};p.push({id:n.id,name:n.name,arguments:d,status:y?"error":"complete",result:w,error:y})}if(m)throw m;const O=f==="stop"||f==="length"||f==="content_filter",E=p.length>0;return{toolCalls:p,finished:O&&!E,finalUsage:g,model:a}}}function M(i,t){return i.length<=t?i:`${i.slice(0,t-1)}…`}function R(i){const t=i.find(s=>s.role==="user");if(!t)return"New chat";let e=t.content;if(e.startsWith("{"))try{const s=JSON.parse(e);s?.__type==="ai-chat-blocks"&&(e=s.blocks?.map(r=>r.type==="text"?r.text??"":"").filter(Boolean).join(" ")||"Attachment")}catch{}return M(e.replace(/[\n\r]+/g," ").trim(),60)||"New chat"}exports.ChatEngine=z;exports.attachmentIcon=D;exports.clearAllConversations=W;exports.deleteConversation=_;exports.downloadAttachment=U;exports.extractMessageText=J;exports.filesToAttachments=k;exports.listConversations=x;exports.loadConversation=C;exports.saveConversation=b;exports.serializeUserContent=S;exports.summarizeUserMessage=T;exports.trimPersisted=N;
8
+ //# sourceMappingURL=chat-engine-BaFdimfx.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-engine-BaFdimfx.cjs","sources":["../src/lib/attachments.ts","../src/lib/persistence.ts","../src/lib/chat-engine.ts"],"sourcesContent":["import type { AttachmentMeta, ChatConfig, ChatMessage } from \"../types/chat.js\";\nimport { formatBytes, readFileAsDataUrl, readFileAsText, uid } from \"./utils.js\";\nimport { buildAnthropicUserContent } from \"../providers/anthropic.js\";\nimport { buildOpenAIUserContent } from \"../providers/openai.js\";\n\n/**\n * Convert a list of browser File objects into AttachmentMeta records.\n * Large text files (.md/.txt/.csv/.json) are read as text for cheap\n * context-injection. Binary files are stored as data URLs for vision\n * models (subject to the cfg.ui.maxFileSizeMb cap).\n */\nexport async function filesToAttachments(\n files: File[],\n cfg: ChatConfig,\n): Promise<AttachmentMeta[]> {\n const maxBytes = (cfg.ui?.maxFileSizeMb ?? 10) * 1024 * 1024;\n const accepted = new Set<string>(\n cfg.ui?.acceptedFileTypes ?? [\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"application/pdf\",\n \"text/plain\",\n \"text/markdown\",\n \"text/csv\",\n \"application/json\",\n ],\n );\n\n const out: AttachmentMeta[] = [];\n for (const file of files) {\n if (file.size > maxBytes) {\n throw new Error(\n `${file.name} is ${formatBytes(file.size)} — exceeds the ${formatBytes(maxBytes)} limit`,\n );\n }\n if (accepted.size > 0 && !accepted.has(file.type)) {\n throw new Error(`${file.name} has unsupported type ${file.type || \"unknown\"}`);\n }\n const att: AttachmentMeta = {\n id: uid(\"att\"),\n name: file.name,\n mimeType: file.type || \"application/octet-stream\",\n size: file.size,\n };\n if (file.type.startsWith(\"text/\") || file.type === \"application/json\") {\n att.text = await readFileAsText(file);\n } else {\n att.dataUrl = await readFileAsDataUrl(file);\n }\n out.push(att);\n }\n return out;\n}\n\n/**\n * Serialize a (text, attachments) tuple into the structured user content\n * the provider adapters recognize. Returns either a plain string (most\n * turns) or a JSON-encoded marker (`{__type:\"ai-chat-blocks\",blocks:[...]}`).\n *\n * Adapters inspect the marker and expand it into native content blocks.\n */\nexport function serializeUserContent(\n text: string,\n attachments: AttachmentMeta[] | undefined,\n providerKind: ChatConfig[\"provider\"][\"kind\"],\n): string {\n if (!attachments?.length) return text;\n const provider = providerKind;\n if (provider === \"anthropic\") {\n const blocks = buildAnthropicUserContent({ text, attachments });\n if (!blocks.length) return text;\n return JSON.stringify({ __type: \"ai-chat-blocks\", blocks });\n }\n // OpenAI / openai-compatible share the same multimodal format.\n const blocks = buildOpenAIUserContent({ text, attachments });\n if (!blocks.length) return text;\n return JSON.stringify({ __type: \"ai-chat-blocks\", blocks });\n}\n\n/**\n * Download an attachment by triggering a synthetic anchor click.\n * Used for \"Download\" buttons next to each file chip in the UI.\n */\nexport function downloadAttachment(att: AttachmentMeta): void {\n if (typeof document === \"undefined\") return;\n const href = att.dataUrl ?? att.url;\n if (!href) return;\n const a = document.createElement(\"a\");\n a.href = href;\n a.download = att.name;\n a.rel = \"noopener\";\n document.body.appendChild(a);\n a.click();\n a.remove();\n}\n\n/**\n * Friendly preview thumbnail for non-image attachments.\n */\nexport function attachmentIcon(mimeType: string): string {\n if (mimeType.startsWith(\"image/\")) return \"image\";\n if (mimeType.startsWith(\"video/\")) return \"video\";\n if (mimeType.startsWith(\"audio/\")) return \"audio\";\n if (mimeType === \"application/pdf\") return \"pdf\";\n if (mimeType.startsWith(\"text/\") || mimeType === \"application/json\") return \"text\";\n return \"file\";\n}\n\n/**\n * Extract the human-readable text from a message's `content`. When the\n * content is the internal `ai-chat-blocks` JSON marker (used when a turn\n * carries attachments), the text blocks are concatenated and the binary\n * image/file blocks are dropped — so the UI never renders raw JSON. Plain\n * content is returned unchanged.\n */\nexport function extractMessageText(content: string): string {\n if (!content.startsWith(\"{\")) return content;\n try {\n const parsed = JSON.parse(content) as {\n __type?: string;\n blocks?: { type?: string; text?: string }[];\n };\n if (parsed?.__type === \"ai-chat-blocks\" && Array.isArray(parsed.blocks)) {\n return parsed.blocks\n .map((b) => (b.type === \"text\" && typeof b.text === \"string\" ? b.text : \"\"))\n .filter(Boolean)\n .join(\"\\n\")\n .trim();\n }\n } catch {\n /* fall through — not a blocks marker */\n }\n return content;\n}\n\n/**\n * Reconstruct a plain-text representation of a user message:\n * strips the JSON marker, surfaces attachment names, and returns the\n * raw text the user typed. Used by the on-screen header.\n */\nexport function summarizeUserMessage(msg: ChatMessage): string {\n let text = msg.content;\n if (text.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(text);\n if (parsed?.__type === \"ai-chat-blocks\" && Array.isArray(parsed.blocks)) {\n text = parsed.blocks\n .map((b: { type?: string; text?: string }) =>\n b?.type === \"text\" && typeof b.text === \"string\" ? b.text : \"\",\n )\n .filter(Boolean)\n .join(\"\\n\");\n }\n } catch {\n // fall through\n }\n }\n const names = (msg.attachments ?? []).map((a) => a.name).join(\", \");\n return names ? `${text}\\n\\nšŸ“Ž ${names}` : text;\n}","import type { ChatMessage } from \"../types/chat.js\";\n\nconst STORAGE_PREFIX = \"ai-schadcn-chat:conversation:\";\n\n/**\n * Per-conversation persistence using `localStorage` by default.\n * The package intentionally avoids a hard dep on Indexeddb — the\n * persisted payload is small (messages + metadata) and localStorage\n * is available in every React 18/19 environment we target.\n *\n * For SSR safety, all reads/writes are guarded behind a typeof window check.\n */\nexport interface PersistedConversation {\n id: string;\n title: string;\n messages: ChatMessage[];\n updatedAt: number;\n config?: { model?: string; temperature?: number };\n}\n\nexport function loadConversation(id: string): PersistedConversation | null {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(STORAGE_PREFIX + id);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as PersistedConversation;\n if (!parsed?.messages) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nexport function saveConversation(conv: PersistedConversation): void {\n if (typeof window === \"undefined\") return;\n try {\n // Strip tool-call `result` payloads that may contain Functions or DOM nodes —\n // they're not JSON-serializable and would crash the save.\n const safeMessages = conv.messages.map((m) => ({\n ...m,\n toolCalls: (m.toolCalls ?? []).map((tc) => ({\n ...tc,\n result: tc.result instanceof Object ? safeClone(tc.result) : tc.result,\n })),\n }));\n window.localStorage.setItem(\n STORAGE_PREFIX + conv.id,\n JSON.stringify({ ...conv, messages: safeMessages }),\n );\n } catch {\n // quota / privacy mode — silently skip; the chat still works in memory.\n }\n}\n\nexport function listConversations(): PersistedConversation[] {\n if (typeof window === \"undefined\") return [];\n const out: PersistedConversation[] = [];\n for (let i = 0; i < window.localStorage.length; i++) {\n const key = window.localStorage.key(i);\n if (!key || !key.startsWith(STORAGE_PREFIX)) continue;\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) continue;\n const parsed = JSON.parse(raw) as PersistedConversation;\n if (parsed?.id) out.push(parsed);\n } catch {\n // skip corrupted entries\n }\n }\n return out.sort((a, b) => b.updatedAt - a.updatedAt);\n}\n\nexport function deleteConversation(id: string): void {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.removeItem(STORAGE_PREFIX + id);\n } catch {\n // ignore\n }\n}\n\nexport function clearAllConversations(): void {\n if (typeof window === \"undefined\") return;\n try {\n const toRemove: string[] = [];\n for (let i = 0; i < window.localStorage.length; i++) {\n const key = window.localStorage.key(i);\n if (key && key.startsWith(STORAGE_PREFIX)) toRemove.push(key);\n }\n toRemove.forEach((k) => window.localStorage.removeItem(k));\n } catch {\n // ignore\n }\n}\n\nfunction safeClone(value: unknown): unknown {\n try {\n return JSON.parse(JSON.stringify(value));\n } catch {\n return undefined;\n }\n}\n\n/**\n * Trim persisted messages to a sane size to prevent runaway storage use.\n * 1000 messages Ɨ ~5KB each = 5MB, well within localStorage limits.\n */\nexport function trimPersisted(conv: PersistedConversation, max = 1000): PersistedConversation {\n if (conv.messages.length <= max) return conv;\n return {\n ...conv,\n messages: conv.messages.slice(-max),\n };\n}","import type {\n AttachmentMeta,\n ChatConfig,\n ChatError,\n ChatMessage,\n ChatResponseChunk,\n ConversationMeta,\n ProviderAdapter,\n Role,\n ToolCallRecord,\n ToolDefinition,\n} from \"../types/chat.js\";\nimport { createProvider } from \"../providers/index.js\";\nimport {\n estimateTokens,\n sleep,\n toError,\n trimMessagesByTokens,\n uid,\n} from \"./utils.js\";\nimport { filesToAttachments, serializeUserContent } from \"./attachments.js\";\nimport {\n listConversations as listPersistedConversations,\n loadConversation,\n saveConversation,\n trimPersisted,\n} from \"./persistence.js\";\nimport { toChatError } from \"../providers/base.js\";\n\n/**\n * Public, framework-agnostic chat engine. Wraps a streaming ProviderAdapter\n * and exposes a subscribe-based API for any UI framework (React/Vue/Solid).\n *\n * - Conversations have a stable id; switching ids swaps the active buffer\n * - messages stream incrementally; partial content is observable mid-flight\n * - tool calls are dispatched through user-supplied handlers when present\n * - persistence is opt-in via `cfg.persistKey`\n */\nexport class ChatEngine {\n private config: ChatConfig;\n private provider: ProviderAdapter;\n private listeners = new Set<() => void>();\n private conversations = new Map<string, ConversationState>();\n private activeId: string;\n\n constructor(config: ChatConfig, initialConversationId?: string) {\n this.config = config;\n this.provider = createProvider(config);\n this.activeId = initialConversationId ?? uid(\"conv\");\n if (this.config.persistKey) {\n // Restore active conversation if it was persisted with the same key prefix.\n // For now: restore the active id if it exists.\n const persisted = loadConversation(this.activeId);\n if (persisted) {\n this.conversations.set(this.activeId, {\n id: this.activeId,\n messages: persisted.messages,\n });\n } else {\n this.conversations.set(this.activeId, { id: this.activeId, messages: [] });\n }\n } else {\n this.conversations.set(this.activeId, { id: this.activeId, messages: [] });\n }\n }\n\n public getConfig(): ChatConfig {\n return this.config;\n }\n\n public updateConfig(partial: Partial<ChatConfig>): void;\n public updateConfig(updater: (current: ChatConfig) => Partial<ChatConfig>): void;\n public updateConfig(\n arg: Partial<ChatConfig> | ((current: ChatConfig) => Partial<ChatConfig>),\n ): void {\n const partial =\n typeof arg === \"function\" ? arg(this.config) : arg;\n this.config = { ...this.config, ...partial };\n if (partial.provider || partial.model) {\n this.provider = createProvider(this.config);\n }\n this.emit();\n }\n\n public getActiveConversationId(): string {\n return this.activeId;\n }\n\n /**\n * Return the count of in-flight tool calls for the active conversation.\n *\n * Default returns 0 because the engine does not yet expose tool-call\n * activity tracking publicly — this method is the hook that any future\n * tool-call plumbing can fill in. Consumers reading this in their UI\n * (e.g. a header badge behind `ui.showToolCalls`) will get a sensible\n * default until the engine wires real tool-call state.\n */\n public getActiveToolCallCount(): number {\n return 0;\n }\n\n public setActiveConversationId(id: string): void {\n if (!this.conversations.has(id)) {\n // Hydrate from persistence when switching to a stored conversation\n // that isn't in memory yet; otherwise start a fresh empty one.\n const persisted = this.config.persistKey ? loadConversation(id) : null;\n this.conversations.set(id, { id, messages: persisted?.messages ?? [] });\n }\n this.activeId = id;\n this.emit();\n }\n\n public listConversationIds(): string[] {\n return Array.from(this.conversations.keys());\n }\n\n /**\n * Rich conversation list for history menus: a title (first user message),\n * last-updated timestamp, and message count. Merges in-memory conversations\n * with persisted ones (persisted wins for title/timestamp). Sorted newest\n * first; the active conversation is always present.\n */\n public listConversationsMeta(): ConversationMeta[] {\n const metas = new Map<string, ConversationMeta>();\n for (const conv of this.conversations.values()) {\n metas.set(conv.id, {\n id: conv.id,\n title: deriveTitle(conv.messages),\n updatedAt: 0,\n messageCount: conv.messages.length,\n });\n }\n if (this.config.persistKey) {\n for (const p of listPersistedConversations()) {\n const existing = metas.get(p.id);\n metas.set(p.id, {\n id: p.id,\n title: p.title || existing?.title || \"New chat\",\n updatedAt: p.updatedAt,\n messageCount: p.messages.length,\n });\n }\n }\n return Array.from(metas.values()).sort((a, b) => b.updatedAt - a.updatedAt);\n }\n\n public getMessages(): ChatMessage[] {\n return this.conversations.get(this.activeId)?.messages ?? [];\n }\n\n /**\n * Returns a shallow copy of the active conversation's messages,\n * cached between `emit()` calls and only rebuilt when the engine\n * signals a real change.\n *\n * Why this exists: React bindings consume this via `useSyncExternalStore`,\n * which bails out when the snapshot reference is identical to the\n * previous one (that's the contract). Because we mutate `messages`\n * in place (`push`, etc.) the underlying array reference never changes\n * — so the user bubble would never render. The fix is to cache a fresh\n * copy between `emit()` calls and invalidate the cache when something\n * actually changes. Returning a NEW array on every call would itself\n * infinite-loop the snapshot.\n */\n private snapshotCache: ChatMessage[] = [];\n private snapshotDirty = true;\n\n public getMessagesSnapshot(): ChatMessage[] {\n if (this.snapshotDirty) {\n const msgs = this.conversations.get(this.activeId)?.messages;\n this.snapshotCache = msgs ? [...msgs] : [];\n this.snapshotDirty = false;\n }\n return this.snapshotCache;\n }\n\n public subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private emit(): void {\n this.snapshotDirty = true;\n for (const l of this.listeners) l();\n }\n\n private persist(): void {\n if (!this.config.persistKey) return;\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n const first = conv.messages.find((m) => m.role === \"user\");\n const title = first ? truncate(first.content.replace(/[\\n\\r]+/g, \" \"), 60) : \"New chat\";\n saveConversation(\n trimPersisted({\n id: conv.id,\n title,\n messages: conv.messages,\n updatedAt: Date.now(),\n config: { model: this.config.model.id, temperature: this.config.temperature },\n }),\n );\n }\n\n /**\n * Public: stop the currently streaming response, if any.\n */\n public abort(): void {\n this.provider.abort();\n }\n\n /**\n * Public: clear the active conversation.\n */\n public clear(): void {\n this.conversations.set(this.activeId, { id: this.activeId, messages: [] });\n this.persist();\n this.emit();\n }\n\n /**\n * Public: switch the active conversation (creates it if missing).\n */\n public newConversation(): string {\n const id = uid(\"conv\");\n this.conversations.set(id, { id, messages: [] });\n this.activeId = id;\n this.persist();\n this.emit();\n return id;\n }\n\n /** Public: remove a single message from the active conversation. */\n public deleteMessage(messageId: string): void {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n conv.messages = conv.messages.filter((m) => m.id !== messageId);\n this.persist();\n this.emit();\n }\n\n public deleteConversation(id: string): void {\n this.conversations.delete(id);\n if (this.activeId === id) {\n this.activeId = this.conversations.keys().next().value ?? uid(\"conv\");\n this.conversations.set(this.activeId, { id: this.activeId, messages: [] });\n }\n this.persist();\n this.emit();\n }\n\n /**\n * Public: edit a previous user message and re-run the assistant from there.\n * Truncates all messages after `messageId` and triggers a new completion.\n */\n public async editAndResend(messageId: string, newContent: string): Promise<void> {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n const idx = conv.messages.findIndex((m) => m.id === messageId);\n if (idx < 0 || conv.messages[idx].role !== \"user\") return;\n conv.messages[idx] = { ...conv.messages[idx], content: newContent, status: \"complete\" };\n conv.messages = conv.messages.slice(0, idx + 1);\n this.emit();\n this.persist();\n await this.runCompletion();\n }\n\n /**\n * Public: re-run the assistant for the most recent user turn.\n */\n public async regenerate(): Promise<void> {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n // Drop the last assistant message (or error).\n for (let i = conv.messages.length - 1; i >= 0; i--) {\n if (conv.messages[i].role === \"assistant\") {\n conv.messages.splice(i, 1);\n break;\n }\n if (conv.messages[i].role === \"user\") break;\n }\n this.emit();\n this.persist();\n await this.runCompletion();\n }\n\n /**\n * Public: send a user message (optionally with attachments) and stream the\n * assistant reply.\n */\n public async sendUserMessage(text: string, attachments?: AttachmentMeta[]): Promise<void> {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n if (!text.trim() && !attachments?.length) return;\n\n const content = serializeUserContent(\n text,\n attachments,\n this.config.model.provider ?? this.config.provider.kind,\n );\n\n const userMsg: ChatMessage = {\n id: uid(\"msg\"),\n role: \"user\",\n content,\n attachments,\n createdAt: Date.now(),\n status: \"complete\",\n };\n conv.messages.push(userMsg);\n this.emit();\n this.persist();\n\n await this.runCompletion();\n }\n\n /**\n * Public: send files (browser File objects) directly. Convenience wrapper\n * around sendUserMessage + filesToAttachments.\n */\n public async sendFiles(text: string, files: File[]): Promise<void> {\n const attachments = await filesToAttachments(files, this.config);\n return this.sendUserMessage(text, attachments);\n }\n\n /**\n * Internal: stream one assistant turn. Handles tool calls in a loop\n * (max 8 iterations to prevent infinite loops).\n */\n private async runCompletion(): Promise<void> {\n const conv = this.conversations.get(this.activeId);\n if (!conv) return;\n\n const assistantId = uid(\"msg\");\n let assistantMsg: ChatMessage = {\n id: assistantId,\n role: \"assistant\",\n content: \"\",\n createdAt: Date.now(),\n status: \"streaming\",\n };\n conv.messages.push(assistantMsg);\n\n const maxIter = 8;\n for (let iter = 0; iter < maxIter; iter++) {\n try {\n const { toolCalls, finished, finalUsage, model } =\n await this.streamAssistantTurn(assistantMsg, conv);\n\n if (toolCalls.length > 0 && this.config.tools?.length) {\n assistantMsg.status = \"complete\";\n // Materialize tool-call records on the assistant message.\n assistantMsg.toolCalls = toolCalls.map((tc) => ({\n id: tc.id,\n name: tc.name,\n arguments: tc.arguments,\n status: tc.error ? (\"error\" as const) : (\"complete\" as const),\n result: tc.result,\n error: tc.error,\n }));\n\n // Execute each tool and append tool messages.\n for (const tc of toolCalls) {\n const def = this.config.tools?.find((t) => t.name === tc.name);\n const toolMsg: ChatMessage = {\n id: uid(\"msg\"),\n role: \"tool\",\n content: tc.error\n ? `Error: ${tc.error}`\n : typeof tc.result === \"string\"\n ? tc.result\n : JSON.stringify(tc.result ?? null),\n createdAt: Date.now(),\n status: \"complete\",\n toolCallId: tc.id,\n name: tc.name,\n };\n conv.messages.push(toolMsg);\n this.emit();\n this.persist();\n // If a tool produced a result we want the assistant to react to,\n // we let the next iteration append another assistant message.\n // To support that, push a fresh assistant placeholder so the loop\n // continues the conversation. We only do this when the tool\n // returned a usable payload (no error) AND there are still\n // iterations left.\n if (!tc.error && iter < maxIter - 1) {\n assistantId;\n assistantMsg = {\n id: uid(\"msg\"),\n role: \"assistant\",\n content: \"\",\n createdAt: Date.now(),\n status: \"streaming\",\n };\n conv.messages.push(assistantMsg);\n }\n }\n if (!finished) continue;\n break;\n }\n\n if (finished) {\n assistantMsg.status = \"complete\";\n assistantMsg.completedAt = Date.now();\n if (finalUsage) assistantMsg.usage = finalUsage;\n if (model) assistantMsg.model = model;\n break;\n }\n } catch (err) {\n const ce = toChatError(err);\n assistantMsg.status = \"error\";\n assistantMsg.error = ce;\n this.config.onError?.(ce, { conversationId: this.activeId });\n this.emit();\n this.persist();\n return;\n }\n }\n\n this.config.onResponse?.(assistantMsg, { conversationId: this.activeId });\n this.emit();\n this.persist();\n }\n\n /**\n * Streams one assistant message. Returns:\n * - toolCalls: tool_use / tool_calls chunks collected during the stream\n * - finished: true if the API emitted a non-tool finish reason\n * - finalUsage / model: usage & model metadata when available\n */\n private async streamAssistantTurn(\n assistantMsg: ChatMessage,\n conv: ConversationState,\n ): Promise<{\n toolCalls: Array<ToolCallRecord & { arguments: Record<string, unknown> }>;\n finished: boolean;\n finalUsage?: ChatMessage[\"usage\"];\n model?: string;\n }> {\n const maxTokens = this.config.maxContextTokens ?? 32_000;\n const messages = trimMessagesByTokens(conv.messages, maxTokens);\n\n const collected = new Map<\n string,\n { id: string; name: string; arguments: Record<string, unknown> | string; raw: string }\n >();\n let buffer = \"\";\n let reasoningBuffer = \"\";\n let finishReason: ChatResponseChunk[\"finishReason\"];\n let finalUsage: ChatMessage[\"usage\"];\n let finalModel: string | undefined;\n\n const stream = this.provider.stream(\n {\n messages,\n signal: undefined,\n },\n this.config,\n );\n\n let lastError: ChatError | undefined;\n\n for await (const chunk of stream) {\n if (chunk.error) {\n lastError = chunk.error;\n buffer += chunk.error.message ? `\\n\\n[error] ${chunk.error.message}` : \"\";\n finishReason = \"error\";\n break;\n }\n if (chunk.model) finalModel = chunk.model;\n if (chunk.usage) finalUsage = chunk.usage;\n if (chunk.finishReason) finishReason = chunk.finishReason;\n if (chunk.delta) {\n if (!assistantMsg.startedAt) assistantMsg.startedAt = Date.now();\n if (chunk.delta.startsWith(\"\\u0000think:\")) {\n reasoningBuffer += chunk.delta.slice(\"\\u0000think:\".length);\n assistantMsg.reasoning = reasoningBuffer;\n } else {\n buffer += chunk.delta;\n assistantMsg.content = buffer;\n }\n this.emit();\n }\n if (chunk.toolCall) {\n const tc = collected.get(chunk.toolCall.id) ?? {\n id: chunk.toolCall.id,\n name: chunk.toolCall.name,\n arguments: \"\",\n raw: \"\",\n };\n if (chunk.toolCall.name) tc.name = chunk.toolCall.name;\n if (chunk.toolCall.arguments) tc.raw += chunk.toolCall.arguments;\n tc.arguments = tc.raw;\n collected.set(chunk.toolCall.id, tc);\n this.emit();\n }\n }\n\n // Persist incremental buffer onto the assistant message.\n assistantMsg.content = buffer;\n\n // Materialize tool-call records.\n const toolCalls: Array<ToolCallRecord & { arguments: Record<string, unknown> }> = [];\n for (const tc of collected.values()) {\n let parsedArgs: Record<string, unknown> = {};\n if (typeof tc.raw === \"string\" && tc.raw.trim().length > 0) {\n try {\n parsedArgs = JSON.parse(tc.raw);\n } catch {\n parsedArgs = { raw: tc.raw };\n }\n }\n const def = this.config.tools?.find((t) => t.name === tc.name);\n let result: unknown;\n let error: string | undefined;\n if (def?.handler) {\n try {\n const handlerResult = await def.handler(parsedArgs, { conversationId: this.activeId });\n result = handlerResult;\n } catch (err) {\n error = (err instanceof Error ? err.message : String(err)) || \"Tool failed\";\n }\n } else {\n // No handler — surface a soft warning so the model can still react.\n result = { ok: false, message: `No handler registered for tool \"${tc.name}\".` };\n }\n toolCalls.push({\n id: tc.id,\n name: tc.name,\n arguments: parsedArgs,\n status: error ? (\"error\" as const) : (\"complete\" as const),\n result,\n error,\n });\n }\n\n if (lastError) throw lastError;\n\n const finished = finishReason === \"stop\" || finishReason === \"length\" || finishReason === \"content_filter\";\n const hasToolCalls = toolCalls.length > 0;\n return { toolCalls, finished: finished && !hasToolCalls, finalUsage, model: finalModel };\n }\n}\n\ninterface ConversationState {\n id: string;\n messages: ChatMessage[];\n}\n\nfunction truncate(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max - 1)}…`;\n}\n\n/** Derive a human title from the first user message (ignores JSON markers). */\nfunction deriveTitle(messages: ChatMessage[]): string {\n const first = messages.find((m) => m.role === \"user\");\n if (!first) return \"New chat\";\n let content = first.content;\n if (content.startsWith(\"{\")) {\n try {\n const parsed = JSON.parse(content) as {\n __type?: string;\n blocks?: { type?: string; text?: string }[];\n };\n if (parsed?.__type === \"ai-chat-blocks\") {\n content =\n parsed.blocks\n ?.map((b) => (b.type === \"text\" ? (b.text ?? \"\") : \"\"))\n .filter(Boolean)\n .join(\" \") || \"Attachment\";\n }\n } catch {\n /* keep raw */\n }\n }\n return truncate(content.replace(/[\\n\\r]+/g, \" \").trim(), 60) || \"New chat\";\n}"],"names":["filesToAttachments","files","cfg","maxBytes","accepted","out","file","formatBytes","att","uid","readFileAsText","readFileAsDataUrl","serializeUserContent","text","attachments","providerKind","blocks","buildAnthropicUserContent","buildOpenAIUserContent","downloadAttachment","href","a","attachmentIcon","mimeType","extractMessageText","content","parsed","b","summarizeUserMessage","msg","names","STORAGE_PREFIX","loadConversation","id","raw","saveConversation","conv","safeMessages","m","tc","safeClone","listConversations","i","key","deleteConversation","clearAllConversations","toRemove","k","value","trimPersisted","max","ChatEngine","config","initialConversationId","createProvider","persisted","arg","partial","metas","deriveTitle","p","listPersistedConversations","existing","msgs","listener","l","first","title","truncate","messageId","newContent","idx","userMsg","assistantId","assistantMsg","maxIter","iter","toolCalls","finished","finalUsage","model","def","t","toolMsg","err","ce","toChatError","maxTokens","messages","trimMessagesByTokens","collected","buffer","reasoningBuffer","finishReason","finalModel","stream","lastError","chunk","parsedArgs","result","error","hasToolCalls"],"mappings":"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"}