@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,202 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react';
|
|
2
|
+
import ReactMarkdown from 'react-markdown';
|
|
3
|
+
import type { Components } from 'react-markdown';
|
|
4
|
+
import type { IChatTheme } from '../types';
|
|
5
|
+
import { getMarkdownStyles } from '../utils/markdown-styles';
|
|
6
|
+
|
|
7
|
+
interface MarkdownMessageProps {
|
|
8
|
+
content: string;
|
|
9
|
+
theme?: IChatTheme;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function computeIsLightTheme(theme?: IChatTheme): boolean {
|
|
13
|
+
const bgColor = theme?.background || '#00001a';
|
|
14
|
+
if (!bgColor.startsWith('#')) return false;
|
|
15
|
+
const hex = bgColor.replace('#', '');
|
|
16
|
+
if (hex.length < 6) return false;
|
|
17
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
18
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
19
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
20
|
+
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.5;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function MarkdownMessage({ content, theme }: MarkdownMessageProps) {
|
|
24
|
+
const isLightTheme = computeIsLightTheme(theme);
|
|
25
|
+
const styles = getMarkdownStyles(theme, isLightTheme);
|
|
26
|
+
|
|
27
|
+
// Last paragraph in a message should have no bottom margin to avoid
|
|
28
|
+
// extra space at the bottom of the bubble. We track last child via
|
|
29
|
+
// a wrapper + CSS last-child, but since we use inline styles we
|
|
30
|
+
// strip the margin in the paragraph renderer if the node is the last
|
|
31
|
+
// element. react-markdown does not expose "is last sibling" easily,
|
|
32
|
+
// so we use a simple wrapper approach: strip bottom margin on the
|
|
33
|
+
// outermost container's last child by keeping the container style tight
|
|
34
|
+
// and letting natural flow handle it.
|
|
35
|
+
|
|
36
|
+
const wrapperStyle: CSSProperties = {
|
|
37
|
+
// Collapse bottom margin of the last child to avoid extra padding
|
|
38
|
+
// inside the bubble.
|
|
39
|
+
display: 'block',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div style={wrapperStyle}>
|
|
44
|
+
<ReactMarkdown
|
|
45
|
+
components={{
|
|
46
|
+
// Paragraphs
|
|
47
|
+
p: ({ children }) => (
|
|
48
|
+
<p style={styles.paragraph}>{children}</p>
|
|
49
|
+
),
|
|
50
|
+
|
|
51
|
+
// Bold
|
|
52
|
+
strong: ({ children }) => (
|
|
53
|
+
<strong style={styles.strong}>{children}</strong>
|
|
54
|
+
),
|
|
55
|
+
|
|
56
|
+
// Italic
|
|
57
|
+
em: ({ children }) => (
|
|
58
|
+
<em style={styles.emphasis}>{children}</em>
|
|
59
|
+
),
|
|
60
|
+
|
|
61
|
+
// Links — open in new tab
|
|
62
|
+
a: ({ href, children }) => (
|
|
63
|
+
<a
|
|
64
|
+
href={href}
|
|
65
|
+
target="_blank"
|
|
66
|
+
rel="noopener noreferrer"
|
|
67
|
+
style={styles.link}
|
|
68
|
+
>
|
|
69
|
+
{children}
|
|
70
|
+
</a>
|
|
71
|
+
),
|
|
72
|
+
|
|
73
|
+
// Unordered list
|
|
74
|
+
ul: ({ children }) => (
|
|
75
|
+
<ul style={{ ...styles.list, listStyleType: 'disc' }}>
|
|
76
|
+
{children}
|
|
77
|
+
</ul>
|
|
78
|
+
),
|
|
79
|
+
|
|
80
|
+
// Ordered list
|
|
81
|
+
ol: ({ children }) => (
|
|
82
|
+
<ol
|
|
83
|
+
style={{ ...styles.list, listStyleType: 'decimal' }}
|
|
84
|
+
>
|
|
85
|
+
{children}
|
|
86
|
+
</ol>
|
|
87
|
+
),
|
|
88
|
+
|
|
89
|
+
// List item
|
|
90
|
+
li: ({ children }) => (
|
|
91
|
+
<li style={styles.listItem}>{children}</li>
|
|
92
|
+
),
|
|
93
|
+
|
|
94
|
+
// Inline code vs code block — react-markdown wraps fenced
|
|
95
|
+
// code blocks as <pre><code className="language-*">. The
|
|
96
|
+
// cleanest heuristic: if the className starts with
|
|
97
|
+
// "language-" it is a fenced block; otherwise it's inline.
|
|
98
|
+
code: (({ children, className }) => {
|
|
99
|
+
const isBlock = Boolean(
|
|
100
|
+
className?.startsWith('language-'),
|
|
101
|
+
);
|
|
102
|
+
if (isBlock) {
|
|
103
|
+
return (
|
|
104
|
+
<code
|
|
105
|
+
className={className}
|
|
106
|
+
style={styles.codeBlock}
|
|
107
|
+
>
|
|
108
|
+
{children}
|
|
109
|
+
</code>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return <code style={styles.code}>{children}</code>;
|
|
113
|
+
}) as Components['code'],
|
|
114
|
+
|
|
115
|
+
// Pre wrapper for code blocks
|
|
116
|
+
pre: ({ children }) => (
|
|
117
|
+
<pre
|
|
118
|
+
style={{
|
|
119
|
+
margin: '8px 0',
|
|
120
|
+
padding: 0,
|
|
121
|
+
backgroundColor: 'transparent',
|
|
122
|
+
border: 'none',
|
|
123
|
+
overflow: 'visible',
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
{children}
|
|
127
|
+
</pre>
|
|
128
|
+
),
|
|
129
|
+
|
|
130
|
+
// Headings — h1 through h6 with progressive size reduction
|
|
131
|
+
h1: ({ children }) => (
|
|
132
|
+
<h1
|
|
133
|
+
style={{
|
|
134
|
+
...styles.heading,
|
|
135
|
+
fontSize: '1.15em',
|
|
136
|
+
}}
|
|
137
|
+
>
|
|
138
|
+
{children}
|
|
139
|
+
</h1>
|
|
140
|
+
),
|
|
141
|
+
h2: ({ children }) => (
|
|
142
|
+
<h2
|
|
143
|
+
style={{
|
|
144
|
+
...styles.heading,
|
|
145
|
+
fontSize: '1.1em',
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
{children}
|
|
149
|
+
</h2>
|
|
150
|
+
),
|
|
151
|
+
h3: ({ children }) => (
|
|
152
|
+
<h3
|
|
153
|
+
style={{
|
|
154
|
+
...styles.heading,
|
|
155
|
+
fontSize: '1.05em',
|
|
156
|
+
}}
|
|
157
|
+
>
|
|
158
|
+
{children}
|
|
159
|
+
</h3>
|
|
160
|
+
),
|
|
161
|
+
h4: ({ children }) => (
|
|
162
|
+
<h4 style={{ ...styles.heading, fontSize: '1em' }}>
|
|
163
|
+
{children}
|
|
164
|
+
</h4>
|
|
165
|
+
),
|
|
166
|
+
h5: ({ children }) => (
|
|
167
|
+
<h5
|
|
168
|
+
style={{
|
|
169
|
+
...styles.heading,
|
|
170
|
+
fontSize: '0.95em',
|
|
171
|
+
}}
|
|
172
|
+
>
|
|
173
|
+
{children}
|
|
174
|
+
</h5>
|
|
175
|
+
),
|
|
176
|
+
h6: ({ children }) => (
|
|
177
|
+
<h6
|
|
178
|
+
style={{
|
|
179
|
+
...styles.heading,
|
|
180
|
+
fontSize: '0.9em',
|
|
181
|
+
}}
|
|
182
|
+
>
|
|
183
|
+
{children}
|
|
184
|
+
</h6>
|
|
185
|
+
),
|
|
186
|
+
|
|
187
|
+
// Blockquote
|
|
188
|
+
blockquote: ({ children }) => (
|
|
189
|
+
<blockquote style={styles.blockquote}>
|
|
190
|
+
{children}
|
|
191
|
+
</blockquote>
|
|
192
|
+
),
|
|
193
|
+
|
|
194
|
+
// Horizontal rule
|
|
195
|
+
hr: () => <hr style={styles.hr} />,
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
{content}
|
|
199
|
+
</ReactMarkdown>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
@@ -14,7 +14,6 @@ const meta: Meta<typeof MessageItem> = {
|
|
|
14
14
|
export default meta;
|
|
15
15
|
type Story = StoryObj<typeof MessageItem>;
|
|
16
16
|
|
|
17
|
-
// Mock current user
|
|
18
17
|
const currentUser: IUser = {
|
|
19
18
|
name: 'John Doe',
|
|
20
19
|
email: 'john@example.com',
|
|
@@ -22,7 +21,6 @@ const currentUser: IUser = {
|
|
|
22
21
|
status: 'online',
|
|
23
22
|
};
|
|
24
23
|
|
|
25
|
-
// Base message template
|
|
26
24
|
const baseMessage: IMessage = {
|
|
27
25
|
id: '1',
|
|
28
26
|
conversationId: 'conv-1',
|
|
@@ -34,9 +32,7 @@ const baseMessage: IMessage = {
|
|
|
34
32
|
status: 'sent',
|
|
35
33
|
};
|
|
36
34
|
|
|
37
|
-
/**
|
|
38
|
-
* Customer message (own message)
|
|
39
|
-
*/
|
|
35
|
+
/** Customer message (own message) */
|
|
40
36
|
export const CustomerMessage: Story = {
|
|
41
37
|
args: {
|
|
42
38
|
message: {
|
|
@@ -50,17 +46,14 @@ export const CustomerMessage: Story = {
|
|
|
50
46
|
},
|
|
51
47
|
};
|
|
52
48
|
|
|
53
|
-
/**
|
|
54
|
-
* Human agent message
|
|
55
|
-
*/
|
|
49
|
+
/** Human agent message */
|
|
56
50
|
export const AgentMessage: Story = {
|
|
57
51
|
args: {
|
|
58
52
|
message: {
|
|
59
53
|
...baseMessage,
|
|
60
54
|
senderId: 'agent@example.com',
|
|
61
55
|
senderType: 'agent',
|
|
62
|
-
content:
|
|
63
|
-
"Hi! I'd be happy to help you with your account. What specific issue are you experiencing?",
|
|
56
|
+
content: "Hi! I'd be happy to help you with your account. What issue are you experiencing?",
|
|
64
57
|
},
|
|
65
58
|
currentUser,
|
|
66
59
|
showAvatar: true,
|
|
@@ -68,20 +61,16 @@ export const AgentMessage: Story = {
|
|
|
68
61
|
},
|
|
69
62
|
};
|
|
70
63
|
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
*/
|
|
74
|
-
export const AIMessage: Story = {
|
|
64
|
+
/** Bot message (AI assistant) — new senderType */
|
|
65
|
+
export const BotMessage: Story = {
|
|
75
66
|
args: {
|
|
76
67
|
message: {
|
|
77
68
|
...baseMessage,
|
|
78
69
|
senderId: 'ai-assistant',
|
|
79
|
-
senderType: '
|
|
80
|
-
content:
|
|
81
|
-
|
|
82
|
-
metadata: {
|
|
83
|
-
isAI: true,
|
|
84
|
-
},
|
|
70
|
+
senderType: 'bot',
|
|
71
|
+
content: "Hello! I'm the Xcelsior assistant. Based on our knowledge base, here are some common solutions:\n\n1. Reset your password\n2. Verify your email\n3. Contact support\n\nWhich would you like help with?",
|
|
72
|
+
confidence: 0.85,
|
|
73
|
+
metadata: { isAI: true, kbUsed: true },
|
|
85
74
|
},
|
|
86
75
|
currentUser,
|
|
87
76
|
showAvatar: true,
|
|
@@ -89,15 +78,66 @@ export const AIMessage: Story = {
|
|
|
89
78
|
},
|
|
90
79
|
};
|
|
91
80
|
|
|
92
|
-
/**
|
|
93
|
-
|
|
94
|
-
*/
|
|
95
|
-
export const AIMessageWithMarkdown: Story = {
|
|
81
|
+
/** Bot message with low confidence */
|
|
82
|
+
export const BotMessageLowConfidence: Story = {
|
|
96
83
|
args: {
|
|
97
84
|
message: {
|
|
98
85
|
...baseMessage,
|
|
99
86
|
senderId: 'ai-assistant',
|
|
100
|
-
senderType: '
|
|
87
|
+
senderType: 'bot',
|
|
88
|
+
content: "I'm not entirely sure about this, but let me check with my team. In the meantime, you might want to visit our help center.",
|
|
89
|
+
confidence: 0.3,
|
|
90
|
+
metadata: { isAI: true, kbUsed: false },
|
|
91
|
+
},
|
|
92
|
+
currentUser,
|
|
93
|
+
showAvatar: true,
|
|
94
|
+
showTimestamp: true,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/** Bot message with feedback (rated good) */
|
|
99
|
+
export const BotMessageFeedbackGood: Story = {
|
|
100
|
+
args: {
|
|
101
|
+
message: {
|
|
102
|
+
...baseMessage,
|
|
103
|
+
senderId: 'ai-assistant',
|
|
104
|
+
senderType: 'bot',
|
|
105
|
+
content: "You can reset your password by going to Settings > Security > Change Password.",
|
|
106
|
+
feedback: 'good',
|
|
107
|
+
confidence: 0.9,
|
|
108
|
+
metadata: { isAI: true, kbUsed: true },
|
|
109
|
+
},
|
|
110
|
+
currentUser,
|
|
111
|
+
showAvatar: true,
|
|
112
|
+
showTimestamp: true,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** Bot message with feedback (rated wrong) */
|
|
117
|
+
export const BotMessageFeedbackWrong: Story = {
|
|
118
|
+
args: {
|
|
119
|
+
message: {
|
|
120
|
+
...baseMessage,
|
|
121
|
+
senderId: 'ai-assistant',
|
|
122
|
+
senderType: 'bot',
|
|
123
|
+
content: "Your subscription expires on March 15th.",
|
|
124
|
+
feedback: 'wrong',
|
|
125
|
+
confidence: 0.6,
|
|
126
|
+
metadata: { isAI: true, kbUsed: true },
|
|
127
|
+
},
|
|
128
|
+
currentUser,
|
|
129
|
+
showAvatar: true,
|
|
130
|
+
showTimestamp: true,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/** AI message with markdown formatting */
|
|
135
|
+
export const BotMessageWithMarkdown: Story = {
|
|
136
|
+
args: {
|
|
137
|
+
message: {
|
|
138
|
+
...baseMessage,
|
|
139
|
+
senderId: 'ai-assistant',
|
|
140
|
+
senderType: 'bot',
|
|
101
141
|
content: `## Getting Started
|
|
102
142
|
|
|
103
143
|
Here's how you can reset your password:
|
|
@@ -109,10 +149,8 @@ Here's how you can reset your password:
|
|
|
109
149
|
|
|
110
150
|
*Note: The link expires in 24 hours.*
|
|
111
151
|
|
|
112
|
-
For more help, visit our [support page](https://
|
|
113
|
-
metadata: {
|
|
114
|
-
isAI: true,
|
|
115
|
-
},
|
|
152
|
+
For more help, visit our [support page](https://xcelsior.co/support).`,
|
|
153
|
+
metadata: { isAI: true, kbUsed: true },
|
|
116
154
|
},
|
|
117
155
|
currentUser,
|
|
118
156
|
showAvatar: true,
|
|
@@ -120,9 +158,7 @@ For more help, visit our [support page](https://example.com/support).`,
|
|
|
120
158
|
},
|
|
121
159
|
};
|
|
122
160
|
|
|
123
|
-
/**
|
|
124
|
-
* System message
|
|
125
|
-
*/
|
|
161
|
+
/** System message — generic */
|
|
126
162
|
export const SystemMessage: Story = {
|
|
127
163
|
args: {
|
|
128
164
|
message: {
|
|
@@ -130,6 +166,23 @@ export const SystemMessage: Story = {
|
|
|
130
166
|
senderId: 'system',
|
|
131
167
|
senderType: 'system',
|
|
132
168
|
content: 'Agent John joined the conversation',
|
|
169
|
+
messageType: 'system',
|
|
170
|
+
},
|
|
171
|
+
currentUser,
|
|
172
|
+
showAvatar: true,
|
|
173
|
+
showTimestamp: true,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/** System message — escalation notice */
|
|
178
|
+
export const EscalationNotice: Story = {
|
|
179
|
+
args: {
|
|
180
|
+
message: {
|
|
181
|
+
...baseMessage,
|
|
182
|
+
senderId: 'system',
|
|
183
|
+
senderType: 'system',
|
|
184
|
+
content: 'Connecting you with a team member who can help further...',
|
|
185
|
+
messageType: 'system',
|
|
133
186
|
},
|
|
134
187
|
currentUser,
|
|
135
188
|
showAvatar: true,
|
|
@@ -137,9 +190,23 @@ export const SystemMessage: Story = {
|
|
|
137
190
|
},
|
|
138
191
|
};
|
|
139
192
|
|
|
140
|
-
/**
|
|
141
|
-
|
|
142
|
-
|
|
193
|
+
/** System message — agent took over */
|
|
194
|
+
export const AgentTookOver: Story = {
|
|
195
|
+
args: {
|
|
196
|
+
message: {
|
|
197
|
+
...baseMessage,
|
|
198
|
+
senderId: 'system',
|
|
199
|
+
senderType: 'system',
|
|
200
|
+
content: 'Sarah from our team is here to help!',
|
|
201
|
+
messageType: 'system',
|
|
202
|
+
},
|
|
203
|
+
currentUser,
|
|
204
|
+
showAvatar: true,
|
|
205
|
+
showTimestamp: true,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/** Image message */
|
|
143
210
|
export const ImageMessage: Story = {
|
|
144
211
|
args: {
|
|
145
212
|
message: {
|
|
@@ -153,18 +220,14 @@ export const ImageMessage: Story = {
|
|
|
153
220
|
},
|
|
154
221
|
};
|
|
155
222
|
|
|
156
|
-
/**
|
|
157
|
-
* Message with file attachment
|
|
158
|
-
*/
|
|
223
|
+
/** File message */
|
|
159
224
|
export const FileMessage: Story = {
|
|
160
225
|
args: {
|
|
161
226
|
message: {
|
|
162
227
|
...baseMessage,
|
|
163
228
|
content: 'https://example.com/document.pdf',
|
|
164
229
|
messageType: 'file',
|
|
165
|
-
metadata: {
|
|
166
|
-
fileName: 'Technical Documentation.pdf',
|
|
167
|
-
},
|
|
230
|
+
metadata: { fileName: 'Technical Documentation.pdf' },
|
|
168
231
|
},
|
|
169
232
|
currentUser,
|
|
170
233
|
showAvatar: true,
|
|
@@ -172,19 +235,142 @@ export const FileMessage: Story = {
|
|
|
172
235
|
},
|
|
173
236
|
};
|
|
174
237
|
|
|
175
|
-
/**
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
238
|
+
/** Bot message with rich markdown — bullet list, bold, pricing options */
|
|
239
|
+
export const BotMarkdownMessage: Story = {
|
|
240
|
+
args: {
|
|
241
|
+
message: {
|
|
242
|
+
id: 'md-1',
|
|
243
|
+
conversationId: 'conv-1',
|
|
244
|
+
senderId: 'ai-assistant',
|
|
245
|
+
senderType: 'bot',
|
|
246
|
+
content: `Hi there! I'd be happy to help you get a quote for your project.
|
|
247
|
+
|
|
248
|
+
To provide you with the most accurate quote, I'll need to understand more about what you're looking for. Could you tell me:
|
|
249
|
+
|
|
250
|
+
- **What type of project** are you considering? (mobile app, website, custom software, etc.)
|
|
251
|
+
- **What's the main purpose** or functionality you need?
|
|
252
|
+
- **Any specific requirements** or features you have in mind?
|
|
253
|
+
|
|
254
|
+
At Xcelsior, we offer flexible pricing models to fit your needs:
|
|
255
|
+
|
|
256
|
+
1. Hour-based pricing
|
|
257
|
+
2. Feature-based pricing
|
|
258
|
+
3. Fixed-sum pricing
|
|
259
|
+
|
|
260
|
+
Once I understand your project better, I can connect you with our team for a detailed quote and consultation.`,
|
|
261
|
+
messageType: 'text',
|
|
262
|
+
createdAt: new Date().toISOString(),
|
|
263
|
+
status: 'sent',
|
|
264
|
+
},
|
|
265
|
+
currentUser: {
|
|
266
|
+
name: 'Visitor',
|
|
267
|
+
email: 'visitor@example.com',
|
|
268
|
+
type: 'customer',
|
|
269
|
+
status: 'online',
|
|
270
|
+
},
|
|
271
|
+
showAvatar: true,
|
|
272
|
+
showTimestamp: true,
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/** Bot message with a fenced code block and inline code */
|
|
277
|
+
export const BotCodeBlock: Story = {
|
|
278
|
+
args: {
|
|
279
|
+
message: {
|
|
280
|
+
id: 'md-2',
|
|
281
|
+
conversationId: 'conv-1',
|
|
282
|
+
senderId: 'ai-assistant',
|
|
283
|
+
senderType: 'bot',
|
|
284
|
+
content: `Here's a quick example of how to integrate our SDK:
|
|
285
|
+
|
|
286
|
+
\`\`\`javascript
|
|
287
|
+
import { XcelsiorChat } from '@xcelsior/ui-chat';
|
|
288
|
+
|
|
289
|
+
const config = {
|
|
290
|
+
apiKey: 'your-api-key',
|
|
291
|
+
theme: { primary: '#337eff' }
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
<XcelsiorChat config={config} />
|
|
295
|
+
\`\`\`
|
|
296
|
+
|
|
297
|
+
You can also use inline code like \`npm install @xcelsior/ui-chat\` to get started.
|
|
298
|
+
|
|
299
|
+
For more details, check our **documentation** or reach out to our team!`,
|
|
300
|
+
messageType: 'text',
|
|
301
|
+
createdAt: new Date().toISOString(),
|
|
302
|
+
status: 'sent',
|
|
303
|
+
},
|
|
304
|
+
currentUser: {
|
|
305
|
+
name: 'Visitor',
|
|
306
|
+
email: 'visitor@example.com',
|
|
307
|
+
type: 'customer',
|
|
308
|
+
status: 'online',
|
|
309
|
+
},
|
|
310
|
+
showAvatar: true,
|
|
311
|
+
showTimestamp: true,
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
/** Bot message with headings, nested lists, and a blockquote */
|
|
316
|
+
export const BotLongResponse: Story = {
|
|
317
|
+
args: {
|
|
318
|
+
message: {
|
|
319
|
+
id: 'md-3',
|
|
320
|
+
conversationId: 'conv-1',
|
|
321
|
+
senderId: 'ai-assistant',
|
|
322
|
+
senderType: 'bot',
|
|
323
|
+
content: `Great question! Here's a comprehensive overview of our services:
|
|
324
|
+
|
|
325
|
+
## Web Development
|
|
326
|
+
We build modern, responsive web applications using cutting-edge technologies like **Next.js**, **React**, and **TypeScript**. Our team focuses on performance, accessibility, and SEO.
|
|
327
|
+
|
|
328
|
+
## Mobile Development
|
|
329
|
+
We develop cross-platform mobile apps using **React Native** and **Flutter**, ensuring your app runs smoothly on both iOS and Android.
|
|
330
|
+
|
|
331
|
+
## Digital Solutions
|
|
332
|
+
- Custom CMS development
|
|
333
|
+
- E-commerce platforms
|
|
334
|
+
- API integrations
|
|
335
|
+
- Cloud infrastructure (AWS, GCP)
|
|
336
|
+
|
|
337
|
+
## IT Consulting
|
|
338
|
+
Our experienced consultants help you:
|
|
339
|
+
1. Plan your technology roadmap
|
|
340
|
+
2. Optimize existing systems
|
|
341
|
+
3. Scale your infrastructure
|
|
342
|
+
4. Implement security best practices
|
|
343
|
+
|
|
344
|
+
> "Xcelsior delivered our project on time and exceeded our expectations." — Client Testimonial
|
|
345
|
+
|
|
346
|
+
Would you like to know more about any specific service?`,
|
|
347
|
+
messageType: 'text',
|
|
348
|
+
createdAt: new Date().toISOString(),
|
|
349
|
+
status: 'sent',
|
|
350
|
+
},
|
|
351
|
+
currentUser: {
|
|
352
|
+
name: 'Visitor',
|
|
353
|
+
email: 'visitor@example.com',
|
|
354
|
+
type: 'customer',
|
|
355
|
+
status: 'online',
|
|
356
|
+
},
|
|
357
|
+
showAvatar: true,
|
|
358
|
+
showTimestamp: true,
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
/** Full conversation flow: customer → bot → escalation → agent */
|
|
363
|
+
export const FullConversationFlow: Story = {
|
|
179
364
|
render: (args) => (
|
|
180
|
-
<div className="flex flex-col gap-2 max-w-2xl">
|
|
365
|
+
<div className="flex flex-col gap-2 max-w-2xl p-4 bg-gray-950 rounded-lg">
|
|
181
366
|
<MessageItem
|
|
182
367
|
{...args}
|
|
183
368
|
message={{
|
|
184
369
|
...baseMessage,
|
|
185
370
|
id: '1',
|
|
186
371
|
senderId: currentUser.email,
|
|
187
|
-
|
|
372
|
+
senderType: 'customer',
|
|
373
|
+
content: 'Hi, I need help with pricing for a custom web app.',
|
|
188
374
|
}}
|
|
189
375
|
/>
|
|
190
376
|
<MessageItem
|
|
@@ -193,10 +379,10 @@ export const ConversationExample: Story = {
|
|
|
193
379
|
...baseMessage,
|
|
194
380
|
id: '2',
|
|
195
381
|
senderId: 'ai-assistant',
|
|
196
|
-
senderType: '
|
|
197
|
-
content:
|
|
198
|
-
|
|
199
|
-
metadata: { isAI: true },
|
|
382
|
+
senderType: 'bot',
|
|
383
|
+
content: "Hello! I'd be happy to help with pricing. At Xcelsior, we offer custom quotes based on your project requirements. Could you tell me more about what you're looking for?",
|
|
384
|
+
confidence: 0.8,
|
|
385
|
+
metadata: { isAI: true, kbUsed: true },
|
|
200
386
|
createdAt: new Date(Date.now() - 60000).toISOString(),
|
|
201
387
|
}}
|
|
202
388
|
/>
|
|
@@ -206,7 +392,8 @@ export const ConversationExample: Story = {
|
|
|
206
392
|
...baseMessage,
|
|
207
393
|
id: '3',
|
|
208
394
|
senderId: currentUser.email,
|
|
209
|
-
|
|
395
|
+
senderType: 'customer',
|
|
396
|
+
content: 'I want to talk to a real person about this. Can you connect me with your sales team?',
|
|
210
397
|
createdAt: new Date(Date.now() - 30000).toISOString(),
|
|
211
398
|
}}
|
|
212
399
|
/>
|
|
@@ -215,10 +402,21 @@ export const ConversationExample: Story = {
|
|
|
215
402
|
message={{
|
|
216
403
|
...baseMessage,
|
|
217
404
|
id: '4',
|
|
218
|
-
senderId: '
|
|
405
|
+
senderId: 'system',
|
|
406
|
+
senderType: 'system',
|
|
407
|
+
content: 'Connecting you with a team member who can help further...',
|
|
408
|
+
messageType: 'system',
|
|
409
|
+
createdAt: new Date(Date.now() - 25000).toISOString(),
|
|
410
|
+
}}
|
|
411
|
+
/>
|
|
412
|
+
<MessageItem
|
|
413
|
+
{...args}
|
|
414
|
+
message={{
|
|
415
|
+
...baseMessage,
|
|
416
|
+
id: '5',
|
|
417
|
+
senderId: 'sarah@xcelsior.co',
|
|
219
418
|
senderType: 'agent',
|
|
220
|
-
content:
|
|
221
|
-
"I've sent the password reset link to your registered email address. Please check your inbox and spam folder. Let me know if you need any further assistance!",
|
|
419
|
+
content: "Hi there! I'm Sarah from our sales team. I'd love to discuss your custom web app project. Can you share a bit about the features you need and your timeline?",
|
|
222
420
|
createdAt: new Date().toISOString(),
|
|
223
421
|
}}
|
|
224
422
|
/>
|