@xcelsior/ui-chat 1.0.8 → 2.0.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.
Files changed (96) hide show
  1. package/dist/index.d.mts +69 -69
  2. package/dist/index.d.ts +69 -69
  3. package/dist/index.js +2458 -627
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +2457 -628
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +6 -5
  8. package/src/components/BrandIcons.stories.tsx +95 -0
  9. package/src/components/BrandIcons.tsx +84 -0
  10. package/src/components/Chat.stories.tsx +149 -16
  11. package/src/components/Chat.tsx +116 -96
  12. package/src/components/ChatHeader.tsx +124 -69
  13. package/src/components/ChatInput.tsx +253 -104
  14. package/src/components/ChatWidget.tsx +209 -63
  15. package/src/components/ConversationRating.stories.tsx +33 -0
  16. package/src/components/ConversationRating.tsx +156 -0
  17. package/src/components/MarkdownMessage.tsx +202 -0
  18. package/src/components/MessageItem.stories.tsx +253 -55
  19. package/src/components/MessageItem.tsx +223 -60
  20. package/src/components/MessageList.tsx +164 -35
  21. package/src/components/PreChatForm.tsx +236 -96
  22. package/src/components/ThinkingIndicator.tsx +370 -0
  23. package/src/components/TypingIndicator.tsx +27 -11
  24. package/src/hooks/useDraggablePosition.ts +91 -0
  25. package/src/hooks/useMessages.ts +12 -13
  26. package/src/hooks/useResizableWidget.ts +324 -0
  27. package/src/index.tsx +5 -0
  28. package/src/types.ts +51 -5
  29. package/src/utils/markdown-styles.ts +140 -0
  30. package/storybook-static/assets/BrandIcons-Cjy5INAp.js +4 -0
  31. package/storybook-static/assets/BrandIcons.stories-BeVC6svr.js +64 -0
  32. package/storybook-static/assets/Chat.stories-J_Yp51wU.js +803 -0
  33. package/storybook-static/assets/Color-YHDXOIA2-BMnd3YrF.js +1 -0
  34. package/storybook-static/assets/ConversationRating.stories-B5_QddHN.js +12 -0
  35. package/storybook-static/assets/DocsRenderer-CFRXHY34-i_W8iCu9.js +575 -0
  36. package/storybook-static/assets/MessageItem-DAaKZ9s9.js +14 -0
  37. package/storybook-static/assets/MessageItem.stories-Ckr1_scc.js +255 -0
  38. package/storybook-static/assets/ToastContext-Bty1K7ya.js +1 -0
  39. package/storybook-static/assets/chunk-XP5HYGXS-BpfKkqn7.js +1 -0
  40. package/storybook-static/assets/en-US-BukEqXxE.js +1 -0
  41. package/storybook-static/assets/entry-preview-docs-DHohToDm.js +46 -0
  42. package/storybook-static/assets/entry-preview-oDnntGcx.js +2 -0
  43. package/storybook-static/assets/iframe-CGBtu2Se.js +211 -0
  44. package/storybook-static/assets/index--qcDGAq6.js +1 -0
  45. package/storybook-static/assets/index-BLHw34Di.js +24 -0
  46. package/storybook-static/assets/index-B_4m48Mv.js +1 -0
  47. package/storybook-static/assets/index-DgH-xKnr.js +11 -0
  48. package/storybook-static/assets/index-DrFu-skq.js +6 -0
  49. package/storybook-static/assets/index-DrdPSA1J.js +240 -0
  50. package/storybook-static/assets/index-jvNEZhzf.js +1 -0
  51. package/storybook-static/assets/index-yBjzXJbu.js +9 -0
  52. package/storybook-static/assets/jsx-runtime-Cf8x2fCZ.js +9 -0
  53. package/storybook-static/assets/preview-B8lJiyuQ.js +34 -0
  54. package/storybook-static/assets/preview-BBWR9nbA.js +1 -0
  55. package/storybook-static/assets/preview-BRpahs9B.js +2 -0
  56. package/storybook-static/assets/preview-BWzBA1C2.js +396 -0
  57. package/storybook-static/assets/preview-CvbIS5ZJ.js +1 -0
  58. package/storybook-static/assets/preview-DD_OYowb.js +1 -0
  59. package/storybook-static/assets/preview-DGUiP6tS.js +7 -0
  60. package/storybook-static/assets/preview-DHQbi4pV.js +1 -0
  61. package/storybook-static/assets/preview-DUOvJmsz.js +1 -0
  62. package/storybook-static/assets/preview-DcGwT3kv.css +1 -0
  63. package/storybook-static/assets/preview-DwI0w3cI.js +1 -0
  64. package/storybook-static/assets/react-18-CALspjOX.js +1 -0
  65. package/storybook-static/assets/test-utils-BE0XkMtV.js +9 -0
  66. package/storybook-static/favicon.svg +1 -0
  67. package/storybook-static/iframe.html +666 -0
  68. package/storybook-static/index.html +177 -0
  69. package/storybook-static/index.json +1 -0
  70. package/storybook-static/nunito-sans-bold-italic.woff2 +0 -0
  71. package/storybook-static/nunito-sans-bold.woff2 +0 -0
  72. package/storybook-static/nunito-sans-italic.woff2 +0 -0
  73. package/storybook-static/nunito-sans-regular.woff2 +0 -0
  74. package/storybook-static/project.json +1 -0
  75. package/storybook-static/sb-addons/essentials-actions-3/manager-bundle.js +3 -0
  76. package/storybook-static/sb-addons/essentials-backgrounds-5/manager-bundle.js +12 -0
  77. package/storybook-static/sb-addons/essentials-controls-2/manager-bundle.js +405 -0
  78. package/storybook-static/sb-addons/essentials-docs-4/manager-bundle.js +245 -0
  79. package/storybook-static/sb-addons/essentials-measure-8/manager-bundle.js +3 -0
  80. package/storybook-static/sb-addons/essentials-outline-9/manager-bundle.js +3 -0
  81. package/storybook-static/sb-addons/essentials-toolbars-7/manager-bundle.js +3 -0
  82. package/storybook-static/sb-addons/essentials-viewport-6/manager-bundle.js +3 -0
  83. package/storybook-static/sb-addons/interactions-10/manager-bundle.js +222 -0
  84. package/storybook-static/sb-addons/links-1/manager-bundle.js +3 -0
  85. package/storybook-static/sb-addons/storybook-core-core-server-presets-0/common-manager-bundle.js +3 -0
  86. package/storybook-static/sb-common-assets/favicon.svg +1 -0
  87. package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
  88. package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
  89. package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
  90. package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
  91. package/storybook-static/sb-manager/globals-module-info.js +1052 -0
  92. package/storybook-static/sb-manager/globals-runtime.js +42127 -0
  93. package/storybook-static/sb-manager/globals.js +48 -0
  94. package/storybook-static/sb-manager/runtime.js +12048 -0
  95. package/.turbo/turbo-build.log +0 -22
  96. package/.turbo/turbo-lint.log +0 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xcelsior/ui-chat",
3
- "version": "1.0.8",
3
+ "version": "2.0.1",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "license": "MIT",
@@ -22,12 +22,12 @@
22
22
  "@tailwindcss/postcss": "^4.1.13",
23
23
  "tsup": "^8.0.2",
24
24
  "typescript": "^5.3.3",
25
- "@xcelsior/design-system": "1.0.7",
26
- "@xcelsior/ui-fields": "1.0.4"
25
+ "@xcelsior/design-system": "1.0.11",
26
+ "@xcelsior/ui-fields": "1.0.6"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "tailwindcss": "^4",
30
- "@xcelsior/design-system": "^1.0.7",
30
+ "@xcelsior/design-system": "^1.0.8",
31
31
  "@xcelsior/ui-fields": "^1.0.4",
32
32
  "flowbite-react": "^0.12",
33
33
  "react": "^18.2.0",
@@ -43,7 +43,8 @@
43
43
  },
44
44
  "exports": {
45
45
  ".": {
46
- "import": "./src/index.tsx",
46
+ "types": "./dist/index.d.ts",
47
+ "import": "./dist/index.mjs",
47
48
  "require": "./dist/index.js"
48
49
  }
49
50
  },
@@ -0,0 +1,95 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { XcelsiorSymbol, XcelsiorAvatar } from './BrandIcons';
3
+
4
+ const meta: Meta<typeof XcelsiorSymbol> = {
5
+ title: 'Brand/Icons',
6
+ parameters: {
7
+ layout: 'padded',
8
+ },
9
+ tags: ['autodocs'],
10
+ };
11
+
12
+ export default meta;
13
+
14
+ /** Xcelsior X symbol at various sizes */
15
+ export const SymbolSizes: StoryObj = {
16
+ render: () => (
17
+ <div className="flex items-center gap-6 p-4">
18
+ <div className="flex flex-col items-center gap-2">
19
+ <XcelsiorSymbol size={16} color="#337eff" />
20
+ <span className="text-xs text-gray-500">16px</span>
21
+ </div>
22
+ <div className="flex flex-col items-center gap-2">
23
+ <XcelsiorSymbol size={24} color="#337eff" />
24
+ <span className="text-xs text-gray-500">24px</span>
25
+ </div>
26
+ <div className="flex flex-col items-center gap-2">
27
+ <XcelsiorSymbol size={32} color="#337eff" />
28
+ <span className="text-xs text-gray-500">32px</span>
29
+ </div>
30
+ <div className="flex flex-col items-center gap-2">
31
+ <XcelsiorSymbol size={48} color="#337eff" />
32
+ <span className="text-xs text-gray-500">48px</span>
33
+ </div>
34
+ </div>
35
+ ),
36
+ };
37
+
38
+ /** Symbol on dark backgrounds */
39
+ export const SymbolOnDark: StoryObj = {
40
+ render: () => (
41
+ <div className="flex items-center gap-6 p-6 bg-gray-900 rounded-lg">
42
+ <XcelsiorSymbol size={24} color="white" />
43
+ <XcelsiorSymbol size={32} color="white" />
44
+ <XcelsiorSymbol size={48} color="white" />
45
+ </div>
46
+ ),
47
+ };
48
+
49
+ /** Xcelsior avatar (gradient circle with symbol) at various sizes */
50
+ export const AvatarSizes: StoryObj = {
51
+ render: () => (
52
+ <div className="flex items-center gap-6 p-4">
53
+ <div className="flex flex-col items-center gap-2">
54
+ <XcelsiorAvatar size={24} />
55
+ <span className="text-xs text-gray-500">24px</span>
56
+ </div>
57
+ <div className="flex flex-col items-center gap-2">
58
+ <XcelsiorAvatar size={32} />
59
+ <span className="text-xs text-gray-500">32px</span>
60
+ </div>
61
+ <div className="flex flex-col items-center gap-2">
62
+ <XcelsiorAvatar size={40} />
63
+ <span className="text-xs text-gray-500">40px (default)</span>
64
+ </div>
65
+ <div className="flex flex-col items-center gap-2">
66
+ <XcelsiorAvatar size={56} />
67
+ <span className="text-xs text-gray-500">56px (FAB)</span>
68
+ </div>
69
+ </div>
70
+ ),
71
+ };
72
+
73
+ /** Avatar in context — message list style */
74
+ export const AvatarInContext: StoryObj = {
75
+ render: () => (
76
+ <div className="flex flex-col gap-3 max-w-md p-4 bg-gray-900 rounded-lg">
77
+ <div className="flex items-start gap-3">
78
+ <XcelsiorAvatar size={32} />
79
+ <div className="bg-gray-800 rounded-lg px-3 py-2 text-sm text-white">
80
+ <p className="text-xs text-blue-400 mb-1">AI Assistant</p>
81
+ Hello! How can I help you today?
82
+ </div>
83
+ </div>
84
+ <div className="flex items-start gap-3">
85
+ <div className="w-8 h-8 rounded-full bg-green-600 flex items-center justify-center text-white text-sm font-medium">
86
+ S
87
+ </div>
88
+ <div className="bg-gray-800 rounded-lg px-3 py-2 text-sm text-white">
89
+ <p className="text-xs text-green-400 mb-1">Sarah (Agent)</p>
90
+ I&apos;m here to help with your inquiry!
91
+ </div>
92
+ </div>
93
+ </div>
94
+ ),
95
+ };
@@ -0,0 +1,84 @@
1
+ import type { CSSProperties } from 'react';
2
+
3
+ interface BrandIconProps {
4
+ size?: number;
5
+ color?: string;
6
+ className?: string;
7
+ style?: CSSProperties;
8
+ }
9
+
10
+ /**
11
+ * Xcelsior "X" symbol mark (from favicon-brand.svg)
12
+ * Reusable across the chat widget — FAB button, avatar, header, etc.
13
+ */
14
+ export function XcelsiorSymbol({ size = 24, color = 'white', className = '', style }: BrandIconProps) {
15
+ return (
16
+ <svg
17
+ width={size}
18
+ height={size}
19
+ viewBox="0 0 32 32"
20
+ fill="none"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ className={className}
23
+ style={style}
24
+ aria-hidden="true"
25
+ >
26
+ <path
27
+ 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"
28
+ fill={color}
29
+ />
30
+ <path
31
+ d="M14.313 15.027H18.402L7.135 0.036H0.185L9.406 12.392C10.587 13.983 12.359 15.027 14.313 15.027Z"
32
+ fill={color}
33
+ />
34
+ <path
35
+ d="M0.185 31.837H7.135L18.402 16.846H14.313C12.359 16.846 10.588 17.891 9.406 19.481L0.185 31.837Z"
36
+ fill={color}
37
+ />
38
+ </svg>
39
+ );
40
+ }
41
+
42
+ /**
43
+ * Chat bubble icon for the FAB launcher button.
44
+ * Instantly recognizable as a chat trigger.
45
+ */
46
+ export function ChatBubbleIcon({ size = 28, color = 'white', className = '', style }: BrandIconProps) {
47
+ return (
48
+ <svg
49
+ width={size}
50
+ height={size}
51
+ viewBox="0 0 24 24"
52
+ fill="none"
53
+ stroke={color}
54
+ strokeWidth={2}
55
+ strokeLinecap="round"
56
+ strokeLinejoin="round"
57
+ className={className}
58
+ style={style}
59
+ aria-hidden="true"
60
+ >
61
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
62
+ </svg>
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Xcelsior "X" avatar — symbol inside a gradient circle
68
+ * Used as bot avatar in messages and chat header
69
+ */
70
+ export function XcelsiorAvatar({ size = 40, className = '' }: { size?: number; className?: string }) {
71
+ const iconSize = Math.round(size * 0.55);
72
+ return (
73
+ <div
74
+ className={`flex items-center justify-center rounded-full ${className}`}
75
+ style={{
76
+ width: size,
77
+ height: size,
78
+ background: 'linear-gradient(135deg, #337eff, #005eff)',
79
+ }}
80
+ >
81
+ <XcelsiorSymbol size={iconSize} color="white" />
82
+ </div>
83
+ );
84
+ }
@@ -1,6 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react';
2
2
  import { Chat } from './Chat';
3
- import type { IChatConfig } from '../types';
3
+
4
+ import type { IChatConfig, IChatTheme } from '../types';
4
5
 
5
6
  const meta: Meta<typeof Chat> = {
6
7
  title: 'Components/Chat',
@@ -14,29 +15,33 @@ const meta: Meta<typeof Chat> = {
14
15
  export default meta;
15
16
  type Story = StoryObj<typeof Chat>;
16
17
 
17
- // Base configuration without user details
18
- const baseConfig: Omit<IChatConfig, 'currentUser' | 'conversationId' | 'userId'> = {
18
+ const xcelsiorTheme: IChatTheme = {
19
+ primary: '#337eff',
20
+ primaryStrong: '#005eff',
21
+ background: '#02164a',
22
+ backgroundAlt: '#00001a',
23
+ text: '#f7f7f8',
24
+ textMuted: 'rgba(247, 247, 248, 0.6)',
25
+ statusPositive: '#1ed473',
26
+ statusCaution: '#ffa938',
27
+ statusNegative: '#ff6363',
28
+ borderColor: 'rgba(255, 255, 255, 0.1)',
29
+ };
30
+
31
+ const baseConfig: Omit<IChatConfig, 'conversationId'> = {
19
32
  websocketUrl: 'wss://dev-ws-chat.xcelsior.co',
20
33
  httpApiUrl: 'https://dev-chat.xcelsior.co/api',
21
34
  enableEmoji: true,
22
35
  enableFileUpload: true,
23
36
  enableTypingIndicator: true,
24
37
  fileUpload: {
25
- uploadUrl: `https://dev-chat.xcelsior.co/api/attachments/upload-url`,
26
- maxFileSize: 2 * 1024 * 1024, // 2MB (default)
27
- allowedTypes: [
28
- 'image/jpeg',
29
- 'image/png',
30
- 'image/gif',
31
- 'image/webp',
32
- 'application/pdf',
33
- 'application/msword',
34
- 'text/plain',
35
- 'text/csv',
36
- ],
38
+ uploadUrl: 'https://dev-chat.xcelsior.co/api/attachments/upload-url',
39
+ maxFileSize: 2 * 1024 * 1024,
40
+ allowedTypes: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf', 'text/plain'],
37
41
  },
38
42
  enableReadReceipts: true,
39
43
  apiKey: 'test',
44
+ theme: xcelsiorTheme,
40
45
  toast: {
41
46
  success: (message: string) => console.log('Success:', message),
42
47
  error: (message: string) => console.error('Error:', message),
@@ -45,10 +50,138 @@ const baseConfig: Omit<IChatConfig, 'currentUser' | 'conversationId' | 'userId'>
45
50
  };
46
51
 
47
52
  /**
48
- * Default story - Shows pre-chat form first
53
+ * Progressive identity mode (default) — no pre-chat form, bot collects info during conversation
54
+ */
55
+ export const ProgressiveIdentity: Story = {
56
+ args: {
57
+ config: {
58
+ ...baseConfig,
59
+ identityCollection: 'progressive',
60
+ },
61
+ },
62
+ };
63
+
64
+ /**
65
+ * Form identity mode — shows pre-chat form before chat
66
+ */
67
+ export const FormIdentity: Story = {
68
+ args: {
69
+ config: {
70
+ ...baseConfig,
71
+ identityCollection: 'form',
72
+ },
73
+ },
74
+ };
75
+
76
+ /**
77
+ * With known user — skips form/anonymous, goes straight to chat
78
+ */
79
+ export const WithKnownUser: Story = {
80
+ args: {
81
+ config: {
82
+ ...baseConfig,
83
+ identityCollection: 'progressive',
84
+ currentUser: {
85
+ name: 'John Doe',
86
+ email: 'john@example.com',
87
+ type: 'customer',
88
+ status: 'online',
89
+ },
90
+ },
91
+ },
92
+ };
93
+
94
+ /**
95
+ * Widget positioned on the left
96
+ */
97
+ export const PositionLeft: Story = {
98
+ args: {
99
+ config: {
100
+ ...baseConfig,
101
+ identityCollection: 'progressive',
102
+ position: 'left',
103
+ },
104
+ },
105
+ };
106
+
107
+ /**
108
+ * Widget positioned on the right (explicit)
109
+ */
110
+ export const PositionRight: Story = {
111
+ args: {
112
+ config: {
113
+ ...baseConfig,
114
+ identityCollection: 'progressive',
115
+ position: 'right',
116
+ },
117
+ },
118
+ };
119
+
120
+ /**
121
+ * Auto position — reads from localStorage, falls back to right
122
+ */
123
+ export const PositionAuto: Story = {
124
+ args: {
125
+ config: {
126
+ ...baseConfig,
127
+ identityCollection: 'progressive',
128
+ position: 'auto',
129
+ },
130
+ },
131
+ };
132
+
133
+ /**
134
+ * Light theme variant
135
+ */
136
+ export const LightTheme: Story = {
137
+ args: {
138
+ config: {
139
+ ...baseConfig,
140
+ identityCollection: 'progressive',
141
+ theme: {
142
+ primary: '#005eff',
143
+ primaryStrong: '#003ecc',
144
+ background: '#ffffff',
145
+ backgroundAlt: '#f7f7f8',
146
+ text: '#171719',
147
+ textMuted: 'rgba(23, 23, 25, 0.5)',
148
+ statusPositive: '#1ed473',
149
+ statusCaution: '#ffa938',
150
+ statusNegative: '#ff6363',
151
+ borderColor: 'rgba(0, 0, 0, 0.1)',
152
+ },
153
+ },
154
+ },
155
+ };
156
+
157
+ /**
158
+ * Default (dark theme Xcelsior branded)
49
159
  */
50
160
  export const Default: Story = {
51
161
  args: {
52
162
  config: baseConfig,
53
163
  },
54
164
  };
165
+
166
+ /**
167
+ * Starts in open state
168
+ */
169
+ export const OpenByDefault: Story = {
170
+ args: {
171
+ config: baseConfig,
172
+ defaultState: 'open',
173
+ },
174
+ };
175
+
176
+ /**
177
+ * Resizable widget — drag the bottom-right corner to resize
178
+ */
179
+ export const Resizable: Story = {
180
+ args: {
181
+ config: {
182
+ ...baseConfig,
183
+ identityCollection: 'progressive',
184
+ },
185
+ defaultState: 'open',
186
+ },
187
+ };