@xcelsior/ui-chat 1.0.7 → 2.0.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/CHANGELOG.md +10 -0
- package/dist/index.d.mts +69 -69
- package/dist/index.d.ts +69 -69
- package/dist/index.js +2458 -627
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2457 -628
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
- package/src/components/BrandIcons.stories.tsx +95 -0
- package/src/components/BrandIcons.tsx +84 -0
- package/src/components/Chat.stories.tsx +149 -16
- package/src/components/Chat.tsx +116 -96
- package/src/components/ChatHeader.tsx +124 -69
- package/src/components/ChatInput.tsx +253 -104
- package/src/components/ChatWidget.tsx +209 -63
- package/src/components/ConversationRating.stories.tsx +33 -0
- package/src/components/ConversationRating.tsx +156 -0
- package/src/components/MarkdownMessage.tsx +202 -0
- package/src/components/MessageItem.stories.tsx +253 -55
- package/src/components/MessageItem.tsx +222 -59
- package/src/components/MessageList.tsx +164 -35
- package/src/components/PreChatForm.tsx +236 -96
- package/src/components/ThinkingIndicator.tsx +370 -0
- package/src/components/TypingIndicator.tsx +27 -11
- package/src/hooks/useDraggablePosition.ts +91 -0
- package/src/hooks/useMessages.ts +12 -13
- package/src/hooks/useResizableWidget.ts +324 -0
- package/src/index.tsx +5 -0
- package/src/types.ts +51 -5
- package/src/utils/markdown-styles.ts +140 -0
- package/storybook-static/assets/BrandIcons-Cjy5INAp.js +4 -0
- package/storybook-static/assets/BrandIcons.stories-BeVC6svr.js +64 -0
- package/storybook-static/assets/Chat.stories-J_Yp51wU.js +803 -0
- package/storybook-static/assets/Color-YHDXOIA2-BMnd3YrF.js +1 -0
- package/storybook-static/assets/ConversationRating.stories-B5_QddHN.js +12 -0
- package/storybook-static/assets/DocsRenderer-CFRXHY34-i_W8iCu9.js +575 -0
- package/storybook-static/assets/MessageItem-DAaKZ9s9.js +14 -0
- package/storybook-static/assets/MessageItem.stories-Ckr1_scc.js +255 -0
- package/storybook-static/assets/ToastContext-Bty1K7ya.js +1 -0
- package/storybook-static/assets/chunk-XP5HYGXS-BpfKkqn7.js +1 -0
- package/storybook-static/assets/en-US-BukEqXxE.js +1 -0
- package/storybook-static/assets/entry-preview-docs-DHohToDm.js +46 -0
- package/storybook-static/assets/entry-preview-oDnntGcx.js +2 -0
- package/storybook-static/assets/iframe-CGBtu2Se.js +211 -0
- package/storybook-static/assets/index--qcDGAq6.js +1 -0
- package/storybook-static/assets/index-BLHw34Di.js +24 -0
- package/storybook-static/assets/index-B_4m48Mv.js +1 -0
- package/storybook-static/assets/index-DgH-xKnr.js +11 -0
- package/storybook-static/assets/index-DrFu-skq.js +6 -0
- package/storybook-static/assets/index-DrdPSA1J.js +240 -0
- package/storybook-static/assets/index-jvNEZhzf.js +1 -0
- package/storybook-static/assets/index-yBjzXJbu.js +9 -0
- package/storybook-static/assets/jsx-runtime-Cf8x2fCZ.js +9 -0
- package/storybook-static/assets/preview-B8lJiyuQ.js +34 -0
- package/storybook-static/assets/preview-BBWR9nbA.js +1 -0
- package/storybook-static/assets/preview-BRpahs9B.js +2 -0
- package/storybook-static/assets/preview-BWzBA1C2.js +396 -0
- package/storybook-static/assets/preview-CvbIS5ZJ.js +1 -0
- package/storybook-static/assets/preview-DD_OYowb.js +1 -0
- package/storybook-static/assets/preview-DGUiP6tS.js +7 -0
- package/storybook-static/assets/preview-DHQbi4pV.js +1 -0
- package/storybook-static/assets/preview-DUOvJmsz.js +1 -0
- package/storybook-static/assets/preview-DcGwT3kv.css +1 -0
- package/storybook-static/assets/preview-DwI0w3cI.js +1 -0
- package/storybook-static/assets/react-18-CALspjOX.js +1 -0
- package/storybook-static/assets/test-utils-BE0XkMtV.js +9 -0
- package/storybook-static/favicon.svg +1 -0
- package/storybook-static/iframe.html +666 -0
- package/storybook-static/index.html +177 -0
- package/storybook-static/index.json +1 -0
- package/storybook-static/nunito-sans-bold-italic.woff2 +0 -0
- package/storybook-static/nunito-sans-bold.woff2 +0 -0
- package/storybook-static/nunito-sans-italic.woff2 +0 -0
- package/storybook-static/nunito-sans-regular.woff2 +0 -0
- package/storybook-static/project.json +1 -0
- package/storybook-static/sb-addons/essentials-actions-3/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/essentials-backgrounds-5/manager-bundle.js +12 -0
- package/storybook-static/sb-addons/essentials-controls-2/manager-bundle.js +405 -0
- package/storybook-static/sb-addons/essentials-docs-4/manager-bundle.js +245 -0
- package/storybook-static/sb-addons/essentials-measure-8/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/essentials-outline-9/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/essentials-toolbars-7/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/essentials-viewport-6/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/interactions-10/manager-bundle.js +222 -0
- package/storybook-static/sb-addons/links-1/manager-bundle.js +3 -0
- package/storybook-static/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js +3 -0
- package/storybook-static/sb-common-assets/favicon.svg +1 -0
- package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
- package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
- package/storybook-static/sb-manager/globals-module-info.js +1052 -0
- package/storybook-static/sb-manager/globals-runtime.js +42127 -0
- package/storybook-static/sb-manager/globals.js +48 -0
- package/storybook-static/sb-manager/runtime.js +12048 -0
- package/.turbo/turbo-lint.log +0 -5
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export type ResizeEdge = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw';
|
|
4
|
+
|
|
5
|
+
interface UseResizableWidgetOptions {
|
|
6
|
+
initialWidth?: number;
|
|
7
|
+
initialHeight?: number;
|
|
8
|
+
minWidth?: number;
|
|
9
|
+
minHeight?: number;
|
|
10
|
+
maxWidth?: number;
|
|
11
|
+
maxHeight?: number;
|
|
12
|
+
storageKey?: string;
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UseResizableWidgetReturn {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
isResizing: boolean;
|
|
20
|
+
/** Attach to the widget container for edge/corner resize zones */
|
|
21
|
+
containerResizeProps: {
|
|
22
|
+
onMouseMove: (e: React.MouseEvent) => void;
|
|
23
|
+
onMouseDown: (e: React.MouseEvent) => void;
|
|
24
|
+
onMouseLeave: (e: React.MouseEvent) => void;
|
|
25
|
+
onTouchStart: (e: React.TouchEvent) => void;
|
|
26
|
+
};
|
|
27
|
+
/** True when user hovers near a resize edge — use for visual hint */
|
|
28
|
+
isNearEdge: boolean;
|
|
29
|
+
/** Which edge/corner the user is near or dragging */
|
|
30
|
+
activeEdge: ResizeEdge | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const STORAGE_KEY = 'xcelsior-chat-size';
|
|
34
|
+
const EDGE_ZONE = 8; // px from edge to trigger resize cursor
|
|
35
|
+
|
|
36
|
+
const CURSOR_MAP: Record<ResizeEdge, string> = {
|
|
37
|
+
n: 'ns-resize',
|
|
38
|
+
s: 'ns-resize',
|
|
39
|
+
e: 'ew-resize',
|
|
40
|
+
w: 'ew-resize',
|
|
41
|
+
ne: 'nesw-resize',
|
|
42
|
+
nw: 'nwse-resize',
|
|
43
|
+
se: 'nwse-resize',
|
|
44
|
+
sw: 'nesw-resize',
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function readStoredSize(
|
|
48
|
+
key: string,
|
|
49
|
+
fallbackWidth: number,
|
|
50
|
+
fallbackHeight: number,
|
|
51
|
+
): { width: number; height: number } {
|
|
52
|
+
try {
|
|
53
|
+
const stored = localStorage.getItem(key);
|
|
54
|
+
if (stored) {
|
|
55
|
+
const parsed = JSON.parse(stored);
|
|
56
|
+
if (typeof parsed.width === 'number' && typeof parsed.height === 'number') {
|
|
57
|
+
return { width: parsed.width, height: parsed.height };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// localStorage unavailable
|
|
62
|
+
}
|
|
63
|
+
return { width: fallbackWidth, height: fallbackHeight };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function persistSize(key: string, width: number, height: number) {
|
|
67
|
+
try {
|
|
68
|
+
localStorage.setItem(key, JSON.stringify({ width, height }));
|
|
69
|
+
} catch {
|
|
70
|
+
// Storage unavailable
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isMobile(): boolean {
|
|
75
|
+
return typeof window !== 'undefined' && window.innerWidth < 768;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Detect which edge/corner the mouse is near */
|
|
79
|
+
function detectEdge(
|
|
80
|
+
e: { clientX: number; clientY: number },
|
|
81
|
+
rect: DOMRect,
|
|
82
|
+
): ResizeEdge | null {
|
|
83
|
+
const { clientX: x, clientY: y } = e;
|
|
84
|
+
const nearTop = y - rect.top < EDGE_ZONE;
|
|
85
|
+
const nearBottom = rect.bottom - y < EDGE_ZONE;
|
|
86
|
+
const nearLeft = x - rect.left < EDGE_ZONE;
|
|
87
|
+
const nearRight = rect.right - x < EDGE_ZONE;
|
|
88
|
+
|
|
89
|
+
if (nearTop && nearLeft) return 'nw';
|
|
90
|
+
if (nearTop && nearRight) return 'ne';
|
|
91
|
+
if (nearBottom && nearLeft) return 'sw';
|
|
92
|
+
if (nearBottom && nearRight) return 'se';
|
|
93
|
+
if (nearTop) return 'n';
|
|
94
|
+
if (nearBottom) return 's';
|
|
95
|
+
if (nearLeft) return 'w';
|
|
96
|
+
if (nearRight) return 'e';
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function useResizableWidget({
|
|
101
|
+
initialWidth = 380,
|
|
102
|
+
initialHeight = 580,
|
|
103
|
+
minWidth = 320,
|
|
104
|
+
minHeight = 400,
|
|
105
|
+
maxWidth = 800,
|
|
106
|
+
maxHeight = 900,
|
|
107
|
+
storageKey = STORAGE_KEY,
|
|
108
|
+
enabled = true,
|
|
109
|
+
}: UseResizableWidgetOptions = {}): UseResizableWidgetReturn {
|
|
110
|
+
const [size, setSize] = useState<{ width: number; height: number }>(() => {
|
|
111
|
+
if (typeof window === 'undefined') return { width: initialWidth, height: initialHeight };
|
|
112
|
+
return readStoredSize(storageKey, initialWidth, initialHeight);
|
|
113
|
+
});
|
|
114
|
+
const [isResizing, setIsResizing] = useState(false);
|
|
115
|
+
const [isNearEdge, setIsNearEdge] = useState(false);
|
|
116
|
+
const [activeEdge, setActiveEdge] = useState<ResizeEdge | null>(null);
|
|
117
|
+
|
|
118
|
+
const sizeRef = useRef(size);
|
|
119
|
+
sizeRef.current = size;
|
|
120
|
+
|
|
121
|
+
const dragRef = useRef<{
|
|
122
|
+
edge: ResizeEdge;
|
|
123
|
+
startX: number;
|
|
124
|
+
startY: number;
|
|
125
|
+
startWidth: number;
|
|
126
|
+
startHeight: number;
|
|
127
|
+
} | null>(null);
|
|
128
|
+
|
|
129
|
+
const containerRef = useRef<HTMLElement | null>(null);
|
|
130
|
+
|
|
131
|
+
const clamp = useCallback(
|
|
132
|
+
(w: number, h: number) => {
|
|
133
|
+
const mxW = Math.min(maxWidth, window.innerWidth - 24);
|
|
134
|
+
const mxH = Math.min(maxHeight, window.innerHeight - 100);
|
|
135
|
+
return {
|
|
136
|
+
width: Math.round(Math.max(minWidth, Math.min(mxW, w))),
|
|
137
|
+
height: Math.round(Math.max(minHeight, Math.min(mxH, h))),
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
[minWidth, minHeight, maxWidth, maxHeight],
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
/** Calculate new size from drag delta based on which edge is being dragged */
|
|
144
|
+
const calcSize = useCallback(
|
|
145
|
+
(dx: number, dy: number) => {
|
|
146
|
+
if (!dragRef.current) return sizeRef.current;
|
|
147
|
+
const { edge, startWidth, startHeight } = dragRef.current;
|
|
148
|
+
|
|
149
|
+
let w = startWidth;
|
|
150
|
+
let h = startHeight;
|
|
151
|
+
|
|
152
|
+
// Horizontal: east edges expand right, west edges expand left (invert delta)
|
|
153
|
+
if (edge.includes('e')) w = startWidth + dx;
|
|
154
|
+
if (edge.includes('w')) w = startWidth - dx;
|
|
155
|
+
|
|
156
|
+
// Vertical: south edges expand down, north edges expand up (invert delta)
|
|
157
|
+
if (edge.includes('s')) h = startHeight + dy;
|
|
158
|
+
if (edge.includes('n')) h = startHeight - dy;
|
|
159
|
+
|
|
160
|
+
return clamp(w, h);
|
|
161
|
+
},
|
|
162
|
+
[clamp],
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// ─── Mouse handlers ──────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
const handleDocMouseMove = useCallback(
|
|
168
|
+
(e: MouseEvent) => {
|
|
169
|
+
if (!dragRef.current) return;
|
|
170
|
+
const dx = e.clientX - dragRef.current.startX;
|
|
171
|
+
const dy = e.clientY - dragRef.current.startY;
|
|
172
|
+
setSize(calcSize(dx, dy));
|
|
173
|
+
},
|
|
174
|
+
[calcSize],
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const handleDocMouseUp = useCallback(
|
|
178
|
+
(e: MouseEvent) => {
|
|
179
|
+
if (!dragRef.current) return;
|
|
180
|
+
const dx = e.clientX - dragRef.current.startX;
|
|
181
|
+
const dy = e.clientY - dragRef.current.startY;
|
|
182
|
+
const final = calcSize(dx, dy);
|
|
183
|
+
persistSize(storageKey, final.width, final.height);
|
|
184
|
+
dragRef.current = null;
|
|
185
|
+
setIsResizing(false);
|
|
186
|
+
setActiveEdge(null);
|
|
187
|
+
document.body.style.cursor = '';
|
|
188
|
+
document.removeEventListener('mousemove', handleDocMouseMove);
|
|
189
|
+
document.removeEventListener('mouseup', handleDocMouseUp);
|
|
190
|
+
},
|
|
191
|
+
[calcSize, storageKey, handleDocMouseMove],
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// ─── Touch handlers ──────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
const handleDocTouchMove = useCallback(
|
|
197
|
+
(e: TouchEvent) => {
|
|
198
|
+
if (!dragRef.current || e.touches.length === 0) return;
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
const t = e.touches[0];
|
|
201
|
+
const dx = t.clientX - dragRef.current.startX;
|
|
202
|
+
const dy = t.clientY - dragRef.current.startY;
|
|
203
|
+
setSize(calcSize(dx, dy));
|
|
204
|
+
},
|
|
205
|
+
[calcSize],
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const handleDocTouchEnd = useCallback(
|
|
209
|
+
(e: TouchEvent) => {
|
|
210
|
+
if (!dragRef.current) return;
|
|
211
|
+
const t = e.changedTouches[0];
|
|
212
|
+
if (t) {
|
|
213
|
+
const dx = t.clientX - dragRef.current.startX;
|
|
214
|
+
const dy = t.clientY - dragRef.current.startY;
|
|
215
|
+
const final = calcSize(dx, dy);
|
|
216
|
+
persistSize(storageKey, final.width, final.height);
|
|
217
|
+
}
|
|
218
|
+
dragRef.current = null;
|
|
219
|
+
setIsResizing(false);
|
|
220
|
+
setActiveEdge(null);
|
|
221
|
+
document.removeEventListener('touchmove', handleDocTouchMove);
|
|
222
|
+
document.removeEventListener('touchend', handleDocTouchEnd);
|
|
223
|
+
},
|
|
224
|
+
[calcSize, storageKey, handleDocTouchMove],
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
// ─── Cleanup ─────────────────────────────────────────────────────────
|
|
228
|
+
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
return () => {
|
|
231
|
+
document.removeEventListener('mousemove', handleDocMouseMove);
|
|
232
|
+
document.removeEventListener('mouseup', handleDocMouseUp);
|
|
233
|
+
document.removeEventListener('touchmove', handleDocTouchMove);
|
|
234
|
+
document.removeEventListener('touchend', handleDocTouchEnd);
|
|
235
|
+
document.body.style.cursor = '';
|
|
236
|
+
};
|
|
237
|
+
}, [handleDocMouseMove, handleDocMouseUp, handleDocTouchMove, handleDocTouchEnd]);
|
|
238
|
+
|
|
239
|
+
// ─── Container event props ───────────────────────────────────────────
|
|
240
|
+
|
|
241
|
+
const onContainerMouseMove = useCallback(
|
|
242
|
+
(e: React.MouseEvent) => {
|
|
243
|
+
if (!enabled || isMobile() || isResizing) return;
|
|
244
|
+
const el = e.currentTarget as HTMLElement;
|
|
245
|
+
containerRef.current = el;
|
|
246
|
+
const rect = el.getBoundingClientRect();
|
|
247
|
+
const edge = detectEdge(e, rect);
|
|
248
|
+
setIsNearEdge(!!edge);
|
|
249
|
+
setActiveEdge(edge);
|
|
250
|
+
el.style.cursor = edge ? CURSOR_MAP[edge] : '';
|
|
251
|
+
},
|
|
252
|
+
[enabled, isResizing],
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const onContainerMouseDown = useCallback(
|
|
256
|
+
(e: React.MouseEvent) => {
|
|
257
|
+
if (!enabled || isMobile() || !activeEdge) return;
|
|
258
|
+
// Only start resize if near an edge
|
|
259
|
+
e.preventDefault();
|
|
260
|
+
e.stopPropagation();
|
|
261
|
+
dragRef.current = {
|
|
262
|
+
edge: activeEdge,
|
|
263
|
+
startX: e.clientX,
|
|
264
|
+
startY: e.clientY,
|
|
265
|
+
startWidth: sizeRef.current.width,
|
|
266
|
+
startHeight: sizeRef.current.height,
|
|
267
|
+
};
|
|
268
|
+
setIsResizing(true);
|
|
269
|
+
document.body.style.cursor = CURSOR_MAP[activeEdge];
|
|
270
|
+
document.addEventListener('mousemove', handleDocMouseMove);
|
|
271
|
+
document.addEventListener('mouseup', handleDocMouseUp);
|
|
272
|
+
},
|
|
273
|
+
[enabled, activeEdge, handleDocMouseMove, handleDocMouseUp],
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const onContainerMouseLeave = useCallback(
|
|
277
|
+
(_e: React.MouseEvent) => {
|
|
278
|
+
if (!isResizing) {
|
|
279
|
+
setIsNearEdge(false);
|
|
280
|
+
setActiveEdge(null);
|
|
281
|
+
if (containerRef.current) containerRef.current.style.cursor = '';
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
[isResizing],
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
const onContainerTouchStart = useCallback(
|
|
288
|
+
(e: React.TouchEvent) => {
|
|
289
|
+
if (!enabled || isMobile() || e.touches.length === 0) return;
|
|
290
|
+
const el = e.currentTarget as HTMLElement;
|
|
291
|
+
const rect = el.getBoundingClientRect();
|
|
292
|
+
const t = e.touches[0];
|
|
293
|
+
const edge = detectEdge(t, rect);
|
|
294
|
+
if (!edge) return;
|
|
295
|
+
|
|
296
|
+
dragRef.current = {
|
|
297
|
+
edge,
|
|
298
|
+
startX: t.clientX,
|
|
299
|
+
startY: t.clientY,
|
|
300
|
+
startWidth: sizeRef.current.width,
|
|
301
|
+
startHeight: sizeRef.current.height,
|
|
302
|
+
};
|
|
303
|
+
setIsResizing(true);
|
|
304
|
+
setActiveEdge(edge);
|
|
305
|
+
document.addEventListener('touchmove', handleDocTouchMove, { passive: false });
|
|
306
|
+
document.addEventListener('touchend', handleDocTouchEnd);
|
|
307
|
+
},
|
|
308
|
+
[enabled, handleDocTouchMove, handleDocTouchEnd],
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
width: size.width,
|
|
313
|
+
height: size.height,
|
|
314
|
+
isResizing,
|
|
315
|
+
isNearEdge,
|
|
316
|
+
activeEdge,
|
|
317
|
+
containerResizeProps: {
|
|
318
|
+
onMouseMove: onContainerMouseMove,
|
|
319
|
+
onMouseDown: onContainerMouseDown,
|
|
320
|
+
onMouseLeave: onContainerMouseLeave,
|
|
321
|
+
onTouchStart: onContainerTouchStart,
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -6,7 +6,10 @@ export { Chat } from './components/Chat';
|
|
|
6
6
|
// Individual components (for custom implementations)
|
|
7
7
|
export { ChatHeader } from './components/ChatHeader';
|
|
8
8
|
export { ChatInput } from './components/ChatInput';
|
|
9
|
+
export { MarkdownMessage } from './components/MarkdownMessage';
|
|
9
10
|
export { MessageItem } from './components/MessageItem';
|
|
11
|
+
export { ThinkingIndicator } from './components/ThinkingIndicator';
|
|
12
|
+
export type { ThinkingIndicatorProps } from './components/ThinkingIndicator';
|
|
10
13
|
export { MessageList } from './components/MessageList';
|
|
11
14
|
export { TypingIndicator } from './components/TypingIndicator';
|
|
12
15
|
export { PreChatForm } from './components/PreChatForm';
|
|
@@ -44,6 +47,7 @@ import type {
|
|
|
44
47
|
ConversationStatus,
|
|
45
48
|
ConversationPriority,
|
|
46
49
|
ConversationChannel,
|
|
50
|
+
UseMessagesReturn,
|
|
47
51
|
} from './types';
|
|
48
52
|
|
|
49
53
|
export type {
|
|
@@ -63,4 +67,5 @@ export type {
|
|
|
63
67
|
ConversationStatus,
|
|
64
68
|
ConversationPriority,
|
|
65
69
|
ConversationChannel,
|
|
70
|
+
UseMessagesReturn,
|
|
66
71
|
};
|
package/src/types.ts
CHANGED
|
@@ -15,16 +15,18 @@ export interface IMessage {
|
|
|
15
15
|
id: string;
|
|
16
16
|
conversationId: string;
|
|
17
17
|
senderId: string;
|
|
18
|
-
senderType: 'customer' | 'agent' | 'system';
|
|
18
|
+
senderType: 'customer' | 'agent' | 'bot' | 'system';
|
|
19
19
|
content: string;
|
|
20
20
|
messageType: MessageType;
|
|
21
21
|
createdAt: string;
|
|
22
22
|
status: MessageStatus;
|
|
23
|
+
feedback?: 'good' | 'wrong' | 'corrected' | null;
|
|
24
|
+
confidence?: number | null;
|
|
23
25
|
metadata?: Record<string, unknown>;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
// Conversation types
|
|
27
|
-
export type ConversationStatus = 'open' | 'pending' | 'closed' | 'archived';
|
|
29
|
+
export type ConversationStatus = 'open' | 'pending' | 'closed' | 'archived' | 'bot_handling' | 'pending_agent' | 'agent_active';
|
|
28
30
|
export type ConversationPriority = 'low' | 'medium' | 'high' | 'urgent';
|
|
29
31
|
export type ConversationChannel = 'web' | 'mobile' | 'email';
|
|
30
32
|
|
|
@@ -49,7 +51,7 @@ export interface IConversation {
|
|
|
49
51
|
|
|
50
52
|
// WebSocket message types
|
|
51
53
|
export interface IWebSocketMessage {
|
|
52
|
-
type: 'message' | 'typing' | 'read' | 'connected' | 'error' | 'system';
|
|
54
|
+
type: 'message' | 'typing' | 'read' | 'connected' | 'error' | 'system' | 'escalation_requested' | 'bot_response' | 'conversation_updated';
|
|
53
55
|
data: any;
|
|
54
56
|
}
|
|
55
57
|
|
|
@@ -85,6 +87,26 @@ export interface IUploadedFile {
|
|
|
85
87
|
markdown?: string;
|
|
86
88
|
}
|
|
87
89
|
|
|
90
|
+
// Theme configuration
|
|
91
|
+
export interface IChatTheme {
|
|
92
|
+
primary?: string;
|
|
93
|
+
primaryStrong?: string;
|
|
94
|
+
background?: string;
|
|
95
|
+
backgroundAlt?: string;
|
|
96
|
+
text?: string;
|
|
97
|
+
textMuted?: string;
|
|
98
|
+
statusPositive?: string;
|
|
99
|
+
statusCaution?: string;
|
|
100
|
+
statusNegative?: string;
|
|
101
|
+
borderColor?: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Widget position
|
|
105
|
+
export type WidgetPosition = 'left' | 'right' | 'auto';
|
|
106
|
+
|
|
107
|
+
// Identity collection mode
|
|
108
|
+
export type IdentityCollectionMode = 'progressive' | 'form' | 'none';
|
|
109
|
+
|
|
88
110
|
// Chat widget configuration
|
|
89
111
|
export interface IChatConfig {
|
|
90
112
|
// WebSocket connection
|
|
@@ -92,8 +114,8 @@ export interface IChatConfig {
|
|
|
92
114
|
conversationId?: string;
|
|
93
115
|
apiKey: string;
|
|
94
116
|
|
|
95
|
-
// User information
|
|
96
|
-
currentUser
|
|
117
|
+
// User information (optional for anonymous mode)
|
|
118
|
+
currentUser?: IUser;
|
|
97
119
|
|
|
98
120
|
// File upload
|
|
99
121
|
fileUpload?: IFileUploadConfig;
|
|
@@ -116,12 +138,22 @@ export interface IChatConfig {
|
|
|
116
138
|
*/
|
|
117
139
|
disableWebSocket?: boolean;
|
|
118
140
|
|
|
141
|
+
// Theme
|
|
142
|
+
theme?: IChatTheme;
|
|
143
|
+
|
|
144
|
+
// Widget position (default: 'auto' — reads localStorage, falls back to 'right')
|
|
145
|
+
position?: WidgetPosition;
|
|
146
|
+
|
|
147
|
+
// Identity collection (default: 'progressive' — no pre-chat form)
|
|
148
|
+
identityCollection?: IdentityCollectionMode;
|
|
149
|
+
|
|
119
150
|
// Callbacks
|
|
120
151
|
onMessageSent?: (message: IMessage) => void;
|
|
121
152
|
onMessageReceived?: (message: IMessage) => void;
|
|
122
153
|
onConversationChange?: (conversation: IConversation) => void;
|
|
123
154
|
onConnectionChange?: (connected: boolean) => void;
|
|
124
155
|
onError?: (error: Error) => void;
|
|
156
|
+
onConversationRated?: (rating: 'positive' | 'negative') => void;
|
|
125
157
|
|
|
126
158
|
// Toast notifications
|
|
127
159
|
toast?: {
|
|
@@ -131,6 +163,20 @@ export interface IChatConfig {
|
|
|
131
163
|
};
|
|
132
164
|
}
|
|
133
165
|
|
|
166
|
+
// UseMessages return type
|
|
167
|
+
export interface UseMessagesReturn {
|
|
168
|
+
messages: IMessage[];
|
|
169
|
+
addMessage: (message: IMessage) => void;
|
|
170
|
+
updateMessageStatus: (messageId: string, status: IMessage['status']) => void;
|
|
171
|
+
clearMessages: () => void;
|
|
172
|
+
isLoading: boolean;
|
|
173
|
+
error: Error | null;
|
|
174
|
+
loadMore: () => Promise<void>;
|
|
175
|
+
hasMore: boolean;
|
|
176
|
+
isLoadingMore: boolean;
|
|
177
|
+
isBotThinking: boolean;
|
|
178
|
+
}
|
|
179
|
+
|
|
134
180
|
// API Response types
|
|
135
181
|
export interface IApiResponse<T> {
|
|
136
182
|
success: boolean;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import type { IChatTheme } from '../types';
|
|
3
|
+
|
|
4
|
+
export interface MarkdownStyles {
|
|
5
|
+
paragraph: CSSProperties;
|
|
6
|
+
strong: CSSProperties;
|
|
7
|
+
emphasis: CSSProperties;
|
|
8
|
+
link: CSSProperties;
|
|
9
|
+
list: CSSProperties;
|
|
10
|
+
listItem: CSSProperties;
|
|
11
|
+
code: CSSProperties;
|
|
12
|
+
codeBlock: CSSProperties;
|
|
13
|
+
heading: CSSProperties;
|
|
14
|
+
blockquote: CSSProperties;
|
|
15
|
+
hr: CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getMarkdownStyles(
|
|
19
|
+
theme?: IChatTheme,
|
|
20
|
+
isLightTheme?: boolean,
|
|
21
|
+
): MarkdownStyles {
|
|
22
|
+
const primaryColor = theme?.primary || '#337eff';
|
|
23
|
+
const textColor = theme?.text || (isLightTheme ? '#1a1a2e' : '#f7f7f8');
|
|
24
|
+
|
|
25
|
+
// Slightly brightened text for strong elements
|
|
26
|
+
const strongColor = isLightTheme
|
|
27
|
+
? 'rgba(0,0,0,0.9)'
|
|
28
|
+
: 'rgba(255,255,255,0.95)';
|
|
29
|
+
|
|
30
|
+
// Subtle background for inline code
|
|
31
|
+
const inlineCodeBg = isLightTheme
|
|
32
|
+
? 'rgba(0,0,0,0.06)'
|
|
33
|
+
: 'rgba(255,255,255,0.08)';
|
|
34
|
+
|
|
35
|
+
// Darker background for code blocks
|
|
36
|
+
const codeBlockBg = isLightTheme
|
|
37
|
+
? 'rgba(0,0,0,0.04)'
|
|
38
|
+
: 'rgba(0,0,0,0.25)';
|
|
39
|
+
|
|
40
|
+
const codeBlockBorder = isLightTheme
|
|
41
|
+
? '1px solid rgba(0,0,0,0.08)'
|
|
42
|
+
: '1px solid rgba(255,255,255,0.06)';
|
|
43
|
+
|
|
44
|
+
const blockquoteBorder = isLightTheme
|
|
45
|
+
? `3px solid ${primaryColor}60`
|
|
46
|
+
: `3px solid ${primaryColor}80`;
|
|
47
|
+
|
|
48
|
+
const blockquoteBg = isLightTheme
|
|
49
|
+
? 'rgba(0,0,0,0.02)'
|
|
50
|
+
: 'rgba(255,255,255,0.02)';
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
paragraph: {
|
|
54
|
+
margin: '0 0 8px 0',
|
|
55
|
+
lineHeight: '1.6',
|
|
56
|
+
color: textColor,
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
strong: {
|
|
60
|
+
fontWeight: 600,
|
|
61
|
+
color: strongColor,
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
emphasis: {
|
|
65
|
+
fontStyle: 'italic',
|
|
66
|
+
color: textColor,
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
link: {
|
|
70
|
+
color: primaryColor,
|
|
71
|
+
textDecoration: 'underline',
|
|
72
|
+
textUnderlineOffset: '2px',
|
|
73
|
+
cursor: 'pointer',
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
list: {
|
|
77
|
+
paddingLeft: '20px',
|
|
78
|
+
margin: '8px 0',
|
|
79
|
+
color: textColor,
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
listItem: {
|
|
83
|
+
margin: '4px 0',
|
|
84
|
+
lineHeight: '1.5',
|
|
85
|
+
color: textColor,
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
code: {
|
|
89
|
+
fontFamily:
|
|
90
|
+
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
|
91
|
+
fontSize: '0.875em',
|
|
92
|
+
backgroundColor: inlineCodeBg,
|
|
93
|
+
padding: '2px 6px',
|
|
94
|
+
borderRadius: '4px',
|
|
95
|
+
color: textColor,
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
codeBlock: {
|
|
99
|
+
fontFamily:
|
|
100
|
+
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
|
|
101
|
+
fontSize: '0.8125em',
|
|
102
|
+
backgroundColor: codeBlockBg,
|
|
103
|
+
border: codeBlockBorder,
|
|
104
|
+
padding: '12px',
|
|
105
|
+
borderRadius: '8px',
|
|
106
|
+
overflowX: 'auto',
|
|
107
|
+
margin: '8px 0',
|
|
108
|
+
color: textColor,
|
|
109
|
+
lineHeight: '1.5',
|
|
110
|
+
display: 'block',
|
|
111
|
+
whiteSpace: 'pre',
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
heading: {
|
|
115
|
+
fontWeight: 600,
|
|
116
|
+
marginTop: '12px',
|
|
117
|
+
marginBottom: '6px',
|
|
118
|
+
color: strongColor,
|
|
119
|
+
lineHeight: '1.3',
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
blockquote: {
|
|
123
|
+
borderLeft: blockquoteBorder,
|
|
124
|
+
backgroundColor: blockquoteBg,
|
|
125
|
+
margin: '8px 0',
|
|
126
|
+
padding: '6px 12px',
|
|
127
|
+
borderRadius: '0 4px 4px 0',
|
|
128
|
+
color: textColor,
|
|
129
|
+
fontStyle: 'italic',
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
hr: {
|
|
133
|
+
border: 'none',
|
|
134
|
+
borderTop: isLightTheme
|
|
135
|
+
? '1px solid rgba(0,0,0,0.1)'
|
|
136
|
+
: '1px solid rgba(255,255,255,0.08)',
|
|
137
|
+
margin: '12px 0',
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{j as a}from"./jsx-runtime-Cf8x2fCZ.js";function i({size:e=24,color:r="white",className:t="",style:n}){return a.jsxs("svg",{width:e,height:e,viewBox:"0 0 32 32",fill:"none",xmlns:"http://www.w3.org/2000/svg",className:t,style:n,"aria-hidden":"true",children:[a.jsx("path",{d:"M20.582 15.027L31.849 0.036H24.808L17.039 10.303L20.582 15.027ZM24.808 31.837H31.849L20.582 16.846L17.039 21.57L24.808 31.837Z",fill:r}),a.jsx("path",{d:"M14.313 15.027H18.402L7.135 0.036H0.185L9.406 12.392C10.587 13.983 12.359 15.027 14.313 15.027Z",fill:r}),a.jsx("path",{d:"M0.185 31.837H7.135L18.402 16.846H14.313C12.359 16.846 10.588 17.891 9.406 19.481L0.185 31.837Z",fill:r})]})}function s({size:e=28,color:r="white",className:t="",style:n}){return a.jsx("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none",stroke:r,strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round",className:t,style:n,"aria-hidden":"true",children:a.jsx("path",{d:"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"})})}function l({size:e=40,className:r=""}){const t=Math.round(e*.55);return a.jsx("div",{className:`flex items-center justify-center rounded-full ${r}`,style:{width:e,height:e,background:"linear-gradient(135deg, #337eff, #005eff)"},children:a.jsx(i,{size:t,color:"white"})})}try{i.displayName="XcelsiorSymbol",i.__docgenInfo={description:`Xcelsior "X" symbol mark (from favicon-brand.svg)
|
|
2
|
+
Reusable across the chat widget — FAB button, avatar, header, etc.`,displayName:"XcelsiorSymbol",props:{size:{defaultValue:{value:"24"},description:"",name:"size",required:!1,type:{name:"number | undefined"}},color:{defaultValue:{value:"white"},description:"",name:"color",required:!1,type:{name:"string | undefined"}},className:{defaultValue:{value:""},description:"",name:"className",required:!1,type:{name:"string | undefined"}},style:{defaultValue:null,description:"",name:"style",required:!1,type:{name:"CSSProperties | undefined"}}}}}catch{}try{s.displayName="ChatBubbleIcon",s.__docgenInfo={description:`Chat bubble icon for the FAB launcher button.
|
|
3
|
+
Instantly recognizable as a chat trigger.`,displayName:"ChatBubbleIcon",props:{size:{defaultValue:{value:"28"},description:"",name:"size",required:!1,type:{name:"number | undefined"}},color:{defaultValue:{value:"white"},description:"",name:"color",required:!1,type:{name:"string | undefined"}},className:{defaultValue:{value:""},description:"",name:"className",required:!1,type:{name:"string | undefined"}},style:{defaultValue:null,description:"",name:"style",required:!1,type:{name:"CSSProperties | undefined"}}}}}catch{}try{l.displayName="XcelsiorAvatar",l.__docgenInfo={description:`Xcelsior "X" avatar — symbol inside a gradient circle
|
|
4
|
+
Used as bot avatar in messages and chat header`,displayName:"XcelsiorAvatar",props:{size:{defaultValue:{value:"40"},description:"",name:"size",required:!1,type:{name:"number | undefined"}},className:{defaultValue:{value:""},description:"",name:"className",required:!1,type:{name:"string | undefined"}}}}}catch{}export{s as C,i as X,l as a};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import{j as e}from"./jsx-runtime-Cf8x2fCZ.js";import{X as s,a as i}from"./BrandIcons-Cjy5INAp.js";import"./index-yBjzXJbu.js";const H={title:"Brand/Icons",parameters:{layout:"padded"},tags:["autodocs"]},a={render:()=>e.jsxs("div",{className:"flex items-center gap-6 p-4",children:[e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(s,{size:16,color:"#337eff"}),e.jsx("span",{className:"text-xs text-gray-500",children:"16px"})]}),e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(s,{size:24,color:"#337eff"}),e.jsx("span",{className:"text-xs text-gray-500",children:"24px"})]}),e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(s,{size:32,color:"#337eff"}),e.jsx("span",{className:"text-xs text-gray-500",children:"32px"})]}),e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(s,{size:48,color:"#337eff"}),e.jsx("span",{className:"text-xs text-gray-500",children:"48px"})]})]})},t={render:()=>e.jsxs("div",{className:"flex items-center gap-6 p-6 bg-gray-900 rounded-lg",children:[e.jsx(s,{size:24,color:"white"}),e.jsx(s,{size:32,color:"white"}),e.jsx(s,{size:48,color:"white"})]})},r={render:()=>e.jsxs("div",{className:"flex items-center gap-6 p-4",children:[e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(i,{size:24}),e.jsx("span",{className:"text-xs text-gray-500",children:"24px"})]}),e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(i,{size:32}),e.jsx("span",{className:"text-xs text-gray-500",children:"32px"})]}),e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(i,{size:40}),e.jsx("span",{className:"text-xs text-gray-500",children:"40px (default)"})]}),e.jsxs("div",{className:"flex flex-col items-center gap-2",children:[e.jsx(i,{size:56}),e.jsx("span",{className:"text-xs text-gray-500",children:"56px (FAB)"})]})]})},l={render:()=>e.jsxs("div",{className:"flex flex-col gap-3 max-w-md p-4 bg-gray-900 rounded-lg",children:[e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx(i,{size:32}),e.jsxs("div",{className:"bg-gray-800 rounded-lg px-3 py-2 text-sm text-white",children:[e.jsx("p",{className:"text-xs text-blue-400 mb-1",children:"AI Assistant"}),"Hello! How can I help you today?"]})]}),e.jsxs("div",{className:"flex items-start gap-3",children:[e.jsx("div",{className:"w-8 h-8 rounded-full bg-green-600 flex items-center justify-center text-white text-sm font-medium",children:"S"}),e.jsxs("div",{className:"bg-gray-800 rounded-lg px-3 py-2 text-sm text-white",children:[e.jsx("p",{className:"text-xs text-green-400 mb-1",children:"Sarah (Agent)"}),"I'm here to help with your inquiry!"]})]})]})};var c,n,x,o,d;a.parameters={...a.parameters,docs:{...(c=a.parameters)==null?void 0:c.docs,source:{originalSource:`{
|
|
2
|
+
render: () => <div className="flex items-center gap-6 p-4">
|
|
3
|
+
<div className="flex flex-col items-center gap-2">
|
|
4
|
+
<XcelsiorSymbol size={16} color="#337eff" />
|
|
5
|
+
<span className="text-xs text-gray-500">16px</span>
|
|
6
|
+
</div>
|
|
7
|
+
<div className="flex flex-col items-center gap-2">
|
|
8
|
+
<XcelsiorSymbol size={24} color="#337eff" />
|
|
9
|
+
<span className="text-xs text-gray-500">24px</span>
|
|
10
|
+
</div>
|
|
11
|
+
<div className="flex flex-col items-center gap-2">
|
|
12
|
+
<XcelsiorSymbol size={32} color="#337eff" />
|
|
13
|
+
<span className="text-xs text-gray-500">32px</span>
|
|
14
|
+
</div>
|
|
15
|
+
<div className="flex flex-col items-center gap-2">
|
|
16
|
+
<XcelsiorSymbol size={48} color="#337eff" />
|
|
17
|
+
<span className="text-xs text-gray-500">48px</span>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
}`,...(x=(n=a.parameters)==null?void 0:n.docs)==null?void 0:x.source},description:{story:"Xcelsior X symbol at various sizes",...(d=(o=a.parameters)==null?void 0:o.docs)==null?void 0:d.description}}};var m,p,g,f,v;t.parameters={...t.parameters,docs:{...(m=t.parameters)==null?void 0:m.docs,source:{originalSource:`{
|
|
21
|
+
render: () => <div className="flex items-center gap-6 p-6 bg-gray-900 rounded-lg">
|
|
22
|
+
<XcelsiorSymbol size={24} color="white" />
|
|
23
|
+
<XcelsiorSymbol size={32} color="white" />
|
|
24
|
+
<XcelsiorSymbol size={48} color="white" />
|
|
25
|
+
</div>
|
|
26
|
+
}`,...(g=(p=t.parameters)==null?void 0:p.docs)==null?void 0:g.source},description:{story:"Symbol on dark backgrounds",...(v=(f=t.parameters)==null?void 0:f.docs)==null?void 0:v.description}}};var y,N,h,u,j;r.parameters={...r.parameters,docs:{...(y=r.parameters)==null?void 0:y.docs,source:{originalSource:`{
|
|
27
|
+
render: () => <div className="flex items-center gap-6 p-4">
|
|
28
|
+
<div className="flex flex-col items-center gap-2">
|
|
29
|
+
<XcelsiorAvatar size={24} />
|
|
30
|
+
<span className="text-xs text-gray-500">24px</span>
|
|
31
|
+
</div>
|
|
32
|
+
<div className="flex flex-col items-center gap-2">
|
|
33
|
+
<XcelsiorAvatar size={32} />
|
|
34
|
+
<span className="text-xs text-gray-500">32px</span>
|
|
35
|
+
</div>
|
|
36
|
+
<div className="flex flex-col items-center gap-2">
|
|
37
|
+
<XcelsiorAvatar size={40} />
|
|
38
|
+
<span className="text-xs text-gray-500">40px (default)</span>
|
|
39
|
+
</div>
|
|
40
|
+
<div className="flex flex-col items-center gap-2">
|
|
41
|
+
<XcelsiorAvatar size={56} />
|
|
42
|
+
<span className="text-xs text-gray-500">56px (FAB)</span>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
}`,...(h=(N=r.parameters)==null?void 0:N.docs)==null?void 0:h.source},description:{story:"Xcelsior avatar (gradient circle with symbol) at various sizes",...(j=(u=r.parameters)==null?void 0:u.docs)==null?void 0:j.description}}};var b,z,S,w,A;l.parameters={...l.parameters,docs:{...(b=l.parameters)==null?void 0:b.docs,source:{originalSource:`{
|
|
46
|
+
render: () => <div className="flex flex-col gap-3 max-w-md p-4 bg-gray-900 rounded-lg">
|
|
47
|
+
<div className="flex items-start gap-3">
|
|
48
|
+
<XcelsiorAvatar size={32} />
|
|
49
|
+
<div className="bg-gray-800 rounded-lg px-3 py-2 text-sm text-white">
|
|
50
|
+
<p className="text-xs text-blue-400 mb-1">AI Assistant</p>
|
|
51
|
+
Hello! How can I help you today?
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<div className="flex items-start gap-3">
|
|
55
|
+
<div className="w-8 h-8 rounded-full bg-green-600 flex items-center justify-center text-white text-sm font-medium">
|
|
56
|
+
S
|
|
57
|
+
</div>
|
|
58
|
+
<div className="bg-gray-800 rounded-lg px-3 py-2 text-sm text-white">
|
|
59
|
+
<p className="text-xs text-green-400 mb-1">Sarah (Agent)</p>
|
|
60
|
+
I'm here to help with your inquiry!
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
}`,...(S=(z=l.parameters)==null?void 0:z.docs)==null?void 0:S.source},description:{story:"Avatar in context — message list style",...(A=(w=l.parameters)==null?void 0:w.docs)==null?void 0:A.description}}};const B=["SymbolSizes","SymbolOnDark","AvatarSizes","AvatarInContext"];export{l as AvatarInContext,r as AvatarSizes,t as SymbolOnDark,a as SymbolSizes,B as __namedExportsOrder,H as default};
|