@miiflow/assistant-ui 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/{WelcomeScreen-CsFaFNcu.d.mts → WelcomeScreen-BiY3uGhp.d.cts} +60 -5
- package/dist/{WelcomeScreen-TrcbOYob.d.ts → WelcomeScreen-YsXZsAwZ.d.ts} +59 -4
- package/dist/{avatar-DftdWqSs.d.ts → avatar-BgjqLv1r.d.ts} +1 -1
- package/dist/{avatar-D5eHcfjf.d.mts → avatar-CL1hlGO5.d.cts} +1 -1
- package/dist/chunk-3SEVU272.cjs +2 -0
- package/dist/chunk-3SEVU272.cjs.map +1 -0
- package/dist/chunk-65VDOUZG.js +2 -0
- package/dist/chunk-65VDOUZG.js.map +1 -0
- package/dist/chunk-D2PFIJNZ.js +2 -0
- package/dist/chunk-D2PFIJNZ.js.map +1 -0
- package/dist/chunk-HVCCZKEO.cjs +2 -0
- package/dist/chunk-HVCCZKEO.cjs.map +1 -0
- package/dist/chunk-KSMAVBLY.cjs +2 -0
- package/dist/{chunk-CRNBTU42.mjs.map → chunk-KSMAVBLY.cjs.map} +1 -1
- package/dist/chunk-NSTK5EUQ.js +1 -1
- package/dist/chunk-NSTK5EUQ.js.map +1 -1
- package/dist/chunk-OCKHJ4WO.js +1 -1
- package/dist/chunk-OCKHJ4WO.js.map +1 -1
- package/dist/chunk-POKFMILU.js +22 -0
- package/dist/chunk-POKFMILU.js.map +1 -0
- package/dist/chunk-QGRXQAWZ.cjs +22 -0
- package/dist/chunk-QGRXQAWZ.cjs.map +1 -0
- package/dist/chunk-SFEPMZCU.cjs +2 -0
- package/dist/chunk-SFEPMZCU.cjs.map +1 -0
- package/dist/chunk-TOYU46SZ.cjs +2 -0
- package/dist/chunk-TOYU46SZ.cjs.map +1 -0
- package/dist/chunk-W3HWTAF6.js +2 -0
- package/dist/chunk-W3HWTAF6.js.map +1 -0
- package/dist/{chunk-3E2HG62U.mjs → chunk-WG77GQR3.js} +2 -2
- package/dist/chunk-WG77GQR3.js.map +1 -0
- package/dist/{chunk-3ERHTQXR.js → chunk-ZKMXEECD.cjs} +2 -2
- package/dist/chunk-ZKMXEECD.cjs.map +1 -0
- package/dist/client/index.cjs +9 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/{index.d.mts → index.d.cts} +3 -3
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +5 -5
- package/dist/client/index.js.map +1 -1
- package/dist/context/index.cjs +2 -0
- package/dist/context/{index.mjs.map → index.cjs.map} +1 -1
- package/dist/context/{index.d.mts → index.d.cts} +2 -2
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +1 -1
- package/dist/hooks/index.cjs +2 -0
- package/dist/hooks/{index.mjs.map → index.cjs.map} +1 -1
- package/dist/hooks/{index.d.mts → index.d.cts} +48 -11
- package/dist/hooks/index.d.ts +46 -9
- package/dist/hooks/index.js +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{index.d.mts → index.d.cts} +8 -8
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/{message-B21_kqE2.d.ts → message-4-BaKes3.d.ts} +11 -1
- package/dist/{message-ufYsvKXP.d.mts → message-9z0crkf9.d.cts} +12 -2
- package/dist/primitives/index.cjs +2 -0
- package/dist/primitives/{index.mjs.map → index.cjs.map} +1 -1
- package/dist/primitives/{index.d.mts → index.d.cts} +10 -5
- package/dist/primitives/index.d.ts +9 -4
- package/dist/primitives/index.js +1 -1
- package/dist/styled/index.cjs +2 -0
- package/dist/styled/{index.mjs.map → index.cjs.map} +1 -1
- package/dist/styled/{index.d.mts → index.d.cts} +8 -8
- package/dist/styled/index.d.ts +4 -4
- package/dist/styled/index.js +1 -1
- package/dist/styles-no-preflight.css +1 -1
- package/dist/styles.css +1 -1
- package/package.json +4 -1
- package/dist/chunk-3E2HG62U.mjs.map +0 -1
- package/dist/chunk-3ERHTQXR.js.map +0 -1
- package/dist/chunk-3GQNGDXX.mjs +0 -22
- package/dist/chunk-3GQNGDXX.mjs.map +0 -1
- package/dist/chunk-3KB4JYSQ.js +0 -2
- package/dist/chunk-3KB4JYSQ.js.map +0 -1
- package/dist/chunk-BA3VCHRC.js +0 -22
- package/dist/chunk-BA3VCHRC.js.map +0 -1
- package/dist/chunk-CRNBTU42.mjs +0 -2
- package/dist/chunk-KPGHBLGY.mjs +0 -2
- package/dist/chunk-KPGHBLGY.mjs.map +0 -1
- package/dist/chunk-LJQHWCUK.js +0 -2
- package/dist/chunk-LJQHWCUK.js.map +0 -1
- package/dist/chunk-MFCWFFJV.mjs +0 -2
- package/dist/chunk-MFCWFFJV.mjs.map +0 -1
- package/dist/chunk-RTT6LULU.mjs +0 -2
- package/dist/chunk-RTT6LULU.mjs.map +0 -1
- package/dist/client/index.mjs +0 -9
- package/dist/client/index.mjs.map +0 -1
- package/dist/context/index.mjs +0 -2
- package/dist/hooks/index.mjs +0 -2
- package/dist/index.mjs +0 -2
- package/dist/index.mjs.map +0 -1
- package/dist/primitives/index.mjs +0 -2
- package/dist/styled/index.mjs +0 -2
- /package/dist/{branding-SzYU4ncD.d.mts → branding-SzYU4ncD.d.cts} +0 -0
- /package/dist/{streaming-CF63E6iS.d.mts → streaming-CF63E6iS.d.cts} +0 -0
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ function Chat() {
|
|
|
49
49
|
brandingCSSVars,
|
|
50
50
|
loading,
|
|
51
51
|
} = useMiiflowChat({
|
|
52
|
+
// Find these in your Miiflow dashboard under Settings > Embed
|
|
52
53
|
publicKey: "pk_live_...",
|
|
53
54
|
assistantId: "ast_...",
|
|
54
55
|
});
|
|
@@ -128,8 +129,25 @@ Pass a `MiiflowChatConfig` object to `useMiiflowChat`:
|
|
|
128
129
|
| `hmac` | `string` | No | HMAC for identity verification |
|
|
129
130
|
| `timestamp` | `string` | No | Timestamp for HMAC verification |
|
|
130
131
|
| `baseUrl` | `string` | No | Override API endpoint (default: `https://api.miiflow.ai/api`) |
|
|
132
|
+
| `webSocketUrl` | `string` | No | WebSocket URL for tool invocations (auto-derived from `baseUrl` if not set) |
|
|
131
133
|
| `responseTimeout` | `number` | No | SSE stream timeout in ms (default: `60000`) |
|
|
132
134
|
|
|
135
|
+
## Connecting to a Custom Backend
|
|
136
|
+
|
|
137
|
+
By default, the hook connects to `https://api.miiflow.ai`. To point to your own backend, pass a `baseUrl`:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
useMiiflowChat({
|
|
141
|
+
publicKey: "pk_live_...",
|
|
142
|
+
assistantId: "ast_...",
|
|
143
|
+
baseUrl: "https://your-server.example.com/api",
|
|
144
|
+
// webSocketUrl is auto-derived from baseUrl; override if needed:
|
|
145
|
+
// webSocketUrl: "wss://your-server.example.com/ws",
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Your backend must implement the same API contract as the Miiflow platform (session init, SSE streaming, file upload, and tool-result endpoints).
|
|
150
|
+
|
|
133
151
|
## Hook API — `useMiiflowChat`
|
|
134
152
|
|
|
135
153
|
```ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
-
import { A as Attachment, M as MessageData, P as ParticipantRole,
|
|
4
|
-
import { h as AvatarProps$1 } from './avatar-
|
|
5
|
-
import { S as StreamingChunk, V as VisualizationChunkData, C as ClarificationData } from './streaming-CF63E6iS.
|
|
3
|
+
import { A as Attachment, M as MessageData, P as ParticipantRole, d as SuggestedAction, S as SourceReference } from './message-9z0crkf9.cjs';
|
|
4
|
+
import { h as AvatarProps$1 } from './avatar-CL1hlGO5.cjs';
|
|
5
|
+
import { S as StreamingChunk, V as VisualizationChunkData, C as ClarificationData } from './streaming-CF63E6iS.cjs';
|
|
6
6
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
7
|
|
|
8
8
|
interface AttachmentPreviewProps {
|
|
@@ -88,6 +88,8 @@ interface MessageProps {
|
|
|
88
88
|
visualizations?: VisualizationChunkData[];
|
|
89
89
|
/** Base font size multiplier for markdown rendering */
|
|
90
90
|
baselineFontSize?: number;
|
|
91
|
+
/** Total execution time in seconds (persisted from streaming wall-clock) */
|
|
92
|
+
executionTime?: number;
|
|
91
93
|
/** Pending clarification data (agent needs user input) */
|
|
92
94
|
pendingClarification?: ClarificationData;
|
|
93
95
|
/** Callback when user responds to a clarification */
|
|
@@ -135,11 +137,15 @@ interface MessageListProps {
|
|
|
135
137
|
children: ReactNode;
|
|
136
138
|
/** Whether to auto-scroll to bottom */
|
|
137
139
|
autoScroll?: boolean;
|
|
140
|
+
/** Whether to show the scroll-to-bottom button */
|
|
141
|
+
showScrollToBottom?: boolean;
|
|
138
142
|
/** Additional CSS classes */
|
|
139
143
|
className?: string;
|
|
140
144
|
}
|
|
141
145
|
/**
|
|
142
|
-
* Styled MessageList with scrolling and
|
|
146
|
+
* Styled MessageList with scrolling, spacing, and scroll-to-bottom button.
|
|
147
|
+
* Uses the enhanced useAutoScroll hook directly instead of going through
|
|
148
|
+
* the primitive, since we need reactive isAtBottom for the button.
|
|
143
149
|
*/
|
|
144
150
|
declare const MessageList: react.ForwardRefExoticComponent<MessageListProps & react.RefAttributes<HTMLDivElement>>;
|
|
145
151
|
|
|
@@ -191,6 +197,55 @@ interface TypingIndicatorProps {
|
|
|
191
197
|
*/
|
|
192
198
|
declare const TypingIndicator: react.ForwardRefExoticComponent<TypingIndicatorProps & react.RefAttributes<HTMLDivElement>>;
|
|
193
199
|
|
|
200
|
+
interface MessageActionBarProps {
|
|
201
|
+
/** The text content to copy */
|
|
202
|
+
textContent: string;
|
|
203
|
+
/** Optional callback when regenerate is requested */
|
|
204
|
+
onRegenerate?: () => void;
|
|
205
|
+
/** Optional callback when edit is requested */
|
|
206
|
+
onEdit?: () => void;
|
|
207
|
+
/** Additional CSS classes */
|
|
208
|
+
className?: string;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Action bar that appears on hover below assistant message bubbles.
|
|
212
|
+
* Currently implements Copy; onRegenerate and onEdit are reserved for future use.
|
|
213
|
+
*/
|
|
214
|
+
declare function MessageActionBar({ textContent, onRegenerate, onEdit, className, }: MessageActionBarProps): react_jsx_runtime.JSX.Element;
|
|
215
|
+
|
|
216
|
+
interface ScrollToBottomButtonProps {
|
|
217
|
+
/** Whether the viewport is at the bottom (hides button when true) */
|
|
218
|
+
isAtBottom: boolean;
|
|
219
|
+
/** Callback to scroll to bottom */
|
|
220
|
+
onScrollToBottom: () => void;
|
|
221
|
+
/** Additional CSS classes */
|
|
222
|
+
className?: string;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* A floating button that appears when the user has scrolled up,
|
|
226
|
+
* allowing them to quickly jump back to the latest messages.
|
|
227
|
+
* Fades in/out based on scroll position.
|
|
228
|
+
*/
|
|
229
|
+
declare function ScrollToBottomButton({ isAtBottom, onScrollToBottom, className, }: ScrollToBottomButtonProps): react_jsx_runtime.JSX.Element;
|
|
230
|
+
|
|
231
|
+
type ToolStatus = "running" | "complete" | "failed" | "pending";
|
|
232
|
+
interface ToolStatusIndicatorProps {
|
|
233
|
+
/** Name of the tool */
|
|
234
|
+
toolName: string;
|
|
235
|
+
/** Current status */
|
|
236
|
+
status: ToolStatus;
|
|
237
|
+
/** Optional description of what the tool is doing */
|
|
238
|
+
description?: string;
|
|
239
|
+
/** Additional CSS classes */
|
|
240
|
+
className?: string;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Inline status indicator for individual tool executions.
|
|
244
|
+
* Shows tool name + status icon (spinner/check/X/clock).
|
|
245
|
+
* Running state includes a subtle shimmer animation.
|
|
246
|
+
*/
|
|
247
|
+
declare function ToolStatusIndicator({ toolName, status, description, className, }: ToolStatusIndicatorProps): react_jsx_runtime.JSX.Element;
|
|
248
|
+
|
|
194
249
|
interface ChatLayoutProps {
|
|
195
250
|
/** Whether the chat is in empty state (no messages) */
|
|
196
251
|
isEmpty: boolean;
|
|
@@ -243,4 +298,4 @@ interface WelcomeScreenProps {
|
|
|
243
298
|
*/
|
|
244
299
|
declare const WelcomeScreen: react.ForwardRefExoticComponent<WelcomeScreenProps & react.RefAttributes<HTMLDivElement>>;
|
|
245
300
|
|
|
246
|
-
export { AttachmentPreview as A, ChatContainer as C, MarkdownContent as M,
|
|
301
|
+
export { AttachmentPreview as A, ChatContainer as C, MarkdownContent as M, ScrollToBottomButton as S, ToolStatusIndicator as T, WelcomeScreen as W, Avatar as a, ChatLayout as b, Message as c, MessageActionBar as d, MessageComposer as e, MessageList as f, StreamingText as g, SuggestedActions as h, TypingIndicator as i, type AttachmentPreviewProps as j, type AvatarProps as k, type ChatContainerProps as l, type ChatLayoutProps as m, type MarkdownContentProps as n, type MessageActionBarProps as o, type MessageComposerProps as p, type MessageListProps as q, type MessageProps as r, type ScrollToBottomButtonProps as s, type StreamingTextProps as t, type SuggestedActionsProps as u, type ToolStatus as v, type ToolStatusIndicatorProps as w, type TypingIndicatorProps as x, type WelcomeScreenProps as y };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode } from 'react';
|
|
3
|
-
import { A as Attachment, M as MessageData, P as ParticipantRole,
|
|
4
|
-
import { h as AvatarProps$1 } from './avatar-
|
|
3
|
+
import { A as Attachment, M as MessageData, P as ParticipantRole, d as SuggestedAction, S as SourceReference } from './message-4-BaKes3.js';
|
|
4
|
+
import { h as AvatarProps$1 } from './avatar-BgjqLv1r.js';
|
|
5
5
|
import { S as StreamingChunk, V as VisualizationChunkData, C as ClarificationData } from './streaming-CF63E6iS.js';
|
|
6
6
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
7
|
|
|
@@ -88,6 +88,8 @@ interface MessageProps {
|
|
|
88
88
|
visualizations?: VisualizationChunkData[];
|
|
89
89
|
/** Base font size multiplier for markdown rendering */
|
|
90
90
|
baselineFontSize?: number;
|
|
91
|
+
/** Total execution time in seconds (persisted from streaming wall-clock) */
|
|
92
|
+
executionTime?: number;
|
|
91
93
|
/** Pending clarification data (agent needs user input) */
|
|
92
94
|
pendingClarification?: ClarificationData;
|
|
93
95
|
/** Callback when user responds to a clarification */
|
|
@@ -135,11 +137,15 @@ interface MessageListProps {
|
|
|
135
137
|
children: ReactNode;
|
|
136
138
|
/** Whether to auto-scroll to bottom */
|
|
137
139
|
autoScroll?: boolean;
|
|
140
|
+
/** Whether to show the scroll-to-bottom button */
|
|
141
|
+
showScrollToBottom?: boolean;
|
|
138
142
|
/** Additional CSS classes */
|
|
139
143
|
className?: string;
|
|
140
144
|
}
|
|
141
145
|
/**
|
|
142
|
-
* Styled MessageList with scrolling and
|
|
146
|
+
* Styled MessageList with scrolling, spacing, and scroll-to-bottom button.
|
|
147
|
+
* Uses the enhanced useAutoScroll hook directly instead of going through
|
|
148
|
+
* the primitive, since we need reactive isAtBottom for the button.
|
|
143
149
|
*/
|
|
144
150
|
declare const MessageList: react.ForwardRefExoticComponent<MessageListProps & react.RefAttributes<HTMLDivElement>>;
|
|
145
151
|
|
|
@@ -191,6 +197,55 @@ interface TypingIndicatorProps {
|
|
|
191
197
|
*/
|
|
192
198
|
declare const TypingIndicator: react.ForwardRefExoticComponent<TypingIndicatorProps & react.RefAttributes<HTMLDivElement>>;
|
|
193
199
|
|
|
200
|
+
interface MessageActionBarProps {
|
|
201
|
+
/** The text content to copy */
|
|
202
|
+
textContent: string;
|
|
203
|
+
/** Optional callback when regenerate is requested */
|
|
204
|
+
onRegenerate?: () => void;
|
|
205
|
+
/** Optional callback when edit is requested */
|
|
206
|
+
onEdit?: () => void;
|
|
207
|
+
/** Additional CSS classes */
|
|
208
|
+
className?: string;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Action bar that appears on hover below assistant message bubbles.
|
|
212
|
+
* Currently implements Copy; onRegenerate and onEdit are reserved for future use.
|
|
213
|
+
*/
|
|
214
|
+
declare function MessageActionBar({ textContent, onRegenerate, onEdit, className, }: MessageActionBarProps): react_jsx_runtime.JSX.Element;
|
|
215
|
+
|
|
216
|
+
interface ScrollToBottomButtonProps {
|
|
217
|
+
/** Whether the viewport is at the bottom (hides button when true) */
|
|
218
|
+
isAtBottom: boolean;
|
|
219
|
+
/** Callback to scroll to bottom */
|
|
220
|
+
onScrollToBottom: () => void;
|
|
221
|
+
/** Additional CSS classes */
|
|
222
|
+
className?: string;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* A floating button that appears when the user has scrolled up,
|
|
226
|
+
* allowing them to quickly jump back to the latest messages.
|
|
227
|
+
* Fades in/out based on scroll position.
|
|
228
|
+
*/
|
|
229
|
+
declare function ScrollToBottomButton({ isAtBottom, onScrollToBottom, className, }: ScrollToBottomButtonProps): react_jsx_runtime.JSX.Element;
|
|
230
|
+
|
|
231
|
+
type ToolStatus = "running" | "complete" | "failed" | "pending";
|
|
232
|
+
interface ToolStatusIndicatorProps {
|
|
233
|
+
/** Name of the tool */
|
|
234
|
+
toolName: string;
|
|
235
|
+
/** Current status */
|
|
236
|
+
status: ToolStatus;
|
|
237
|
+
/** Optional description of what the tool is doing */
|
|
238
|
+
description?: string;
|
|
239
|
+
/** Additional CSS classes */
|
|
240
|
+
className?: string;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Inline status indicator for individual tool executions.
|
|
244
|
+
* Shows tool name + status icon (spinner/check/X/clock).
|
|
245
|
+
* Running state includes a subtle shimmer animation.
|
|
246
|
+
*/
|
|
247
|
+
declare function ToolStatusIndicator({ toolName, status, description, className, }: ToolStatusIndicatorProps): react_jsx_runtime.JSX.Element;
|
|
248
|
+
|
|
194
249
|
interface ChatLayoutProps {
|
|
195
250
|
/** Whether the chat is in empty state (no messages) */
|
|
196
251
|
isEmpty: boolean;
|
|
@@ -243,4 +298,4 @@ interface WelcomeScreenProps {
|
|
|
243
298
|
*/
|
|
244
299
|
declare const WelcomeScreen: react.ForwardRefExoticComponent<WelcomeScreenProps & react.RefAttributes<HTMLDivElement>>;
|
|
245
300
|
|
|
246
|
-
export { AttachmentPreview as A, ChatContainer as C, MarkdownContent as M,
|
|
301
|
+
export { AttachmentPreview as A, ChatContainer as C, MarkdownContent as M, ScrollToBottomButton as S, ToolStatusIndicator as T, WelcomeScreen as W, Avatar as a, ChatLayout as b, Message as c, MessageActionBar as d, MessageComposer as e, MessageList as f, StreamingText as g, SuggestedActions as h, TypingIndicator as i, type AttachmentPreviewProps as j, type AvatarProps as k, type ChatContainerProps as l, type ChatLayoutProps as m, type MarkdownContentProps as n, type MessageActionBarProps as o, type MessageComposerProps as p, type MessageListProps as q, type MessageProps as r, type ScrollToBottomButtonProps as s, type StreamingTextProps as t, type SuggestedActionsProps as u, type ToolStatus as v, type ToolStatusIndicatorProps as w, type TypingIndicatorProps as x, type WelcomeScreenProps as y };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode, TextareaHTMLAttributes, ButtonHTMLAttributes } from 'react';
|
|
3
|
-
import { M as MessageData, P as ParticipantRole } from './message-
|
|
3
|
+
import { M as MessageData, P as ParticipantRole } from './message-4-BaKes3.js';
|
|
4
4
|
|
|
5
5
|
interface MessageContextValue {
|
|
6
6
|
message: MessageData;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { HTMLAttributes, ReactNode, TextareaHTMLAttributes, ButtonHTMLAttributes } from 'react';
|
|
3
|
-
import { M as MessageData, P as ParticipantRole } from './message-
|
|
3
|
+
import { M as MessageData, P as ParticipantRole } from './message-9z0crkf9.cjs';
|
|
4
4
|
|
|
5
5
|
interface MessageContextValue {
|
|
6
6
|
message: MessageData;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var chunkTOYU46SZ_cjs=require('./chunk-TOYU46SZ.cjs'),react=require('react'),jsxRuntime=require('react/jsx-runtime');var d=react.createContext(null);function g(){let e=react.useContext(d);if(!e)throw new Error("useMessage must be used within a Message component");return e}var S=react.forwardRef(({message:e,viewerRole:t="user",children:r,...n},s)=>{let o=(e.participant?.role||"").toLowerCase()===(t||"").toLowerCase(),i=e.isStreaming??false;return jsxRuntime.jsx(d.Provider,{value:{message:e,isViewer:o,isStreaming:i},children:jsxRuntime.jsx("div",{ref:s,"data-role":e.participant?.role,"data-viewer":o,"data-streaming":i,...n,children:r})})});S.displayName="Message";var L=react.forwardRef(({children:e,...t},r)=>{let{message:n}=g();return jsxRuntime.jsx("div",{ref:r,...t,children:e??n.textContent})});L.displayName="MessageContent";var h=react.forwardRef(({format:e,...t},r)=>{let{message:n}=g(),s=typeof n.createdAt=="string"?new Date(n.createdAt):n.createdAt,o=e?e(s):s.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit",hour12:true});return jsxRuntime.jsx("span",{ref:r,...t,children:o})});h.displayName="MessageTimestamp";var R=react.forwardRef(({children:e,autoScroll:t=true,onIsAtBottomChange:r,onScrollToBottomRef:n,...s},o)=>{let{containerRef:i,scrollToBottom:a,isAtBottom:p}=chunkTOYU46SZ_cjs.a({enabled:t});return {current:p}.current!==p&&r?.(p),n?.(a),jsxRuntime.jsx("div",{ref:c=>{i.current=c,typeof o=="function"?o(c):o&&(o.current=c);},...s,children:e})});R.displayName="MessageList";var M=react.createContext(null);function x(){let e=react.useContext(M);if(!e)throw new Error("useComposer must be used within a MessageComposer component");return e}var P=react.forwardRef(({onSubmit:e,disabled:t=false,children:r,className:n},s)=>{let o=chunkTOYU46SZ_cjs.b({onSubmit:e,disabled:t}),i=p=>{p.preventDefault(),o.handleSubmit();},a={...o,canSubmit:!!o.canSubmit};return jsxRuntime.jsx(M.Provider,{value:a,children:jsxRuntime.jsx("form",{ref:s,className:n,onSubmit:i,"data-submitting":o.isSubmitting,"data-can-submit":o.canSubmit,children:r})})});P.displayName="MessageComposer";var H=react.forwardRef((e,t)=>{let{content:r,handleContentChange:n,handleKeyDown:s,inputRef:o}=x();return jsxRuntime.jsx("textarea",{ref:a=>{o.current=a,typeof t=="function"?t(a):t&&(t.current=a);},value:r,onChange:a=>n(a.target.value),onKeyDown:s,...e})});H.displayName="ComposerInput";var E=react.forwardRef(({children:e,disabled:t,...r},n)=>{let{canSubmit:s,isSubmitting:o}=x();return jsxRuntime.jsx("button",{ref:n,type:"submit",disabled:t||!s,"data-submitting":o,...r,children:e})});E.displayName="ComposerSubmit";function G(e){let t=e.trim().split(/\s+/);return t.length===1?t[0].charAt(0).toUpperCase():(t[0].charAt(0)+t[t.length-1].charAt(0)).toUpperCase()}var N=react.forwardRef(({name:e,src:t,alt:r,role:n,fallback:s,children:o,...i},a)=>{let p=e?G(e):null;return jsxRuntime.jsx("div",{ref:a,"data-role":n,...i,children:o??jsxRuntime.jsx(jsxRuntime.Fragment,{children:t?jsxRuntime.jsx("img",{src:t,alt:r??e??n??"Avatar",style:{width:"100%",height:"100%"}}):s??p??n?.charAt(0).toUpperCase()})})});N.displayName="Avatar";var w=react.forwardRef(({content:e,isStreaming:t=false,showCursor:r=true,cursor:n,children:s,...o},i)=>jsxRuntime.jsxs("div",{ref:i,"data-streaming":t,...o,children:[s??e,t&&r&&(n??jsxRuntime.jsx("span",{"aria-hidden":"true",style:{display:"inline-block",width:"2px",height:"1em",backgroundColor:"currentColor",marginLeft:"2px",verticalAlign:"text-bottom",animation:"blink 1s step-end infinite"}}))]}));w.displayName="StreamingText";var T=react.createContext(null);function D(){let e=react.useContext(T);if(!e)throw new Error("useSuggestedActions must be used within a SuggestedActions component");return e}var I=react.forwardRef(({actions:e,onSelect:t,children:r,className:n},s)=>e.length===0?null:jsxRuntime.jsx(T.Provider,{value:{actions:e,onSelect:t},children:jsxRuntime.jsx("div",{ref:s,role:"group","aria-label":"Suggested actions",className:n,children:r})}));I.displayName="SuggestedActions";var k=react.forwardRef(({action:e,children:t,onClick:r,...n},s)=>{let{onSelect:o}=D();return jsxRuntime.jsx("button",{ref:s,type:"button",onClick:a=>{r?.(a),a.defaultPrevented||o(e);},...n,children:t??e.label})});k.displayName="ActionButton";var V=react.forwardRef(({children:e,dotCount:t=3,...r},n)=>jsxRuntime.jsx("div",{ref:n,role:"status","aria-label":"Assistant is typing",...r,children:e??jsxRuntime.jsx("span",{"aria-hidden":"true",children:Array.from({length:t}).map((s,o)=>jsxRuntime.jsx("span",{style:{display:"inline-block",width:"6px",height:"6px",borderRadius:"50%",backgroundColor:"currentColor",marginRight:o<t-1?"4px":0,animation:"typing 1.4s infinite ease-in-out",animationDelay:`${o*.2}s`}},o))})}));V.displayName="TypingIndicator";exports.a=d;exports.b=g;exports.c=S;exports.d=L;exports.e=h;exports.f=R;exports.g=M;exports.h=x;exports.i=P;exports.j=H;exports.k=E;exports.l=N;exports.m=w;exports.n=T;exports.o=D;exports.p=I;exports.q=k;exports.r=V;//# sourceMappingURL=chunk-3SEVU272.cjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-3SEVU272.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/primitives/message.tsx","../src/primitives/message-list.tsx","../src/primitives/message-composer.tsx","../src/primitives/avatar.tsx","../src/primitives/streaming-text.tsx","../src/primitives/suggested-actions.tsx","../src/primitives/typing-indicator.tsx"],"names":["MessageContext","createContext","useMessage","context","useContext","Message","forwardRef","message","viewerRole","children","props","ref","isViewer","isStreaming","jsx","MessageContent","MessageTimestamp","format","date","formatted","MessageList","autoScroll","onIsAtBottomChange","onScrollToBottomRef","containerRef","scrollToBottom","isAtBottom","useAutoScroll","node","ComposerContext","useComposer","MessageComposer","onSubmit","disabled","className","composer","useMessageComposer","handleFormSubmit","e","contextValue","ComposerInput","content","handleContentChange","handleKeyDown","inputRef","ComposerSubmit","canSubmit","isSubmitting","getInitials","name","parts","Avatar","src","alt","role","fallback","initials","Fragment","StreamingText","showCursor","cursor","jsxs","SuggestedActionsContext","useSuggestedActions","SuggestedActions","actions","onSelect","ActionButton","action","onClick","TypingIndicator","dotCount","_","i"],"mappings":"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","file":"chunk-3SEVU272.cjs","sourcesContent":["import { createContext, forwardRef, useContext, type HTMLAttributes, type ReactNode } from \"react\";\nimport type { MessageData, ParticipantRole } from \"../types\";\n\ninterface MessageContextValue {\n\tmessage: MessageData;\n\tisViewer: boolean;\n\tisStreaming: boolean;\n}\n\nconst MessageContext = createContext<MessageContextValue | null>(null);\n\n/**\n * Hook to access the current message context.\n * Must be used within a Message component.\n */\nexport function useMessage() {\n\tconst context = useContext(MessageContext);\n\tif (!context) {\n\t\tthrow new Error(\"useMessage must be used within a Message component\");\n\t}\n\treturn context;\n}\n\nexport interface MessageProps extends HTMLAttributes<HTMLDivElement> {\n\t/** The message data */\n\tmessage: MessageData;\n\t/** The viewer's role (used to determine alignment) */\n\tviewerRole?: ParticipantRole;\n\t/** Children to render inside the message */\n\tchildren: ReactNode;\n}\n\n/**\n * Headless Message primitive.\n * Provides message context to children and data attributes for styling.\n */\nexport const Message = forwardRef<HTMLDivElement, MessageProps>(\n\t({ message, viewerRole = \"user\", children, ...props }, ref) => {\n\t\t// Case-insensitive comparison for role matching\n\t\tconst isViewer = (message.participant?.role || \"\").toLowerCase() === (viewerRole || \"\").toLowerCase();\n\t\tconst isStreaming = message.isStreaming ?? false;\n\n\t\treturn (\n\t\t\t<MessageContext.Provider value={{ message, isViewer, isStreaming }}>\n\t\t\t\t<div\n\t\t\t\t\tref={ref}\n\t\t\t\t\tdata-role={message.participant?.role}\n\t\t\t\t\tdata-viewer={isViewer}\n\t\t\t\t\tdata-streaming={isStreaming}\n\t\t\t\t\t{...props}>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t</MessageContext.Provider>\n\t\t);\n\t},\n);\n\nMessage.displayName = \"Message\";\n\nexport interface MessageContentProps extends HTMLAttributes<HTMLDivElement> {\n\t/** Custom content to render instead of message text */\n\tchildren?: ReactNode;\n}\n\n/**\n * Renders the message content.\n * By default renders the message's textContent.\n */\nexport const MessageContent = forwardRef<HTMLDivElement, MessageContentProps>(({ children, ...props }, ref) => {\n\tconst { message } = useMessage();\n\n\treturn (\n\t\t<div ref={ref} {...props}>\n\t\t\t{children ?? message.textContent}\n\t\t</div>\n\t);\n});\n\nMessageContent.displayName = \"MessageContent\";\n\nexport interface MessageTimestampProps extends HTMLAttributes<HTMLSpanElement> {\n\t/** Custom date formatter */\n\tformat?: (date: Date) => string;\n}\n\n/**\n * Renders the message timestamp.\n */\nexport const MessageTimestamp = forwardRef<HTMLSpanElement, MessageTimestampProps>(({ format, ...props }, ref) => {\n\tconst { message } = useMessage();\n\tconst date = typeof message.createdAt === \"string\" ? new Date(message.createdAt) : message.createdAt;\n\n\tconst formatted = format\n\t\t? format(date)\n\t\t: date.toLocaleTimeString(\"en-US\", {\n\t\t\t\thour: \"numeric\",\n\t\t\t\tminute: \"2-digit\",\n\t\t\t\thour12: true,\n\t\t\t});\n\n\treturn (\n\t\t<span ref={ref} {...props}>\n\t\t\t{formatted}\n\t\t</span>\n\t);\n});\n\nMessageTimestamp.displayName = \"MessageTimestamp\";\n\nexport { MessageContext };\n","import { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport { useAutoScroll } from \"../hooks/use-auto-scroll\";\n\nexport interface MessageListProps extends HTMLAttributes<HTMLDivElement> {\n\t/** Messages to render */\n\tchildren: ReactNode;\n\t/** Whether to auto-scroll to bottom on new messages */\n\tautoScroll?: boolean;\n\t/** Whether the viewport is currently at the bottom (exposed for scroll-to-bottom button) */\n\tisAtBottom?: boolean;\n\t/** Callback to receive scroll state updates */\n\tonIsAtBottomChange?: (isAtBottom: boolean) => void;\n\t/** Callback to receive scrollToBottom function */\n\tonScrollToBottomRef?: (scrollToBottom: (behavior?: ScrollBehavior) => void) => void;\n}\n\n/**\n * Headless MessageList primitive.\n * Provides a scrollable container with auto-scroll behavior.\n * Exposes isAtBottom and scrollToBottom for scroll-to-bottom button integration.\n */\nexport const MessageList = forwardRef<HTMLDivElement, MessageListProps>(\n\t({ children, autoScroll = true, onIsAtBottomChange, onScrollToBottomRef, ...props }, ref) => {\n\t\tconst { containerRef, scrollToBottom, isAtBottom } = useAutoScroll<HTMLDivElement>({\n\t\t\tenabled: autoScroll,\n\t\t});\n\n\t\t// Notify parent of scroll state changes\n\t\tconst prevIsAtBottomRef = { current: isAtBottom };\n\t\tif (prevIsAtBottomRef.current !== isAtBottom) {\n\t\t\tonIsAtBottomChange?.(isAtBottom);\n\t\t}\n\n\t\t// Expose scrollToBottom to parent\n\t\tonScrollToBottomRef?.(scrollToBottom);\n\n\t\t// Merge refs\n\t\tconst mergedRef = (node: HTMLDivElement | null) => {\n\t\t\t(containerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n\t\t\tif (typeof ref === \"function\") {\n\t\t\t\tref(node);\n\t\t\t} else if (ref) {\n\t\t\t\tref.current = node;\n\t\t\t}\n\t\t};\n\n\t\treturn (\n\t\t\t<div ref={mergedRef} {...props}>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t);\n\t},\n);\n\nMessageList.displayName = \"MessageList\";\n","import {\n createContext,\n useContext,\n forwardRef,\n type ReactNode,\n type TextareaHTMLAttributes,\n type ButtonHTMLAttributes,\n} from \"react\";\nimport { useMessageComposer } from \"../hooks/use-message-composer\";\n\ninterface ComposerContextValue {\n content: string;\n isSubmitting: boolean;\n canSubmit: boolean;\n error: string | null;\n handleContentChange: (value: string) => void;\n handleSubmit: () => Promise<void>;\n handleKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;\n inputRef: React.RefObject<HTMLTextAreaElement | null>;\n}\n\nconst ComposerContext = createContext<ComposerContextValue | null>(null);\n\n/**\n * Hook to access the composer context.\n * Must be used within a MessageComposer component.\n */\nexport function useComposer() {\n const context = useContext(ComposerContext);\n if (!context) {\n throw new Error(\"useComposer must be used within a MessageComposer component\");\n }\n return context;\n}\n\nexport interface MessageComposerProps {\n /** Callback when message is submitted */\n onSubmit: (content: string, attachments?: File[]) => Promise<void>;\n /** Whether the composer is disabled */\n disabled?: boolean;\n /** Children to render inside the composer */\n children: ReactNode;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Headless MessageComposer primitive.\n * Provides composer state and behavior to children.\n */\nexport const MessageComposer = forwardRef<HTMLFormElement, MessageComposerProps>(\n ({ onSubmit, disabled = false, children, className }, ref) => {\n const composer = useMessageComposer({ onSubmit, disabled });\n\n const handleFormSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n composer.handleSubmit();\n };\n\n const contextValue: ComposerContextValue = {\n ...composer,\n canSubmit: Boolean(composer.canSubmit),\n };\n\n return (\n <ComposerContext.Provider value={contextValue}>\n <form\n ref={ref}\n className={className}\n onSubmit={handleFormSubmit}\n data-submitting={composer.isSubmitting}\n data-can-submit={composer.canSubmit}\n >\n {children}\n </form>\n </ComposerContext.Provider>\n );\n }\n);\n\nMessageComposer.displayName = \"MessageComposer\";\n\nexport interface ComposerInputProps\n extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, \"onChange\" | \"value\"> {}\n\n/**\n * Text input for the message composer.\n */\nexport const ComposerInput = forwardRef<HTMLTextAreaElement, ComposerInputProps>(\n (props, ref) => {\n const { content, handleContentChange, handleKeyDown, inputRef } = useComposer();\n\n // Merge refs\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n (inputRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n };\n\n return (\n <textarea\n ref={mergedRef}\n value={content}\n onChange={(e) => handleContentChange(e.target.value)}\n onKeyDown={handleKeyDown}\n {...props}\n />\n );\n }\n);\n\nComposerInput.displayName = \"ComposerInput\";\n\nexport interface ComposerSubmitProps extends ButtonHTMLAttributes<HTMLButtonElement> {}\n\n/**\n * Submit button for the message composer.\n */\nexport const ComposerSubmit = forwardRef<HTMLButtonElement, ComposerSubmitProps>(\n ({ children, disabled, ...props }, ref) => {\n const { canSubmit, isSubmitting } = useComposer();\n\n return (\n <button\n ref={ref}\n type=\"submit\"\n disabled={disabled || !canSubmit}\n data-submitting={isSubmitting}\n {...props}\n >\n {children}\n </button>\n );\n }\n);\n\nComposerSubmit.displayName = \"ComposerSubmit\";\n\nexport { ComposerContext };\n","import { forwardRef, type HTMLAttributes } from \"react\";\nimport type { ParticipantRole } from \"../types\";\n\nexport interface AvatarProps extends HTMLAttributes<HTMLDivElement> {\n /** Name for fallback initials */\n name?: string;\n /** Image URL */\n src?: string;\n /** Alt text for image */\n alt?: string;\n /** Participant role for styling */\n role?: ParticipantRole;\n /** Fallback content when no image or name */\n fallback?: React.ReactNode;\n}\n\n/**\n * Get initials from a name.\n */\nfunction getInitials(name: string): string {\n const parts = name.trim().split(/\\s+/);\n if (parts.length === 1) {\n return parts[0].charAt(0).toUpperCase();\n }\n return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();\n}\n\n/**\n * Headless Avatar primitive.\n * Renders an image or fallback initials.\n */\nexport const Avatar = forwardRef<HTMLDivElement, AvatarProps>(\n ({ name, src, alt, role, fallback, children, ...props }, ref) => {\n const initials = name ? getInitials(name) : null;\n const altText = alt ?? name ?? role ?? \"Avatar\";\n\n return (\n <div ref={ref} data-role={role} {...props}>\n {children ?? (\n <>\n {src ? (\n <img src={src} alt={altText} style={{ width: \"100%\", height: \"100%\" }} />\n ) : (\n fallback ?? initials ?? role?.charAt(0).toUpperCase()\n )}\n </>\n )}\n </div>\n );\n }\n);\n\nAvatar.displayName = \"Avatar\";\n","import { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport interface StreamingTextProps extends HTMLAttributes<HTMLDivElement> {\n /** The content to display */\n content: string;\n /** Whether the text is currently streaming */\n isStreaming?: boolean;\n /** Show a cursor indicator while streaming */\n showCursor?: boolean;\n /** Custom cursor element */\n cursor?: ReactNode;\n /** Children override (takes precedence over content) */\n children?: ReactNode;\n}\n\n/**\n * Headless StreamingText primitive.\n * Renders text with streaming indicator support.\n */\nexport const StreamingText = forwardRef<HTMLDivElement, StreamingTextProps>(\n (\n { content, isStreaming = false, showCursor = true, cursor, children, ...props },\n ref\n ) => {\n const defaultCursor = (\n <span\n aria-hidden=\"true\"\n style={{\n display: \"inline-block\",\n width: \"2px\",\n height: \"1em\",\n backgroundColor: \"currentColor\",\n marginLeft: \"2px\",\n verticalAlign: \"text-bottom\",\n animation: \"blink 1s step-end infinite\",\n }}\n />\n );\n\n return (\n <div ref={ref} data-streaming={isStreaming} {...props}>\n {children ?? content}\n {isStreaming && showCursor && (cursor ?? defaultCursor)}\n </div>\n );\n }\n);\n\nStreamingText.displayName = \"StreamingText\";\n","import {\n createContext,\n useContext,\n forwardRef,\n type ReactNode,\n type ButtonHTMLAttributes,\n} from \"react\";\nimport type { SuggestedAction } from \"../types\";\n\ninterface SuggestedActionsContextValue {\n actions: SuggestedAction[];\n onSelect: (action: SuggestedAction) => void;\n}\n\nconst SuggestedActionsContext = createContext<SuggestedActionsContextValue | null>(null);\n\n/**\n * Hook to access suggested actions context.\n */\nexport function useSuggestedActions() {\n const context = useContext(SuggestedActionsContext);\n if (!context) {\n throw new Error(\n \"useSuggestedActions must be used within a SuggestedActions component\"\n );\n }\n return context;\n}\n\nexport interface SuggestedActionsProps {\n /** List of suggested actions */\n actions: SuggestedAction[];\n /** Callback when an action is selected */\n onSelect: (action: SuggestedAction) => void;\n /** Children to render */\n children: ReactNode;\n /** Additional CSS classes */\n className?: string;\n}\n\n/**\n * Headless SuggestedActions primitive.\n * Provides context for rendering action buttons.\n */\nexport const SuggestedActions = forwardRef<HTMLDivElement, SuggestedActionsProps>(\n ({ actions, onSelect, children, className }, ref) => {\n if (actions.length === 0) {\n return null;\n }\n\n return (\n <SuggestedActionsContext.Provider value={{ actions, onSelect }}>\n <div ref={ref} role=\"group\" aria-label=\"Suggested actions\" className={className}>\n {children}\n </div>\n </SuggestedActionsContext.Provider>\n );\n }\n);\n\nSuggestedActions.displayName = \"SuggestedActions\";\n\nexport interface ActionButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** The action this button represents */\n action: SuggestedAction;\n /** Children to render inside button */\n children?: ReactNode;\n}\n\n/**\n * Button for a suggested action.\n */\nexport const ActionButton = forwardRef<HTMLButtonElement, ActionButtonProps>(\n ({ action, children, onClick, ...props }, ref) => {\n const { onSelect } = useSuggestedActions();\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(e);\n if (!e.defaultPrevented) {\n onSelect(action);\n }\n };\n\n return (\n <button ref={ref} type=\"button\" onClick={handleClick} {...props}>\n {children ?? action.label}\n </button>\n );\n }\n);\n\nActionButton.displayName = \"ActionButton\";\n\nexport { SuggestedActionsContext };\n","import { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\n\nexport interface TypingIndicatorProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom content to show while typing */\n children?: ReactNode;\n /** Number of dots to show */\n dotCount?: number;\n}\n\n/**\n * Headless TypingIndicator primitive.\n * Shows animated dots or custom content.\n */\nexport const TypingIndicator = forwardRef<HTMLDivElement, TypingIndicatorProps>(\n ({ children, dotCount = 3, ...props }, ref) => {\n return (\n <div ref={ref} role=\"status\" aria-label=\"Assistant is typing\" {...props}>\n {children ?? (\n <span aria-hidden=\"true\">\n {Array.from({ length: dotCount }).map((_, i) => (\n <span\n key={i}\n style={{\n display: \"inline-block\",\n width: \"6px\",\n height: \"6px\",\n borderRadius: \"50%\",\n backgroundColor: \"currentColor\",\n marginRight: i < dotCount - 1 ? \"4px\" : 0,\n animation: `typing 1.4s infinite ease-in-out`,\n animationDelay: `${i * 0.2}s`,\n }}\n />\n ))}\n </span>\n )}\n </div>\n );\n }\n);\n\nTypingIndicator.displayName = \"TypingIndicator\";\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {useRef,useState,useCallback,useEffect}from'react';function L({enabled:s=true,smooth:h=true,scrollToBottomOnMount:T=true}={}){let o=useRef(null),[p,g]=useState(true),l=useRef(0),c=useRef(null),i=useCallback(t=>{let e=o.current;if(!e)return;let f=t??(h?"smooth":"instant");c.current=f,e.scrollTo({top:e.scrollHeight,behavior:f});},[h]),u=useCallback(t=>Math.abs(t.scrollHeight-t.scrollTop-t.clientHeight)<1||t.scrollHeight<=t.clientHeight,[]),m=useCallback(()=>{let t=o.current;if(!t)return;let e=u(t);!e&&l.current<t.scrollTop||(e&&(c.current=null),(e||c.current===null)&&g(e)),l.current=t.scrollTop;},[u]),r=useCallback(()=>{let t=c.current;if(t)i(t);else if(s){let e=o.current;e&&u(e)&&i("instant");}m();},[s,i,u,m]);return useEffect(()=>{let t=o.current;if(!t||!s)return;let e=new ResizeObserver(()=>{r();});return e.observe(t),()=>e.disconnect()},[s,r]),useEffect(()=>{let t=o.current;if(!t||!s)return;let e=new MutationObserver(f=>{f.some(v=>v.type!=="attributes"||v.attributeName!=="style")&&r();});return e.observe(t,{childList:true,subtree:true,attributes:true,characterData:true}),()=>e.disconnect()},[s,r]),useEffect(()=>{let t=o.current;if(t)return t.addEventListener("scroll",m,{passive:true}),()=>t.removeEventListener("scroll",m)},[m]),useEffect(()=>{s&&T&&(c.current="instant",requestAnimationFrame(()=>{i("instant");}));},[]),{containerRef:o,scrollToBottom:i,isAtBottom:p}}function z({onSubmit:s,disabled:h=false,maxFileSize:T=10*1024*1024,allowedFileTypes:o}){let[p,g]=useState(""),[l,c]=useState([]),[i,u]=useState(false),[m,r]=useState(null),t=useRef(null),e=useCallback(n=>{g(n),r(null);},[]),f=useCallback(n=>{let b=Array.from(n),y=[];for(let a of b){if(a.size>T){r(`File "${a.name}" exceeds maximum size`);continue}if(o&&!o.includes(a.type)){r(`File type "${a.type}" is not allowed`);continue}y.push(a);}c(a=>[...a,...y]);},[T,o]),E=useCallback(n=>{c(b=>b.filter((y,a)=>a!==n));},[]),v=useCallback(async()=>{let n=p.trim();if(!(!n&&l.length===0||h||i)){u(true),r(null);try{await s(n,l.length>0?l:void 0),g(""),c([]),t.current?.focus();}catch(b){r(b instanceof Error?b.message:"Failed to send message");}finally{u(false);}}},[p,l,h,i,s]),M=useCallback(n=>{n.key==="Enter"&&!n.shiftKey&&(n.preventDefault(),v());},[v]),x=useCallback(()=>{g(""),c([]),r(null);},[]),C=(p.trim()||l.length>0)&&!h&&!i;return {content:p,attachments:l,isSubmitting:i,error:m,canSubmit:C,inputRef:t,handleContentChange:e,handleAddAttachment:f,handleRemoveAttachment:E,handleSubmit:v,handleKeyDown:M,clear:x}}export{L as a,z as b};//# sourceMappingURL=chunk-65VDOUZG.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-65VDOUZG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-auto-scroll.ts","../src/hooks/use-message-composer.ts"],"names":["useAutoScroll","enabled","smooth","scrollToBottomOnMount","containerRef","useRef","isAtBottom","setIsAtBottom","useState","lastScrollTopRef","scrollingToBottomBehaviorRef","scrollToBottom","useCallback","behavior","container","b","computeIsAtBottom","el","handleScroll","newIsAtBottom","handleContentResize","scrollBehavior","useEffect","resizeObserver","mutationObserver","mutations","m","useMessageComposer","onSubmit","disabled","maxFileSize","allowedFileTypes","content","setContent","attachments","setAttachments","isSubmitting","setIsSubmitting","error","setError","inputRef","handleContentChange","value","handleAddAttachment","files","fileArray","validFiles","file","prev","handleRemoveAttachment","index","_","i","handleSubmit","trimmedContent","err","handleKeyDown","e","clear","canSubmit"],"mappings":"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","file":"chunk-65VDOUZG.js","sourcesContent":["import { useEffect, useRef, useCallback, useState } from \"react\";\n\nexport interface UseAutoScrollOptions {\n /** Whether auto-scroll is enabled */\n enabled?: boolean;\n /** Smooth scroll behavior */\n smooth?: boolean;\n /** Scroll to bottom when component mounts */\n scrollToBottomOnMount?: boolean;\n}\n\nexport interface UseAutoScrollReturn<T extends HTMLElement> {\n /** Ref to attach to the scrollable container */\n containerRef: React.RefObject<T | null>;\n /** Scroll to bottom programmatically (always forces scroll) */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n /** Whether the viewport is currently at the bottom */\n isAtBottom: boolean;\n}\n\n/**\n * Hook to automatically scroll to the bottom of a container\n * when new content is added, unless the user has scrolled up.\n *\n * Combines ResizeObserver + MutationObserver to catch all height changes\n * (image loads, panel expansion, streaming text, new messages).\n *\n * Uses a 1px tolerance for isAtBottom (matches assistant-ui) and\n * persists scroll behavior across content resize to prevent\n * the \"content outraces scroll\" bug.\n */\nexport function useAutoScroll<T extends HTMLElement>({\n enabled = true,\n smooth = true,\n scrollToBottomOnMount = true,\n}: UseAutoScrollOptions = {}): UseAutoScrollReturn<T> {\n const containerRef = useRef<T>(null);\n const [isAtBottom, setIsAtBottom] = useState(true);\n const lastScrollTopRef = useRef(0);\n\n // When scrollToBottom() is called, store the behavior.\n // On subsequent resize/mutation callbacks, re-scroll with the same behavior\n // until the bottom is actually reached. This prevents the \"content outraces\n // scroll\" bug where new content pushes the bottom away faster than smooth\n // scrolling can reach it.\n const scrollingToBottomBehaviorRef = useRef<ScrollBehavior | null>(null);\n\n const scrollToBottom = useCallback(\n (behavior?: ScrollBehavior) => {\n const container = containerRef.current;\n if (!container) return;\n\n const b = behavior ?? (smooth ? \"smooth\" : \"instant\");\n scrollingToBottomBehaviorRef.current = b;\n container.scrollTo({ top: container.scrollHeight, behavior: b });\n },\n [smooth],\n );\n\n // Determine if we're at the bottom (1px tolerance, or content fits without scroll)\n const computeIsAtBottom = useCallback((el: HTMLElement): boolean => {\n return (\n Math.abs(el.scrollHeight - el.scrollTop - el.clientHeight) < 1 ||\n el.scrollHeight <= el.clientHeight\n );\n }, []);\n\n // Scroll event handler — updates isAtBottom state\n const handleScroll = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const newIsAtBottom = computeIsAtBottom(container);\n\n // If user is scrolling UP while we're in the middle of a scrollToBottom,\n // don't flip isAtBottom to false (ignore scroll-down momentum).\n if (!newIsAtBottom && lastScrollTopRef.current < container.scrollTop) {\n // Scroll is moving down — likely from our scrollToBottom call, ignore\n } else {\n if (newIsAtBottom) {\n scrollingToBottomBehaviorRef.current = null;\n }\n\n // Only update state when behavior ref is cleared (i.e. not mid-scroll)\n // or when we actually reached the bottom\n const shouldUpdate =\n newIsAtBottom || scrollingToBottomBehaviorRef.current === null;\n\n if (shouldUpdate) {\n setIsAtBottom(newIsAtBottom);\n }\n }\n\n lastScrollTopRef.current = container.scrollTop;\n }, [computeIsAtBottom]);\n\n // Content resize handler — called by both ResizeObserver and MutationObserver\n const handleContentResize = useCallback(() => {\n const scrollBehavior = scrollingToBottomBehaviorRef.current;\n if (scrollBehavior) {\n // We're in the middle of a scrollToBottom — keep chasing the bottom\n scrollToBottom(scrollBehavior);\n } else if (enabled) {\n // Auto-scroll only if already at bottom\n const container = containerRef.current;\n if (container && computeIsAtBottom(container)) {\n scrollToBottom(\"instant\");\n }\n }\n\n handleScroll();\n }, [enabled, scrollToBottom, computeIsAtBottom, handleScroll]);\n\n // ResizeObserver — catches height changes from image loads, layout shifts, etc.\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !enabled) return;\n\n const resizeObserver = new ResizeObserver(() => {\n handleContentResize();\n });\n\n resizeObserver.observe(container);\n return () => resizeObserver.disconnect();\n }, [enabled, handleContentResize]);\n\n // MutationObserver — catches new messages, streaming text, DOM changes\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !enabled) return;\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Filter out style-only attribute mutations to prevent feedback loops\n // (e.g. components that write styles in response to viewport changes)\n const hasRelevantMutation = mutations.some(\n (m) => m.type !== \"attributes\" || m.attributeName !== \"style\",\n );\n if (hasRelevantMutation) {\n handleContentResize();\n }\n });\n\n mutationObserver.observe(container, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => mutationObserver.disconnect();\n }, [enabled, handleContentResize]);\n\n // Scroll listener\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n container.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => container.removeEventListener(\"scroll\", handleScroll);\n }, [handleScroll]);\n\n // Scroll to bottom on mount\n useEffect(() => {\n if (enabled && scrollToBottomOnMount) {\n // Use instant on mount — no need for smooth animation on initial load\n scrollingToBottomBehaviorRef.current = \"instant\";\n requestAnimationFrame(() => {\n scrollToBottom(\"instant\");\n });\n }\n // Only run on mount\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return {\n containerRef,\n scrollToBottom,\n isAtBottom,\n };\n}\n","import { useState, useCallback, useRef, type KeyboardEvent } from \"react\";\n\ninterface UseMessageComposerOptions {\n /** Callback when message is submitted */\n onSubmit: (content: string, attachments?: File[]) => Promise<void>;\n /** Whether submission is disabled */\n disabled?: boolean;\n /** Maximum file size in bytes */\n maxFileSize?: number;\n /** Allowed file types (MIME types) */\n allowedFileTypes?: string[];\n}\n\n/**\n * Hook to manage message composer state and behavior.\n */\nexport function useMessageComposer({\n onSubmit,\n disabled = false,\n maxFileSize = 10 * 1024 * 1024, // 10MB\n allowedFileTypes,\n}: UseMessageComposerOptions) {\n const [content, setContent] = useState(\"\");\n const [attachments, setAttachments] = useState<File[]>([]);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const inputRef = useRef<HTMLTextAreaElement>(null);\n\n const handleContentChange = useCallback((value: string) => {\n setContent(value);\n setError(null);\n }, []);\n\n const handleAddAttachment = useCallback(\n (files: FileList | File[]) => {\n const fileArray = Array.from(files);\n const validFiles: File[] = [];\n\n for (const file of fileArray) {\n // Check file size\n if (file.size > maxFileSize) {\n setError(`File \"${file.name}\" exceeds maximum size`);\n continue;\n }\n\n // Check file type\n if (allowedFileTypes && !allowedFileTypes.includes(file.type)) {\n setError(`File type \"${file.type}\" is not allowed`);\n continue;\n }\n\n validFiles.push(file);\n }\n\n setAttachments((prev) => [...prev, ...validFiles]);\n },\n [maxFileSize, allowedFileTypes]\n );\n\n const handleRemoveAttachment = useCallback((index: number) => {\n setAttachments((prev) => prev.filter((_, i) => i !== index));\n }, []);\n\n const handleSubmit = useCallback(async () => {\n const trimmedContent = content.trim();\n if ((!trimmedContent && attachments.length === 0) || disabled || isSubmitting) {\n return;\n }\n\n setIsSubmitting(true);\n setError(null);\n\n try {\n await onSubmit(trimmedContent, attachments.length > 0 ? attachments : undefined);\n setContent(\"\");\n setAttachments([]);\n inputRef.current?.focus();\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Failed to send message\");\n } finally {\n setIsSubmitting(false);\n }\n }, [content, attachments, disabled, isSubmitting, onSubmit]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n // Submit on Enter without Shift\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit();\n }\n },\n [handleSubmit]\n );\n\n const clear = useCallback(() => {\n setContent(\"\");\n setAttachments([]);\n setError(null);\n }, []);\n\n const canSubmit = (content.trim() || attachments.length > 0) && !disabled && !isSubmitting;\n\n return {\n content,\n attachments,\n isSubmitting,\n error,\n canSubmit,\n inputRef,\n handleContentChange,\n handleAddAttachment,\n handleRemoveAttachment,\n handleSubmit,\n handleKeyDown,\n clear,\n };\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {useRef,useEffect,useCallback}from'react';function a(t,c){let o=useRef(null),r=useRef(null);return useEffect(()=>()=>{r.current?.();},[]),useCallback(()=>{if(r.current?.(),!o.current&&t.current){let l=t.current;for(;l;){let{overflowY:u}=getComputedStyle(l);if(u==="scroll"||u==="auto"){o.current=l;break}l=l.parentElement;}}let e=o.current;if(!e)return;let f=e.scrollTop,s=e.style.scrollbarWidth;e.style.scrollbarWidth="none";let n=()=>{e.scrollTop=f;};e.addEventListener("scroll",n);let b=setTimeout(()=>{e.removeEventListener("scroll",n),e.style.scrollbarWidth=s,r.current=null;},c);r.current=()=>{clearTimeout(b),e.removeEventListener("scroll",n),e.style.scrollbarWidth=s;};},[c,t])}export{a};//# sourceMappingURL=chunk-D2PFIJNZ.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-D2PFIJNZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-scroll-lock.ts"],"names":["useScrollLock","animatedElementRef","animationDuration","scrollContainerRef","useRef","cleanupRef","useEffect","useCallback","el","overflowY","scrollContainer","scrollPosition","prevScrollbarWidth","resetPosition","timeoutId"],"mappings":"iDA0BO,SAASA,CAAAA,CACdC,EACAC,CAAAA,CACA,CACA,IAAMC,CAAAA,CAAqBC,OAA2B,IAAI,CAAA,CACpDC,CAAAA,CAAaD,MAAAA,CAA4B,IAAI,CAAA,CAGnD,OAAAE,SAAAA,CAAU,IACD,IAAM,CACXD,CAAAA,CAAW,OAAA,KACb,EACC,EAAE,CAAA,CAEcE,WAAAA,CAAY,IAAM,CAKnC,GAHAF,CAAAA,CAAW,OAAA,IAAU,CAGjB,CAACF,CAAAA,CAAmB,OAAA,EAAWF,EAAmB,OAAA,CAAS,CAC7D,IAAIO,CAAAA,CAAyBP,EAAmB,OAAA,CAChD,KAAOO,CAAAA,EAAI,CACT,GAAM,CAAE,SAAA,CAAAC,CAAU,CAAA,CAAI,iBAAiBD,CAAE,CAAA,CACzC,GAAIC,CAAAA,GAAc,UAAYA,CAAAA,GAAc,MAAA,CAAQ,CAClDN,CAAAA,CAAmB,QAAUK,CAAAA,CAC7B,KACF,CACAA,CAAAA,CAAKA,EAAG,cACV,CACF,CAEA,IAAME,CAAAA,CAAkBP,CAAAA,CAAmB,OAAA,CAC3C,GAAI,CAACO,CAAAA,CAAiB,OAGtB,IAAMC,CAAAA,CAAiBD,EAAgB,SAAA,CACjCE,CAAAA,CAAqBF,CAAAA,CAAgB,KAAA,CAAM,eAGjDA,CAAAA,CAAgB,KAAA,CAAM,cAAA,CAAiB,MAAA,CAGvC,IAAMG,CAAAA,CAAgB,IAAM,CAC1BH,CAAAA,CAAgB,UAAYC,EAC9B,CAAA,CACAD,CAAAA,CAAgB,gBAAA,CAAiB,SAAUG,CAAa,CAAA,CAGxD,IAAMC,CAAAA,CAAY,WAAW,IAAM,CACjCJ,CAAAA,CAAgB,mBAAA,CAAoB,QAAA,CAAUG,CAAa,CAAA,CAC3DH,CAAAA,CAAgB,MAAM,cAAA,CAAiBE,CAAAA,CACvCP,CAAAA,CAAW,OAAA,CAAU,KACvB,CAAA,CAAGH,CAAiB,CAAA,CAEpBG,CAAAA,CAAW,QAAU,IAAM,CACzB,YAAA,CAAaS,CAAS,EACtBJ,CAAAA,CAAgB,mBAAA,CAAoB,QAAA,CAAUG,CAAa,EAC3DH,CAAAA,CAAgB,KAAA,CAAM,cAAA,CAAiBE,EACzC,EACF,CAAA,CAAG,CAACV,CAAAA,CAAmBD,CAAkB,CAAC,CAG5C","file":"chunk-D2PFIJNZ.js","sourcesContent":["import { type RefObject, useCallback, useEffect, useRef } from \"react\";\n\n/**\n * Locks scroll position during collapsible/height animations.\n *\n * Prevents viewport jumps when content height changes during animations\n * (e.g. ReasoningPanel expand/collapse). Finds the nearest scrollable\n * ancestor and temporarily locks its scroll position for the animation duration.\n *\n * Adapted from assistant-ui's useScrollLock.\n *\n * @param animatedElementRef - Ref to the animated element\n * @param animationDuration - Lock duration in milliseconds\n * @returns Function to activate the scroll lock (call before toggling)\n *\n * @example\n * ```tsx\n * const panelRef = useRef<HTMLDivElement>(null);\n * const lockScroll = useScrollLock(panelRef, 200);\n *\n * const handleToggle = () => {\n * lockScroll();\n * setIsExpanded(!isExpanded);\n * };\n * ```\n */\nexport function useScrollLock<T extends HTMLElement = HTMLElement>(\n animatedElementRef: RefObject<T | null>,\n animationDuration: number,\n) {\n const scrollContainerRef = useRef<HTMLElement | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n cleanupRef.current?.();\n };\n }, []);\n\n const lockScroll = useCallback(() => {\n // Clean up any previous lock\n cleanupRef.current?.();\n\n // Find nearest scrollable ancestor (cached after first find)\n if (!scrollContainerRef.current && animatedElementRef.current) {\n let el: HTMLElement | null = animatedElementRef.current;\n while (el) {\n const { overflowY } = getComputedStyle(el);\n if (overflowY === \"scroll\" || overflowY === \"auto\") {\n scrollContainerRef.current = el;\n break;\n }\n el = el.parentElement;\n }\n }\n\n const scrollContainer = scrollContainerRef.current;\n if (!scrollContainer) return;\n\n // Capture current scroll position\n const scrollPosition = scrollContainer.scrollTop;\n const prevScrollbarWidth = scrollContainer.style.scrollbarWidth;\n\n // Hide scrollbar during lock to prevent visual jank\n scrollContainer.style.scrollbarWidth = \"none\";\n\n // Lock: intercept any scroll events and reset position\n const resetPosition = () => {\n scrollContainer.scrollTop = scrollPosition;\n };\n scrollContainer.addEventListener(\"scroll\", resetPosition);\n\n // Release lock after animation completes\n const timeoutId = setTimeout(() => {\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = prevScrollbarWidth;\n cleanupRef.current = null;\n }, animationDuration);\n\n cleanupRef.current = () => {\n clearTimeout(timeoutId);\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = prevScrollbarWidth;\n };\n }, [animationDuration, animatedElementRef]);\n\n return lockScroll;\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var react=require('react');function a(t,c){let o=react.useRef(null),r=react.useRef(null);return react.useEffect(()=>()=>{r.current?.();},[]),react.useCallback(()=>{if(r.current?.(),!o.current&&t.current){let l=t.current;for(;l;){let{overflowY:u}=getComputedStyle(l);if(u==="scroll"||u==="auto"){o.current=l;break}l=l.parentElement;}}let e=o.current;if(!e)return;let f=e.scrollTop,s=e.style.scrollbarWidth;e.style.scrollbarWidth="none";let n=()=>{e.scrollTop=f;};e.addEventListener("scroll",n);let b=setTimeout(()=>{e.removeEventListener("scroll",n),e.style.scrollbarWidth=s,r.current=null;},c);r.current=()=>{clearTimeout(b),e.removeEventListener("scroll",n),e.style.scrollbarWidth=s;};},[c,t])}exports.a=a;//# sourceMappingURL=chunk-HVCCZKEO.cjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-HVCCZKEO.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-scroll-lock.ts"],"names":["useScrollLock","animatedElementRef","animationDuration","scrollContainerRef","useRef","cleanupRef","useEffect","useCallback","el","overflowY","scrollContainer","scrollPosition","prevScrollbarWidth","resetPosition","timeoutId"],"mappings":"wCA0BO,SAASA,CAAAA,CACdC,EACAC,CAAAA,CACA,CACA,IAAMC,CAAAA,CAAqBC,aAA2B,IAAI,CAAA,CACpDC,CAAAA,CAAaD,YAAAA,CAA4B,IAAI,CAAA,CAGnD,OAAAE,eAAAA,CAAU,IACD,IAAM,CACXD,CAAAA,CAAW,OAAA,KACb,EACC,EAAE,CAAA,CAEcE,iBAAAA,CAAY,IAAM,CAKnC,GAHAF,CAAAA,CAAW,OAAA,IAAU,CAGjB,CAACF,CAAAA,CAAmB,OAAA,EAAWF,EAAmB,OAAA,CAAS,CAC7D,IAAIO,CAAAA,CAAyBP,EAAmB,OAAA,CAChD,KAAOO,CAAAA,EAAI,CACT,GAAM,CAAE,SAAA,CAAAC,CAAU,CAAA,CAAI,iBAAiBD,CAAE,CAAA,CACzC,GAAIC,CAAAA,GAAc,UAAYA,CAAAA,GAAc,MAAA,CAAQ,CAClDN,CAAAA,CAAmB,QAAUK,CAAAA,CAC7B,KACF,CACAA,CAAAA,CAAKA,EAAG,cACV,CACF,CAEA,IAAME,CAAAA,CAAkBP,CAAAA,CAAmB,OAAA,CAC3C,GAAI,CAACO,CAAAA,CAAiB,OAGtB,IAAMC,CAAAA,CAAiBD,EAAgB,SAAA,CACjCE,CAAAA,CAAqBF,CAAAA,CAAgB,KAAA,CAAM,eAGjDA,CAAAA,CAAgB,KAAA,CAAM,cAAA,CAAiB,MAAA,CAGvC,IAAMG,CAAAA,CAAgB,IAAM,CAC1BH,CAAAA,CAAgB,UAAYC,EAC9B,CAAA,CACAD,CAAAA,CAAgB,gBAAA,CAAiB,SAAUG,CAAa,CAAA,CAGxD,IAAMC,CAAAA,CAAY,WAAW,IAAM,CACjCJ,CAAAA,CAAgB,mBAAA,CAAoB,QAAA,CAAUG,CAAa,CAAA,CAC3DH,CAAAA,CAAgB,MAAM,cAAA,CAAiBE,CAAAA,CACvCP,CAAAA,CAAW,OAAA,CAAU,KACvB,CAAA,CAAGH,CAAiB,CAAA,CAEpBG,CAAAA,CAAW,QAAU,IAAM,CACzB,YAAA,CAAaS,CAAS,EACtBJ,CAAAA,CAAgB,mBAAA,CAAoB,QAAA,CAAUG,CAAa,EAC3DH,CAAAA,CAAgB,KAAA,CAAM,cAAA,CAAiBE,EACzC,EACF,CAAA,CAAG,CAACV,CAAAA,CAAmBD,CAAkB,CAAC,CAG5C","file":"chunk-HVCCZKEO.cjs","sourcesContent":["import { type RefObject, useCallback, useEffect, useRef } from \"react\";\n\n/**\n * Locks scroll position during collapsible/height animations.\n *\n * Prevents viewport jumps when content height changes during animations\n * (e.g. ReasoningPanel expand/collapse). Finds the nearest scrollable\n * ancestor and temporarily locks its scroll position for the animation duration.\n *\n * Adapted from assistant-ui's useScrollLock.\n *\n * @param animatedElementRef - Ref to the animated element\n * @param animationDuration - Lock duration in milliseconds\n * @returns Function to activate the scroll lock (call before toggling)\n *\n * @example\n * ```tsx\n * const panelRef = useRef<HTMLDivElement>(null);\n * const lockScroll = useScrollLock(panelRef, 200);\n *\n * const handleToggle = () => {\n * lockScroll();\n * setIsExpanded(!isExpanded);\n * };\n * ```\n */\nexport function useScrollLock<T extends HTMLElement = HTMLElement>(\n animatedElementRef: RefObject<T | null>,\n animationDuration: number,\n) {\n const scrollContainerRef = useRef<HTMLElement | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n cleanupRef.current?.();\n };\n }, []);\n\n const lockScroll = useCallback(() => {\n // Clean up any previous lock\n cleanupRef.current?.();\n\n // Find nearest scrollable ancestor (cached after first find)\n if (!scrollContainerRef.current && animatedElementRef.current) {\n let el: HTMLElement | null = animatedElementRef.current;\n while (el) {\n const { overflowY } = getComputedStyle(el);\n if (overflowY === \"scroll\" || overflowY === \"auto\") {\n scrollContainerRef.current = el;\n break;\n }\n el = el.parentElement;\n }\n }\n\n const scrollContainer = scrollContainerRef.current;\n if (!scrollContainer) return;\n\n // Capture current scroll position\n const scrollPosition = scrollContainer.scrollTop;\n const prevScrollbarWidth = scrollContainer.style.scrollbarWidth;\n\n // Hide scrollbar during lock to prevent visual jank\n scrollContainer.style.scrollbarWidth = \"none\";\n\n // Lock: intercept any scroll events and reset position\n const resetPosition = () => {\n scrollContainer.scrollTop = scrollPosition;\n };\n scrollContainer.addEventListener(\"scroll\", resetPosition);\n\n // Release lock after animation completes\n const timeoutId = setTimeout(() => {\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = prevScrollbarWidth;\n cleanupRef.current = null;\n }, animationDuration);\n\n cleanupRef.current = () => {\n clearTimeout(timeoutId);\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = prevScrollbarWidth;\n };\n }, [animationDuration, animatedElementRef]);\n\n return lockScroll;\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var react=require('react');function i(e){let n=e.trim();if(n.startsWith("#")){let r=n.slice(1);if(r.length===3)return [parseInt(r[0]+r[0],16),parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16)];if(r.length>=6)return [parseInt(r.slice(0,2),16),parseInt(r.slice(2,4),16),parseInt(r.slice(4,6),16)]}let t=e.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function c(e,n,t){let[r,a,u]=[e/255,n/255,t/255].map(s=>s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4));return .2126*r+.7152*a+.0722*u}function o(e){let n=i(e);return n&&c(...n)>.179?"#1D2033":"#ffffff"}function p(e,n){return react.useMemo(()=>{if(!e&&!n?.iconColor)return {};let t={},r=e?.backgroundBubbleColor||n?.iconColor;return r&&(t["--chat-primary"]=r,t["--chat-user-message-bg"]=r,t["--chat-user-message-text"]=o(r)),e?.headerBackgroundColor&&(t["--chat-header-bg"]=e.headerBackgroundColor),e?.messageFontSize&&(t["--chat-message-font-size"]=`${e.messageFontSize}px`),t},[e,n?.iconColor])}exports.a=o;exports.b=p;//# sourceMappingURL=chunk-KSMAVBLY.cjs.map
|
|
2
|
+
//# sourceMappingURL=chunk-KSMAVBLY.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/color-contrast.ts","../src/hooks/use-branding-css-vars.ts"],"names":["parseColor","color","hex","h","rgbMatch","relativeLuminance","r","g","b","rs","gs","bs","c","getContrastTextColor","bgColor","rgb","useBrandingCSSVars","branding","overrides","useMemo","vars","primaryColor"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/utils/color-contrast.ts","../src/hooks/use-branding-css-vars.ts"],"names":["parseColor","color","hex","h","rgbMatch","relativeLuminance","r","g","b","rs","gs","bs","c","getContrastTextColor","bgColor","rgb","useBrandingCSSVars","branding","overrides","useMemo","vars","primaryColor"],"mappings":"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","file":"chunk-KSMAVBLY.cjs","sourcesContent":["/**\n * Parse a CSS color string (hex, rgb, rgba) into [r, g, b] values.\n * Returns null if the color cannot be parsed.\n */\nfunction parseColor(color: string): [number, number, number] | null {\n // Hex: #RGB, #RRGGBB\n const hex = color.trim();\n if (hex.startsWith(\"#\")) {\n const h = hex.slice(1);\n if (h.length === 3) {\n return [\n parseInt(h[0] + h[0], 16),\n parseInt(h[1] + h[1], 16),\n parseInt(h[2] + h[2], 16),\n ];\n }\n if (h.length >= 6) {\n return [\n parseInt(h.slice(0, 2), 16),\n parseInt(h.slice(2, 4), 16),\n parseInt(h.slice(4, 6), 16),\n ];\n }\n }\n\n // rgb(r, g, b) or rgba(r, g, b, a)\n const rgbMatch = color.match(\n /rgba?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})/\n );\n if (rgbMatch) {\n return [Number(rgbMatch[1]), Number(rgbMatch[2]), Number(rgbMatch[3])];\n }\n\n return null;\n}\n\n/**\n * Compute the relative luminance of an sRGB color per WCAG 2.0.\n * https://www.w3.org/TR/WCAG20/#relativeluminancedef\n */\nfunction relativeLuminance(r: number, g: number, b: number): number {\n const [rs, gs, bs] = [r / 255, g / 255, b / 255].map((c) =>\n c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n );\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n}\n\n/**\n * Given a background color string, return the best contrast text color.\n * Returns \"#ffffff\" for dark backgrounds and \"#1D2033\" for light backgrounds.\n * Falls back to \"#ffffff\" if the color cannot be parsed.\n */\nexport function getContrastTextColor(bgColor: string): string {\n const rgb = parseColor(bgColor);\n if (!rgb) return \"#ffffff\";\n\n const lum = relativeLuminance(...rgb);\n // WCAG threshold: luminance > 0.179 means the background is \"light\"\n return lum > 0.179 ? \"#1D2033\" : \"#ffffff\";\n}\n","import { useMemo } from \"react\";\nimport type { BrandingData } from \"../types/branding\";\nimport { getContrastTextColor } from \"../utils/color-contrast\";\n\n/**\n * Converts BrandingData into CSS custom properties for chat-ui theming.\n * Returns a CSSProperties object that can be spread onto a container element.\n */\nexport function useBrandingCSSVars(\n branding: BrandingData | null | undefined,\n overrides?: { iconColor?: string }\n): React.CSSProperties {\n return useMemo(() => {\n if (!branding && !overrides?.iconColor) return {};\n\n const vars: Record<string, string> = {};\n\n const primaryColor =\n branding?.backgroundBubbleColor || overrides?.iconColor;\n if (primaryColor) {\n vars[\"--chat-primary\"] = primaryColor;\n vars[\"--chat-user-message-bg\"] = primaryColor;\n vars[\"--chat-user-message-text\"] = getContrastTextColor(primaryColor);\n }\n\n if (branding?.headerBackgroundColor) {\n vars[\"--chat-header-bg\"] = branding.headerBackgroundColor;\n }\n\n if (branding?.messageFontSize) {\n vars[\"--chat-message-font-size\"] = `${branding.messageFontSize}px`;\n }\n\n return vars as React.CSSProperties;\n }, [branding, overrides?.iconColor]);\n}\n"]}
|
package/dist/chunk-NSTK5EUQ.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import {useMemo}from'react';function i(e){let n=e.trim();if(n.startsWith("#")){let r=n.slice(1);if(r.length===3)return [parseInt(r[0]+r[0],16),parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16)];if(r.length>=6)return [parseInt(r.slice(0,2),16),parseInt(r.slice(2,4),16),parseInt(r.slice(4,6),16)]}let t=e.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function c(e,n,t){let[r,a,u]=[e/255,n/255,t/255].map(s=>s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4));return .2126*r+.7152*a+.0722*u}function o(e){let n=i(e);return n&&c(...n)>.179?"#1D2033":"#ffffff"}function p(e,n){return useMemo(()=>{if(!e&&!n?.iconColor)return {};let t={},r=e?.backgroundBubbleColor||n?.iconColor;return r&&(t["--chat-primary"]=r,t["--chat-user-message-bg"]=r,t["--chat-user-message-text"]=o(r)),e?.headerBackgroundColor&&(t["--chat-header-bg"]=e.headerBackgroundColor),e?.messageFontSize&&(t["--chat-message-font-size"]=`${e.messageFontSize}px`),t},[e,n?.iconColor])}export{o as a,p as b};//# sourceMappingURL=chunk-NSTK5EUQ.js.map
|
|
2
2
|
//# sourceMappingURL=chunk-NSTK5EUQ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/color-contrast.ts","../src/hooks/use-branding-css-vars.ts"],"names":["parseColor","color","hex","h","rgbMatch","relativeLuminance","r","g","b","rs","gs","bs","c","getContrastTextColor","bgColor","rgb","useBrandingCSSVars","branding","overrides","useMemo","vars","primaryColor"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/utils/color-contrast.ts","../src/hooks/use-branding-css-vars.ts"],"names":["parseColor","color","hex","h","rgbMatch","relativeLuminance","r","g","b","rs","gs","bs","c","getContrastTextColor","bgColor","rgb","useBrandingCSSVars","branding","overrides","useMemo","vars","primaryColor"],"mappings":"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","file":"chunk-NSTK5EUQ.js","sourcesContent":["/**\n * Parse a CSS color string (hex, rgb, rgba) into [r, g, b] values.\n * Returns null if the color cannot be parsed.\n */\nfunction parseColor(color: string): [number, number, number] | null {\n // Hex: #RGB, #RRGGBB\n const hex = color.trim();\n if (hex.startsWith(\"#\")) {\n const h = hex.slice(1);\n if (h.length === 3) {\n return [\n parseInt(h[0] + h[0], 16),\n parseInt(h[1] + h[1], 16),\n parseInt(h[2] + h[2], 16),\n ];\n }\n if (h.length >= 6) {\n return [\n parseInt(h.slice(0, 2), 16),\n parseInt(h.slice(2, 4), 16),\n parseInt(h.slice(4, 6), 16),\n ];\n }\n }\n\n // rgb(r, g, b) or rgba(r, g, b, a)\n const rgbMatch = color.match(\n /rgba?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})/\n );\n if (rgbMatch) {\n return [Number(rgbMatch[1]), Number(rgbMatch[2]), Number(rgbMatch[3])];\n }\n\n return null;\n}\n\n/**\n * Compute the relative luminance of an sRGB color per WCAG 2.0.\n * https://www.w3.org/TR/WCAG20/#relativeluminancedef\n */\nfunction relativeLuminance(r: number, g: number, b: number): number {\n const [rs, gs, bs] = [r / 255, g / 255, b / 255].map((c) =>\n c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n );\n return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;\n}\n\n/**\n * Given a background color string, return the best contrast text color.\n * Returns \"#ffffff\" for dark backgrounds and \"#1D2033\" for light backgrounds.\n * Falls back to \"#ffffff\" if the color cannot be parsed.\n */\nexport function getContrastTextColor(bgColor: string): string {\n const rgb = parseColor(bgColor);\n if (!rgb) return \"#ffffff\";\n\n const lum = relativeLuminance(...rgb);\n // WCAG threshold: luminance > 0.179 means the background is \"light\"\n return lum > 0.179 ? \"#1D2033\" : \"#ffffff\";\n}\n","import { useMemo } from \"react\";\nimport type { BrandingData } from \"../types/branding\";\nimport { getContrastTextColor } from \"../utils/color-contrast\";\n\n/**\n * Converts BrandingData into CSS custom properties for chat-ui theming.\n * Returns a CSSProperties object that can be spread onto a container element.\n */\nexport function useBrandingCSSVars(\n branding: BrandingData | null | undefined,\n overrides?: { iconColor?: string }\n): React.CSSProperties {\n return useMemo(() => {\n if (!branding && !overrides?.iconColor) return {};\n\n const vars: Record<string, string> = {};\n\n const primaryColor =\n branding?.backgroundBubbleColor || overrides?.iconColor;\n if (primaryColor) {\n vars[\"--chat-primary\"] = primaryColor;\n vars[\"--chat-user-message-bg\"] = primaryColor;\n vars[\"--chat-user-message-text\"] = getContrastTextColor(primaryColor);\n }\n\n if (branding?.headerBackgroundColor) {\n vars[\"--chat-header-bg\"] = branding.headerBackgroundColor;\n }\n\n if (branding?.messageFontSize) {\n vars[\"--chat-message-font-size\"] = `${branding.messageFontSize}px`;\n }\n\n return vars as React.CSSProperties;\n }, [branding, overrides?.iconColor]);\n}\n"]}
|
package/dist/chunk-OCKHJ4WO.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import {createContext,useCallback,useMemo,useContext}from'react';import {jsx}from'react/jsx-runtime';var e=createContext(null);function P({children:t,messages:o,isStreaming:n=false,streamingMessageId:a=null,viewerRole:i="user",onSendMessage:r,onStopStreaming:s,onRetryLastMessage:l,customData:C,onVisualizationAction:u}){let c=useCallback(async(h,v)=>{await r(h,v);},[r]),d=useMemo(()=>({messages:o,isStreaming:n,streamingMessageId:a,viewerRole:i,sendMessage:c,stopStreaming:s,retryLastMessage:l,customData:C,onVisualizationAction:u}),[o,n,a,i,c,s,l,C,u]);return jsx(e.Provider,{value:d,children:t})}function f(){let t=useContext(e);if(!t)throw new Error("useChatContext must be used within a ChatProvider");return t}export{e as a,P as b,f as c};//# sourceMappingURL=chunk-OCKHJ4WO.js.map
|
|
2
2
|
//# sourceMappingURL=chunk-OCKHJ4WO.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context/ChatProvider.tsx"],"names":["ChatContext","createContext","ChatProvider","children","messages","isStreaming","streamingMessageId","viewerRole","onSendMessage","onStopStreaming","onRetryLastMessage","customData","onVisualizationAction","sendMessage","useCallback","content","attachments","value","useMemo","jsx","useChatContext","context","useContext"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/context/ChatProvider.tsx"],"names":["ChatContext","createContext","ChatProvider","children","messages","isStreaming","streamingMessageId","viewerRole","onSendMessage","onStopStreaming","onRetryLastMessage","customData","onVisualizationAction","sendMessage","useCallback","content","attachments","value","useMemo","jsx","useChatContext","context","useContext"],"mappings":"qGA8BA,IAAMA,CAAAA,CAAcC,aAAAA,CAAuC,IAAI,EAexD,SAASC,CAAAA,CAAa,CAC3B,QAAA,CAAAC,CAAAA,CACA,QAAA,CAAAC,CAAAA,CACA,WAAA,CAAAC,CAAAA,CAAc,KAAA,CACd,kBAAA,CAAAC,CAAAA,CAAqB,IAAA,CACrB,UAAA,CAAAC,CAAAA,CAAa,MAAA,CACb,aAAA,CAAAC,CAAAA,CACA,eAAA,CAAAC,CAAAA,CACA,kBAAA,CAAAC,CAAAA,CACA,UAAA,CAAAC,CAAAA,CACA,qBAAA,CAAAC,CACF,CAAA,CAAsB,CACpB,IAAMC,CAAAA,CAAcC,WAAAA,CAClB,MAAOC,CAAAA,CAAiBC,CAAAA,GAAyB,CAC/C,MAAMR,CAAAA,CAAcO,CAAAA,CAASC,CAAW,EAC1C,CAAA,CACA,CAACR,CAAa,CAChB,CAAA,CAEMS,CAAAA,CAAQC,OAAAA,CACZ,KAAO,CACL,QAAA,CAAAd,CAAAA,CACA,WAAA,CAAAC,CAAAA,CACA,kBAAA,CAAAC,CAAAA,CACA,UAAA,CAAAC,CAAAA,CACA,WAAA,CAAAM,CAAAA,CACA,aAAA,CAAeJ,CAAAA,CACf,gBAAA,CAAkBC,CAAAA,CAClB,UAAA,CAAAC,CAAAA,CACA,qBAAA,CAAAC,CACF,CAAA,CAAA,CACA,CACER,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAM,CAAAA,CACAJ,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAC,CACF,CACF,CAAA,CAEA,OAAOO,GAAAA,CAACnB,CAAAA,CAAY,QAAA,CAAZ,CAAqB,KAAA,CAAOiB,CAAAA,CAAQ,QAAA,CAAAd,CAAAA,CAAS,CACvD,CAEO,SAASiB,CAAAA,EAAiB,CAC/B,IAAMC,CAAAA,CAAUC,UAAAA,CAAWtB,CAAW,CAAA,CACtC,GAAI,CAACqB,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,mDAAmD,CAAA,CAErE,OAAOA,CACT","file":"chunk-OCKHJ4WO.js","sourcesContent":["import {\n createContext,\n useContext,\n useCallback,\n useMemo,\n type ReactNode,\n} from \"react\";\nimport type { ChatMessage, ParticipantRole, VisualizationActionEvent } from \"../types\";\n\nexport interface ChatContextValue {\n /** List of messages in the conversation */\n messages: ChatMessage[];\n /** Whether a message is currently being streamed */\n isStreaming: boolean;\n /** ID of the message currently being streamed */\n streamingMessageId: string | null;\n /** The viewer's role (determines message alignment) */\n viewerRole: ParticipantRole;\n /** Send a new message */\n sendMessage: (content: string, attachments?: File[]) => Promise<void>;\n /** Stop the current streaming response */\n stopStreaming?: () => void;\n /** Retry the last failed message */\n retryLastMessage?: () => Promise<void>;\n /** Custom data passed through context */\n customData?: Record<string, unknown>;\n /** Callback when user interacts with a visualization (form submit, card action, etc.) */\n onVisualizationAction?: (event: VisualizationActionEvent) => void;\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null);\n\nexport interface ChatProviderProps {\n children: ReactNode;\n messages: ChatMessage[];\n isStreaming?: boolean;\n streamingMessageId?: string | null;\n viewerRole?: ParticipantRole;\n onSendMessage: (content: string, attachments?: File[]) => Promise<void>;\n onStopStreaming?: () => void;\n onRetryLastMessage?: () => Promise<void>;\n customData?: Record<string, unknown>;\n onVisualizationAction?: (event: VisualizationActionEvent) => void;\n}\n\nexport function ChatProvider({\n children,\n messages,\n isStreaming = false,\n streamingMessageId = null,\n viewerRole = \"user\",\n onSendMessage,\n onStopStreaming,\n onRetryLastMessage,\n customData,\n onVisualizationAction,\n}: ChatProviderProps) {\n const sendMessage = useCallback(\n async (content: string, attachments?: File[]) => {\n await onSendMessage(content, attachments);\n },\n [onSendMessage]\n );\n\n const value = useMemo<ChatContextValue>(\n () => ({\n messages,\n isStreaming,\n streamingMessageId,\n viewerRole,\n sendMessage,\n stopStreaming: onStopStreaming,\n retryLastMessage: onRetryLastMessage,\n customData,\n onVisualizationAction,\n }),\n [\n messages,\n isStreaming,\n streamingMessageId,\n viewerRole,\n sendMessage,\n onStopStreaming,\n onRetryLastMessage,\n customData,\n onVisualizationAction,\n ]\n );\n\n return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;\n}\n\nexport function useChatContext() {\n const context = useContext(ChatContext);\n if (!context) {\n throw new Error(\"useChatContext must be used within a ChatProvider\");\n }\n return context;\n}\n\nexport { ChatContext };\n"]}
|