@coral-ai/chat 0.3.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/LICENSE +21 -0
- package/README.md +28 -0
- package/dist/index.d.ts +252 -0
- package/dist/index.js +1537 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eunsoo Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @coral-ai/chat
|
|
2
|
+
|
|
3
|
+
Chat surface for Coral UI: `<Chat>`, `<Chat.Canvas>`, `<Chat.Messages>`, `<Chat.Input>`, agent registration, and streaming helpers.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @coral-ai/chat @coral-ai/markdown @coral-ai/bundleicons @fluentui/react-components @fluentui/react-icons react react-dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies do not install transitively. Install all of the above explicitly.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { Chat, createHandler } from "@coral-ai/chat";
|
|
17
|
+
|
|
18
|
+
const myHandler = createHandler(async ({ message }) => {
|
|
19
|
+
return { content: `You said: ${message}` };
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
<Chat
|
|
23
|
+
agents={{ default: { handler: myHandler } }}
|
|
24
|
+
agent="default"
|
|
25
|
+
/>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
See [coral-ai](https://github.com/mochimilk/coral-ai) for the full project.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { ChatFontSize, ChatFontFamily } from '@coral-ai/markdown';
|
|
3
|
+
|
|
4
|
+
/** Agent registration — keyed by agent name; `"default"` is the catch-all. */
|
|
5
|
+
type AgentConfig = {
|
|
6
|
+
handler: AgentHandler;
|
|
7
|
+
};
|
|
8
|
+
interface ChatCanvasProps {
|
|
9
|
+
/** Name of the agent from the `agents` map. Falls back to `"default"`. */
|
|
10
|
+
agent?: string;
|
|
11
|
+
/** Named agent handlers keyed by agent name. Use `"default"` for the catch-all. */
|
|
12
|
+
agents?: Record<string, AgentConfig>;
|
|
13
|
+
/** Stable conversation identifier, passed back in onMessageComplete. */
|
|
14
|
+
conversationId?: string;
|
|
15
|
+
/** Pre-load conversation history. */
|
|
16
|
+
initialMessages?: ChatMessage[];
|
|
17
|
+
/** System prompt prepended to every request (not shown in UI). */
|
|
18
|
+
systemPrompt?: string;
|
|
19
|
+
/** Called when the user sends a message. Return a string, async iterable, or ChatResponse. */
|
|
20
|
+
onSendMessage?: (input: string, history: ChatMessage[], attachments: File[]) => ReturnType<AgentHandler>;
|
|
21
|
+
/** Called after every completed assistant response. Use to persist messages. */
|
|
22
|
+
onMessageComplete?: (messages: ChatMessage[], conversationId?: string) => void;
|
|
23
|
+
/** Called on every message-state update (user append, streaming chunk, final). Use for live persistence. */
|
|
24
|
+
onMessagesChange?: (messages: ChatMessage[], conversationId?: string) => void;
|
|
25
|
+
/** Called when an error occurs during send. */
|
|
26
|
+
onError?: (err: unknown) => void;
|
|
27
|
+
/** Automatically sends this message once on mount. */
|
|
28
|
+
autoSendMessage?: string;
|
|
29
|
+
/** Override the thinking indicator text during generation. */
|
|
30
|
+
thinkingStatus?: string;
|
|
31
|
+
/** Called when the user clicks Stop. Use to abort your fetch/API call. */
|
|
32
|
+
onStop?: () => void;
|
|
33
|
+
children?: ReactNode;
|
|
34
|
+
}
|
|
35
|
+
declare const ChatCanvas: React.FC<ChatCanvasProps>;
|
|
36
|
+
|
|
37
|
+
type ChatMessage = {
|
|
38
|
+
/** Stable identifier — generated automatically by Chat.Canvas. Used as React key. */
|
|
39
|
+
id?: string;
|
|
40
|
+
role: "user" | "assistant" | "system";
|
|
41
|
+
content: string;
|
|
42
|
+
/** Identifies the message type for custom renderer lookup via the `components` prop. */
|
|
43
|
+
type?: string;
|
|
44
|
+
/** Arbitrary data passed to the custom renderer for this message. */
|
|
45
|
+
metadata?: Record<string, unknown>;
|
|
46
|
+
};
|
|
47
|
+
/** A single chunk yielded by a streaming onSendMessage. */
|
|
48
|
+
type ChatChunk = string | {
|
|
49
|
+
type: "reasoning";
|
|
50
|
+
content: string;
|
|
51
|
+
} | {
|
|
52
|
+
type: "agent";
|
|
53
|
+
name: string;
|
|
54
|
+
};
|
|
55
|
+
type ChatResponse = string | AsyncIterable<ChatChunk> | {
|
|
56
|
+
content: string;
|
|
57
|
+
type?: string;
|
|
58
|
+
metadata?: Record<string, unknown>;
|
|
59
|
+
};
|
|
60
|
+
/** The typed handler signature engineers implement. */
|
|
61
|
+
type ChatAttachment = {
|
|
62
|
+
id: string;
|
|
63
|
+
file: File;
|
|
64
|
+
/** Data URL — populated for image/* files only. */
|
|
65
|
+
preview?: string;
|
|
66
|
+
};
|
|
67
|
+
type AgentHandler = (input: string, history: ChatMessage[], attachments: File[]) => ChatResponse | Promise<ChatResponse>;
|
|
68
|
+
/** Props passed to a custom message renderer. */
|
|
69
|
+
type ChatMessageRendererProps = {
|
|
70
|
+
message: ChatMessage;
|
|
71
|
+
/** Sends a message back into the chat as the user. */
|
|
72
|
+
onReply: (content: string) => void;
|
|
73
|
+
};
|
|
74
|
+
type ChatComponents = Record<string, React.FC<ChatMessageRendererProps>>;
|
|
75
|
+
interface ChatProps {
|
|
76
|
+
agent?: string;
|
|
77
|
+
/** Named agent handlers keyed by agent name. Use `"default"` for the catch-all. */
|
|
78
|
+
agents?: Record<string, AgentConfig>;
|
|
79
|
+
conversationId?: string;
|
|
80
|
+
initialMessages?: ChatMessage[];
|
|
81
|
+
systemPrompt?: string;
|
|
82
|
+
onSendMessage?: AgentHandler;
|
|
83
|
+
onMessageComplete?: (messages: ChatMessage[], conversationId?: string) => void;
|
|
84
|
+
/** Fires on every message-state update (user append, streaming chunk, final). Use for live persistence. */
|
|
85
|
+
onMessagesChange?: (messages: ChatMessage[], conversationId?: string) => void;
|
|
86
|
+
onError?: (err: unknown) => void;
|
|
87
|
+
autoSendMessage?: string;
|
|
88
|
+
onFeedback?: (messageIndex: number, rating: "up" | "down") => void;
|
|
89
|
+
onCopy?: (content: string) => void;
|
|
90
|
+
onNavigate?: (path: string) => void;
|
|
91
|
+
components?: ChatComponents;
|
|
92
|
+
thinkingStatus?: string;
|
|
93
|
+
onStop?: () => void;
|
|
94
|
+
/** Placeholder text for the chat input. @default "Type a message..." */
|
|
95
|
+
placeholder?: string;
|
|
96
|
+
/** Disables the entire input. */
|
|
97
|
+
disabled?: boolean;
|
|
98
|
+
/** Slot rendered at the left of the input toolbar. */
|
|
99
|
+
actionsStart?: React.ReactNode;
|
|
100
|
+
/** Slot rendered at the right of the input toolbar, just before Send/Stop. */
|
|
101
|
+
actionsEnd?: React.ReactNode;
|
|
102
|
+
/** Shown 24px above the input while messages is empty. */
|
|
103
|
+
emptyState?: React.ReactNode;
|
|
104
|
+
/**
|
|
105
|
+
* Clickable suggestion chips rendered below the input while the chat is empty.
|
|
106
|
+
* Clicking a chip sends that string as the user's message. Chips fade out once
|
|
107
|
+
* any message has been sent.
|
|
108
|
+
*/
|
|
109
|
+
promptStarters?: string[];
|
|
110
|
+
/**
|
|
111
|
+
* Pass `null` to disable the chat's built-in surface motion (input slide to
|
|
112
|
+
* bottom + prompt-starter fade). Omit / leave undefined for default motion.
|
|
113
|
+
*/
|
|
114
|
+
surfaceMotion?: null;
|
|
115
|
+
/** Font size for assistant message markdown. */
|
|
116
|
+
fontSize?: ChatFontSize;
|
|
117
|
+
/** Font family for assistant message markdown. */
|
|
118
|
+
fontFamily?: ChatFontFamily;
|
|
119
|
+
}
|
|
120
|
+
declare const Chat$1: React.FC<ChatProps>;
|
|
121
|
+
|
|
122
|
+
interface ChatMessagesProps {
|
|
123
|
+
/**
|
|
124
|
+
* Custom renderers keyed by message type.
|
|
125
|
+
* Falls back to default markdown rendering if no match.
|
|
126
|
+
*/
|
|
127
|
+
components?: ChatComponents;
|
|
128
|
+
/** Called when the user clicks thumbs-up on an assistant message. */
|
|
129
|
+
onFeedback?: (messageIndex: number, rating: "up" | "down") => void;
|
|
130
|
+
/** Called when the user copies an assistant message. */
|
|
131
|
+
onCopy?: (content: string) => void;
|
|
132
|
+
/** Called when the user clicks an internal link (href starts with "/"). */
|
|
133
|
+
onNavigate?: (path: string) => void;
|
|
134
|
+
/** Font size for assistant message markdown. */
|
|
135
|
+
fontSize?: ChatFontSize;
|
|
136
|
+
/** Font family for assistant message markdown. */
|
|
137
|
+
fontFamily?: ChatFontFamily;
|
|
138
|
+
}
|
|
139
|
+
declare const ChatMessages: React.FC<ChatMessagesProps>;
|
|
140
|
+
|
|
141
|
+
interface ChatInputProps {
|
|
142
|
+
/** Current input value. Optional inside Chat.Canvas — auto-wired from context. */
|
|
143
|
+
value?: string;
|
|
144
|
+
/** Change handler. Optional inside Chat.Canvas — auto-wired from context. */
|
|
145
|
+
onChange?: (val: string) => void;
|
|
146
|
+
/** Called on Enter. Optional inside Chat.Canvas — auto-wired to sendMessage. */
|
|
147
|
+
onEnter?: () => void;
|
|
148
|
+
placeholder?: string;
|
|
149
|
+
/** Disabled state. */
|
|
150
|
+
disabled?: boolean;
|
|
151
|
+
style?: React.CSSProperties;
|
|
152
|
+
/** Slot rendered at the left of the bottom toolbar. */
|
|
153
|
+
actionsStart?: React.ReactNode;
|
|
154
|
+
/** Slot rendered at the right of the bottom toolbar, just before Send/Stop. */
|
|
155
|
+
actionsEnd?: React.ReactNode;
|
|
156
|
+
/** Override attachments when used outside a Chat.Canvas. */
|
|
157
|
+
attachments?: ChatAttachment[];
|
|
158
|
+
/** Override remove handler when used outside a Chat.Canvas. */
|
|
159
|
+
removeAttachment?: (id: string) => void;
|
|
160
|
+
}
|
|
161
|
+
declare const ChatInput: React.ForwardRefExoticComponent<ChatInputProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
162
|
+
|
|
163
|
+
type ErrorCategory = "auth_billing" | "rate_limit" | "overloaded" | "bad_input" | "not_found" | "server_error" | "unknown";
|
|
164
|
+
interface NormalizedError {
|
|
165
|
+
category: ErrorCategory;
|
|
166
|
+
message: string;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Normalizes raw errors from Anthropic, OpenAI, and Google Gemini into a
|
|
170
|
+
* common category + user-friendly message. Safe to call with any unknown value.
|
|
171
|
+
*/
|
|
172
|
+
declare function normalizeChatError$1(err: unknown): NormalizedError;
|
|
173
|
+
interface ChatErrorProps {
|
|
174
|
+
/** Raw error thrown by your onSendMessage handler — any provider shape works. */
|
|
175
|
+
error: unknown;
|
|
176
|
+
/** Optional retry callback shown as a button. */
|
|
177
|
+
onRetry?: () => void;
|
|
178
|
+
}
|
|
179
|
+
declare const ChatError: React.FC<ChatErrorProps>;
|
|
180
|
+
|
|
181
|
+
interface ChatCalloutProps {
|
|
182
|
+
message: string;
|
|
183
|
+
onRetry?: () => void;
|
|
184
|
+
className?: string;
|
|
185
|
+
}
|
|
186
|
+
declare const ChatCallout: React.FC<ChatCalloutProps>;
|
|
187
|
+
|
|
188
|
+
type CreateHandlerOptions = {
|
|
189
|
+
/** Your backend endpoint. Receives POST { input, history, context }. */
|
|
190
|
+
endpoint: string;
|
|
191
|
+
/** Additional headers (e.g. Authorization). */
|
|
192
|
+
headers?: HeadersInit;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Wraps a fetch endpoint into an AgentHandler.
|
|
196
|
+
* The endpoint should stream newline-delimited responses where each line is either:
|
|
197
|
+
* - A plain text chunk (yielded as a string)
|
|
198
|
+
* - A JSON-encoded ChatChunk object (e.g. `{"type":"reasoning","content":"..."}`)
|
|
199
|
+
* - An SSE `data: ...` line (the `data: ` prefix is stripped automatically)
|
|
200
|
+
* - `[DONE]` to signal end-of-stream
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* // main.tsx
|
|
204
|
+
* <CoralProvider agents={{ default: { handler: createHandler({ endpoint: '/api/chat' }) } }}>
|
|
205
|
+
*/
|
|
206
|
+
declare function createHandler$1({ endpoint, headers }: CreateHandlerOptions): AgentHandler;
|
|
207
|
+
|
|
208
|
+
interface ChatContextValue {
|
|
209
|
+
sendMessage: (overrideInput?: string) => Promise<void>;
|
|
210
|
+
scrollToBottom: () => void;
|
|
211
|
+
/** Pre-binds navigator.clipboard.writeText + the onCopy prop callback. */
|
|
212
|
+
handleCopy: (content: string) => void;
|
|
213
|
+
messages: ChatMessage[];
|
|
214
|
+
setMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>;
|
|
215
|
+
input: string;
|
|
216
|
+
setInput: React.Dispatch<React.SetStateAction<string>>;
|
|
217
|
+
isTyping: boolean;
|
|
218
|
+
isRetrying: boolean;
|
|
219
|
+
isInterrupted: boolean;
|
|
220
|
+
streamingReasoning: string;
|
|
221
|
+
streamingAgentName?: string;
|
|
222
|
+
/** Forwarded from Chat.Canvas thinkingStatus prop so Chat.Messages can read it. */
|
|
223
|
+
thinkingStatus?: string;
|
|
224
|
+
chatError: unknown | null;
|
|
225
|
+
showScrollButton: boolean;
|
|
226
|
+
/** Called by Chat.Input to report its measured height so Canvas positions the scroll button. */
|
|
227
|
+
setInputHeight: (height: number) => void;
|
|
228
|
+
messagesContainerRef: React.RefObject<HTMLDivElement | null>;
|
|
229
|
+
/** Called by Chat.Messages on mount/unmount so Canvas can attach scroll listener lazily. */
|
|
230
|
+
registerMessagesContainer: (el: HTMLDivElement | null) => void;
|
|
231
|
+
bottomRef: React.RefObject<HTMLDivElement | null>;
|
|
232
|
+
lastUserInputRef: React.MutableRefObject<string>;
|
|
233
|
+
hasEverSentRef: React.MutableRefObject<boolean>;
|
|
234
|
+
shouldStopRef: React.MutableRefObject<boolean>;
|
|
235
|
+
attachments: ChatAttachment[];
|
|
236
|
+
addAttachments: (files: File[]) => void;
|
|
237
|
+
removeAttachment: (id: string) => void;
|
|
238
|
+
onStop?: () => void;
|
|
239
|
+
}
|
|
240
|
+
declare function useChatContext(): ChatContextValue;
|
|
241
|
+
|
|
242
|
+
declare const Chat: typeof Chat$1 & {
|
|
243
|
+
Canvas: typeof ChatCanvas;
|
|
244
|
+
Messages: typeof ChatMessages;
|
|
245
|
+
Input: typeof ChatInput;
|
|
246
|
+
Error: typeof ChatError;
|
|
247
|
+
Callout: typeof ChatCallout;
|
|
248
|
+
};
|
|
249
|
+
declare const normalizeChatError: typeof normalizeChatError$1;
|
|
250
|
+
declare const createHandler: typeof createHandler$1;
|
|
251
|
+
|
|
252
|
+
export { type AgentConfig, type AgentHandler, Chat, type ChatAttachment, type ChatCanvasProps, type ChatChunk, type ChatComponents, type ChatErrorProps, type ChatInputProps, type ChatMessage, type ChatMessageRendererProps, type ChatMessagesProps, type ChatResponse, createHandler, normalizeChatError, useChatContext };
|