@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
@@ -1,10 +1,21 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
  import { createPortal } from 'react-dom';
3
- import { Button, TextArea } from '@xcelsior/design-system';
4
3
  import Picker from '@emoji-mart/react';
5
4
  import type { IChatConfig } from '../types';
6
5
  import type { UseFileUploadReturn } from '../hooks/useFileUpload';
7
6
 
7
+ /** Detect if a hex/rgb color is "light" (luminance > 0.5) */
8
+ function isLightColor(color: string): boolean {
9
+ let r = 0, g = 0, b = 0;
10
+ if (color.startsWith('#')) {
11
+ const hex = color.replace('#', '');
12
+ r = parseInt(hex.substring(0, 2), 16);
13
+ g = parseInt(hex.substring(2, 4), 16);
14
+ b = parseInt(hex.substring(4, 6), 16);
15
+ }
16
+ return (0.299 * r + 0.587 * g + 0.114 * b) / 255 > 0.5;
17
+ }
18
+
8
19
  interface ChatInputProps {
9
20
  onSend: (message: string) => void;
10
21
  onTyping?: (isTyping: boolean) => void;
@@ -27,6 +38,7 @@ export function ChatInput({
27
38
  top: number;
28
39
  left: number;
29
40
  } | null>(null);
41
+ const [isFocused, setIsFocused] = useState(false);
30
42
  const textAreaRef = useRef<HTMLTextAreaElement>(null);
31
43
  const emojiPickerRef = useRef<HTMLDivElement>(null);
32
44
  const emojiButtonRef = useRef<HTMLButtonElement>(null);
@@ -38,6 +50,13 @@ export function ChatInput({
38
50
  const enableEmoji = config.enableEmoji ?? true;
39
51
  const enableFileUpload = config.enableFileUpload ?? true;
40
52
 
53
+ // Theme tokens — aligned with Xcelsior website design system
54
+ const bgColor = config.theme?.background || '#00001a';
55
+ const isLightTheme = isLightColor(bgColor);
56
+ const textColor = config.theme?.text || (isLightTheme ? '#1a1a2e' : '#f7f7f8');
57
+ const textMuted = config.theme?.textMuted || (isLightTheme ? 'rgba(0,0,0,0.4)' : 'rgba(247,247,248,0.45)');
58
+ const primaryColor = config.theme?.primary || '#337eff';
59
+
41
60
  // Load emoji data
42
61
  useEffect(() => {
43
62
  if (!enableEmoji) return;
@@ -123,7 +142,7 @@ export function ChatInput({
123
142
  startTypingTimeoutRef.current = setTimeout(() => {
124
143
  onTyping(true);
125
144
  isTypingRef.current = true;
126
- }, 300); // 300ms debounce for start typing
145
+ }, 300);
127
146
  }
128
147
 
129
148
  // Set timeout to stop typing indicator after inactivity
@@ -132,7 +151,7 @@ export function ChatInput({
132
151
  onTyping(false);
133
152
  isTypingRef.current = false;
134
153
  }
135
- }, 1500); // 1.5s of inactivity to stop typing
154
+ }, 1500);
136
155
  }
137
156
  };
138
157
 
@@ -146,14 +165,12 @@ export function ChatInput({
146
165
 
147
166
  // Stop typing indicator
148
167
  if (onTyping) {
149
- // Clear all typing-related timeouts
150
168
  if (typingTimeoutRef.current) {
151
169
  clearTimeout(typingTimeoutRef.current);
152
170
  }
153
171
  if (startTypingTimeoutRef.current) {
154
172
  clearTimeout(startTypingTimeoutRef.current);
155
173
  }
156
- // Send stop typing event if currently typing
157
174
  if (isTypingRef.current) {
158
175
  onTyping(false);
159
176
  isTypingRef.current = false;
@@ -214,114 +231,241 @@ export function ChatInput({
214
231
  }
215
232
  };
216
233
 
217
- return (
218
- <div className="border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 p-3">
219
- <div className="relative flex-1">
220
- <div className="relative">
221
- <TextArea
222
- ref={textAreaRef}
223
- value={message}
224
- onChange={(e) => handleTyping(e.target.value)}
225
- onKeyDown={handleKeyDown}
226
- placeholder="Type a message..."
227
- rows={1}
228
- className="resize-none pr-24 pl-4 py-3 rounded-full bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 text-sm leading-5 placeholder-gray-500 dark:placeholder-gray-400"
229
- disabled={disabled}
230
- />
231
-
232
- {/* Actions inside the input on the right */}
233
- <div className="absolute right-12 top-1/2 -translate-y-1/2 flex items-center gap-1">
234
- {enableEmoji && (
235
- <div className="relative">
236
- <button
237
- ref={emojiButtonRef}
238
- type="button"
239
- onClick={() => {
240
- if (!showEmojiPicker && emojiButtonRef.current) {
241
- const rect =
242
- emojiButtonRef.current.getBoundingClientRect();
243
- setEmojiPickerPosition({
244
- top: rect.top - 450,
245
- left: rect.left - 290,
246
- });
247
- }
248
- setShowEmojiPicker((v) => !v);
249
- }}
250
- className="p-1.5 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
251
- disabled={disabled}
252
- aria-label="Add emoji"
253
- >
254
- <span className="text-lg">😊</span>
255
- </button>
256
- </div>
257
- )}
258
-
259
- {enableFileUpload && fileUpload.canUpload && (
260
- <>
261
- <button
262
- type="button"
263
- onClick={() => fileInputRef.current?.click()}
264
- className="p-1.5 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
265
- disabled={disabled || fileUpload.isUploading}
266
- aria-label="Attach file"
267
- >
268
- {fileUpload.isUploading ? (
269
- <span className="text-lg animate-spin">⏳</span>
270
- ) : (
271
- <span className="text-lg">📎</span>
272
- )}
273
- </button>
274
- <input
275
- ref={fileInputRef}
276
- type="file"
277
- accept="image/*,application/pdf,.doc,.docx"
278
- className="hidden"
279
- onChange={handleFileSelect}
280
- />
281
- </>
282
- )}
283
- </div>
234
+ const canSend = message.trim().length > 0 && !disabled;
235
+
236
+ const placeholderColor = isLightTheme ? 'rgba(0,0,0,0.35)' : 'rgba(247,247,248,0.3)';
284
237
 
285
- {/* Send button positioned inside the input at far right */}
286
- <div className="absolute right-2 top-1/2 -translate-y-1/2">
287
- <Button
288
- onClick={handleSend}
289
- disabled={!message.trim() || disabled}
290
- variant="primary"
291
- size="sm"
292
- className="h-9 w-9 p-0 rounded-full flex items-center justify-center shadow-sm"
238
+ return (
239
+ <div
240
+ className="px-4 py-3"
241
+ style={{
242
+ borderTop: isLightTheme
243
+ ? '1px solid rgba(0,0,0,0.06)'
244
+ : '1px solid rgba(255,255,255,0.06)',
245
+ }}
246
+ >
247
+ <div
248
+ className="relative flex items-center rounded-full transition-all duration-200"
249
+ style={{
250
+ backgroundColor: isLightTheme
251
+ ? 'rgba(112,115,124,0.08)'
252
+ : 'rgba(112,115,124,0.12)',
253
+ outline: isFocused ? `2px solid ${primaryColor}` : 'none',
254
+ outlineOffset: '-1px',
255
+ border: isLightTheme
256
+ ? '1px solid rgba(0,0,0,0.1)'
257
+ : '1px solid rgba(255,255,255,0.08)',
258
+ backdropFilter: 'blur(32px)',
259
+ }}
260
+ >
261
+ {/* Placeholder color via inline style element */}
262
+ <style>{`.xchat-input::placeholder { color: ${placeholderColor}; }`}</style>
263
+ <textarea
264
+ ref={textAreaRef}
265
+ value={message}
266
+ onChange={(e) => handleTyping(e.target.value)}
267
+ onKeyDown={handleKeyDown}
268
+ onFocus={() => setIsFocused(true)}
269
+ onBlur={() => setIsFocused(false)}
270
+ placeholder="Type a message..."
271
+ rows={1}
272
+ className="xchat-input flex-1 resize-none bg-transparent"
273
+ style={{
274
+ color: textColor,
275
+ border: 'none',
276
+ outline: 'none',
277
+ boxShadow: 'none',
278
+ WebkitAppearance: 'none',
279
+ padding: '10px 0 10px 20px',
280
+ minHeight: '42px',
281
+ maxHeight: '120px',
282
+ caretColor: primaryColor,
283
+ fontSize: '14px',
284
+ lineHeight: '20px',
285
+ letterSpacing: '0.006em',
286
+ }}
287
+ disabled={disabled}
288
+ />
289
+
290
+ {/* Action buttons row */}
291
+ <div className="flex items-center gap-1 px-2 shrink-0">
292
+ {enableEmoji && (
293
+ <button
294
+ ref={emojiButtonRef}
295
+ type="button"
296
+ onClick={() => {
297
+ if (!showEmojiPicker && emojiButtonRef.current) {
298
+ const rect = emojiButtonRef.current.getBoundingClientRect();
299
+ setEmojiPickerPosition({
300
+ top: rect.top - 450,
301
+ left: rect.left - 290,
302
+ });
303
+ }
304
+ setShowEmojiPicker((v) => !v);
305
+ }}
306
+ className="p-1.5 rounded-lg transition-all duration-150"
307
+ style={{
308
+ color: textMuted,
309
+ backgroundColor: 'transparent',
310
+ }}
311
+ onMouseEnter={(e) => {
312
+ e.currentTarget.style.backgroundColor = isLightTheme ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.06)';
313
+ e.currentTarget.style.color = textColor;
314
+ }}
315
+ onMouseLeave={(e) => {
316
+ e.currentTarget.style.backgroundColor = 'transparent';
317
+ e.currentTarget.style.color = textMuted;
318
+ }}
319
+ disabled={disabled}
320
+ aria-label="Add emoji"
293
321
  >
294
- <span className="flex items-center justify-center">
295
- <svg
296
- className="w-4 h-4 rotate-90"
297
- fill="none"
298
- viewBox="0 0 24 24"
299
- stroke="currentColor"
300
- aria-hidden="true"
301
- >
302
- <title>Send icon</title>
303
- <path
322
+ <svg
323
+ width="18"
324
+ height="18"
325
+ viewBox="0 0 24 24"
326
+ fill="none"
327
+ stroke="currentColor"
328
+ strokeWidth="1.75"
329
+ strokeLinecap="round"
330
+ strokeLinejoin="round"
331
+ aria-hidden="true"
332
+ >
333
+ <title>Emoji</title>
334
+ <circle cx="12" cy="12" r="10" />
335
+ <path d="M8 14s1.5 2 4 2 4-2 4-2" />
336
+ <line x1="9" y1="9" x2="9.01" y2="9" />
337
+ <line x1="15" y1="9" x2="15.01" y2="9" />
338
+ </svg>
339
+ </button>
340
+ )}
341
+
342
+ {enableFileUpload && fileUpload.canUpload && (
343
+ <>
344
+ <button
345
+ type="button"
346
+ onClick={() => fileInputRef.current?.click()}
347
+ className="p-1.5 rounded-lg transition-all duration-150"
348
+ style={{
349
+ color: textMuted,
350
+ backgroundColor: 'transparent',
351
+ }}
352
+ onMouseEnter={(e) => {
353
+ e.currentTarget.style.backgroundColor =
354
+ 'rgba(255,255,255,0.06)';
355
+ e.currentTarget.style.color = textColor;
356
+ }}
357
+ onMouseLeave={(e) => {
358
+ e.currentTarget.style.backgroundColor = 'transparent';
359
+ e.currentTarget.style.color = textMuted;
360
+ }}
361
+ disabled={disabled || fileUpload.isUploading}
362
+ aria-label="Attach file"
363
+ >
364
+ {fileUpload.isUploading ? (
365
+ <svg
366
+ width="18"
367
+ height="18"
368
+ viewBox="0 0 24 24"
369
+ fill="none"
370
+ stroke="currentColor"
371
+ strokeWidth="1.75"
372
+ className="animate-spin"
373
+ aria-hidden="true"
374
+ >
375
+ <title>Uploading</title>
376
+ <path d="M21 12a9 9 0 11-6.219-8.56" />
377
+ </svg>
378
+ ) : (
379
+ <svg
380
+ width="18"
381
+ height="18"
382
+ viewBox="0 0 24 24"
383
+ fill="none"
384
+ stroke="currentColor"
385
+ strokeWidth="1.75"
304
386
  strokeLinecap="round"
305
387
  strokeLinejoin="round"
306
- strokeWidth={2}
307
- d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
308
- />
309
- </svg>
310
- </span>
311
- </Button>
312
- </div>
388
+ aria-hidden="true"
389
+ >
390
+ <title>Attach file</title>
391
+ <path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" />
392
+ </svg>
393
+ )}
394
+ </button>
395
+ <input
396
+ ref={fileInputRef}
397
+ type="file"
398
+ accept="image/*,application/pdf,.doc,.docx"
399
+ className="hidden"
400
+ onChange={handleFileSelect}
401
+ />
402
+ </>
403
+ )}
404
+
405
+ {/* Send button — gradient pill matching website CTA style */}
406
+ <button
407
+ type="button"
408
+ onClick={handleSend}
409
+ disabled={!canSend}
410
+ className="p-1.5 rounded-lg transition-all duration-200"
411
+ style={{
412
+ background: canSend
413
+ ? `linear-gradient(135deg, ${primaryColor}, ${config.theme?.primaryStrong || '#005eff'})`
414
+ : 'transparent',
415
+ color: canSend ? '#ffffff' : textMuted,
416
+ opacity: canSend ? 1 : 0.35,
417
+ cursor: canSend ? 'pointer' : 'default',
418
+ boxShadow: canSend
419
+ ? `0 2px 8px -2px ${primaryColor}60`
420
+ : 'none',
421
+ }}
422
+ aria-label="Send message"
423
+ >
424
+ <svg
425
+ width="18"
426
+ height="18"
427
+ viewBox="0 0 24 24"
428
+ fill="none"
429
+ stroke="currentColor"
430
+ strokeWidth="2"
431
+ strokeLinecap="round"
432
+ strokeLinejoin="round"
433
+ aria-hidden="true"
434
+ >
435
+ <title>Send</title>
436
+ <line x1="22" y1="2" x2="11" y2="13" />
437
+ <polygon points="22 2 15 22 11 13 2 9 22 2" />
438
+ </svg>
439
+ </button>
313
440
  </div>
314
441
  </div>
315
442
 
443
+ {/* Upload progress */}
316
444
  {fileUpload.isUploading && (
317
- <div className="mt-2">
318
- <div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-1.5">
445
+ <div className="mt-2 px-1">
446
+ <div
447
+ className="w-full rounded-full overflow-hidden"
448
+ style={{
449
+ height: 3,
450
+ backgroundColor: isLightTheme ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.06)',
451
+ }}
452
+ >
319
453
  <div
320
- className="bg-blue-600 h-1.5 rounded-full transition-all duration-300"
321
- style={{ width: `${fileUpload.uploadProgress}%` }}
454
+ className="h-full rounded-full transition-all duration-300"
455
+ style={{
456
+ width: `${fileUpload.uploadProgress}%`,
457
+ background: `linear-gradient(90deg, ${primaryColor}, ${config.theme?.primaryStrong || '#005eff'})`,
458
+ }}
322
459
  />
323
460
  </div>
324
- <p className="text-xs text-gray-600 dark:text-gray-400 mt-1">
461
+ <p
462
+ className="mt-1"
463
+ style={{
464
+ fontSize: '11px',
465
+ letterSpacing: '0.019em',
466
+ color: textMuted,
467
+ }}
468
+ >
325
469
  Uploading... {fileUpload.uploadProgress}%
326
470
  </p>
327
471
  </div>
@@ -335,11 +479,16 @@ export function ChatInput({
335
479
  createPortal(
336
480
  <div
337
481
  ref={emojiPickerRef}
338
- className="fixed rounded-lg border bg-white dark:bg-gray-800 dark:border-gray-700 shadow-xl"
482
+ className="fixed rounded-xl overflow-hidden"
339
483
  style={{
340
484
  top: `${emojiPickerPosition.top}px`,
341
485
  left: `${emojiPickerPosition.left}px`,
342
486
  zIndex: 9999,
487
+ boxShadow: [
488
+ 'inset 0 0 0 0.5px rgba(255,255,255,0.06)',
489
+ 'inset 0 1px 0 0 rgba(255,255,255,0.1)',
490
+ '0 16px 48px -8px rgba(0,0,0,0.5)',
491
+ ].join(', '),
343
492
  }}
344
493
  >
345
494
  <Picker
@@ -353,7 +502,7 @@ export function ChatInput({
353
502
  navPosition="bottom"
354
503
  perLine={8}
355
504
  searchPosition="sticky"
356
- theme="auto"
505
+ theme="dark"
357
506
  />
358
507
  </div>,
359
508
  document.body