reachat 2.1.0-alpha.21 → 2.1.0-alpha.22

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 (36) hide show
  1. package/dist/{CSVFileRenderer-GYEEFRXD.js → CSVFileRenderer-DC2ZhtNx.js} +2 -2
  2. package/dist/{CSVFileRenderer-GYEEFRXD.js.map → CSVFileRenderer-DC2ZhtNx.js.map} +1 -1
  3. package/dist/ChatInput/ChatInput.d.ts +9 -1
  4. package/dist/ChatInput/FileDropzone.d.ts +33 -0
  5. package/dist/ChatInput/FileInput.d.ts +0 -4
  6. package/dist/ChatInput/index.d.ts +1 -0
  7. package/dist/ChatSuggestions/ChatSuggestion.d.ts +9 -0
  8. package/dist/ChatSuggestions/ChatSuggestions.d.ts +22 -0
  9. package/dist/ChatSuggestions/index.d.ts +2 -0
  10. package/dist/{DefaultFileRenderer-CUcl0kc2.js → DefaultFileRenderer-3oHDLIk4.js} +2 -2
  11. package/dist/{DefaultFileRenderer-CUcl0kc2.js.map → DefaultFileRenderer-3oHDLIk4.js.map} +1 -1
  12. package/dist/MessageStatus/MessageStatus.d.ts +44 -0
  13. package/dist/MessageStatus/MessageStatusItem.d.ts +9 -0
  14. package/dist/MessageStatus/StatusIcon.d.ts +17 -0
  15. package/dist/MessageStatus/index.d.ts +3 -0
  16. package/dist/SessionMessages/SessionMessages.d.ts +4 -0
  17. package/dist/docs.json +555 -16
  18. package/dist/{index-CwH75cwk.js → index-CWCW-OiG.js} +471 -122
  19. package/dist/index-CWCW-OiG.js.map +1 -0
  20. package/dist/index.css +186 -55
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.js +30 -24
  23. package/dist/index.umd.cjs +444 -97
  24. package/dist/index.umd.cjs.map +1 -1
  25. package/dist/stories/ChatSuggestions.stories.tsx +542 -0
  26. package/dist/stories/Console.stories.tsx +101 -623
  27. package/dist/stories/Files.stories.tsx +348 -0
  28. package/dist/stories/Markdown.stories.tsx +108 -1
  29. package/dist/stories/MessageStatus.stories.tsx +326 -0
  30. package/dist/stories/SessionsList.stories.tsx +276 -0
  31. package/dist/stories/assets/sparkles.svg +7 -0
  32. package/dist/stories/examples.ts +34 -0
  33. package/dist/theme.d.ts +46 -0
  34. package/dist/types.d.ts +10 -0
  35. package/package.json +7 -4
  36. package/dist/index-CwH75cwk.js.map +0 -1
@@ -2,17 +2,19 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import { cn, ConnectedOverlay, Button, Textarea, IconButton, Ellipsis, Card, Divider, useInfinityList, DateFormat, ListItem, List } from "reablocks";
3
3
  import { AnimatePresence, motion } from "motion/react";
4
4
  import * as React from "react";
5
- import { createContext, useState, useCallback, useEffect, useMemo, memo, useRef, useContext, forwardRef, useImperativeHandle, lazy, Suspense } from "react";
5
+ import { createContext, useState, useCallback, useEffect, useMemo, memo, useRef, useContext, forwardRef, useImperativeHandle, isValidElement, cloneElement, lazy, Suspense } from "react";
6
6
  import { useHotkeys } from "reakeys";
7
7
  import remarkGfm from "remark-gfm";
8
8
  import remarkMath from "remark-math";
9
9
  import remarkYoutube from "remark-youtube";
10
10
  import { offset } from "@floating-ui/react";
11
+ import { useDropzone } from "react-dropzone";
11
12
  import { Prism } from "react-syntax-highlighter";
12
13
  import ReactMarkdown from "react-markdown";
13
14
  import rehypeKatex from "rehype-katex";
14
15
  import { findAndReplace } from "mdast-util-find-and-replace";
15
16
  import { Slot } from "@radix-ui/react-slot";
17
+ import debounce from "lodash/debounce";
16
18
  import { isToday, isYesterday, isThisWeek, differenceInYears, format } from "date-fns";
17
19
  const chatTheme = {
18
20
  base: "text-content-text-neutral-base",
@@ -20,6 +22,33 @@ const chatTheme = {
20
22
  companion: "w-full h-full overflow-hidden",
21
23
  empty: "text-center flex-1",
22
24
  appbar: "flex p-5",
25
+ status: {
26
+ base: "py-2 px-3 rounded-lg bg-gray-100/50 dark:bg-gray-800/30",
27
+ header: "flex items-center gap-2",
28
+ icon: {
29
+ base: "flex-shrink-0 w-4 h-4",
30
+ loading: "text-blue-500 dark:text-blue-400",
31
+ complete: "text-green-500 dark:text-green-400",
32
+ error: "text-red-500 dark:text-red-400"
33
+ },
34
+ text: {
35
+ base: "text-sm",
36
+ loading: "text-gray-600 dark:text-gray-400",
37
+ complete: "text-gray-600 dark:text-gray-400",
38
+ error: "text-red-600 dark:text-red-400"
39
+ },
40
+ steps: {
41
+ base: "mt-1 ml-6 space-y-0.5",
42
+ step: {
43
+ base: "flex items-center gap-2",
44
+ icon: "flex-shrink-0 w-3.5 h-3.5",
45
+ text: "text-sm",
46
+ loading: "text-gray-500 dark:text-gray-500",
47
+ complete: "text-gray-500 dark:text-gray-500",
48
+ error: "text-red-500 dark:text-red-400"
49
+ }
50
+ }
51
+ },
23
52
  sessions: {
24
53
  base: "overflow-auto",
25
54
  console: "min-w-[150px] w-[30%] max-w-[300px] bg-gradient-neutral-200 p-5 rounded-3xl",
@@ -34,7 +63,7 @@ const chatTheme = {
34
63
  },
35
64
  messages: {
36
65
  base: "",
37
- console: "flex flex-col mx-5 flex-1 overflow-hidden",
66
+ console: "flex flex-col mx-5 flex-1 min-h-0",
38
67
  companion: "flex w-full h-full",
39
68
  back: "self-start pl-0 my-2 ",
40
69
  inner: "flex-1 h-full flex flex-col",
@@ -49,7 +78,11 @@ const chatTheme = {
49
78
  response: "relative data-[compact=false]:px-4 text-content-text-neutral-base",
50
79
  overlay: "overflow-y-hidden max-h-[350px] after:content-[''] after:absolute after:inset-x-0 after:bottom-0 after:h-16 after:bg-linear-to-b after:from-transparent after:to-effects-shadows-base-2-xl",
51
80
  cursor: "inline-block w-1 h-4 bg-current",
52
- expand: "absolute bottom-0 right-1 z-10",
81
+ expand: "absolute bottom-1 right-1 z-10",
82
+ scrollToBottom: {
83
+ container: "absolute bottom-2 left-1/2 transform -translate-x-1/2 z-10",
84
+ button: "rounded-full p-2 shadow-lg"
85
+ },
53
86
  files: {
54
87
  base: "mb-2 flex flex-wrap gap-3 ",
55
88
  file: {
@@ -118,6 +151,26 @@ const chatTheme = {
118
151
  base: "absolute flex gap-2 items-center right-2 inset-y-1/2 -translate-y-1/2 z-10",
119
152
  send: "px-3 py-3 hover:bg-primary-hover rounded-full size-8",
120
153
  stop: "px-2 py-2 bg-content-assets-semantic-error-base text-white rounded-full hover:bg-content-assets-semantic-error-1 size-8"
154
+ },
155
+ dropzone: {
156
+ base: "relative w-full",
157
+ active: "ring-1 ring-primary ring-offset-2 rounded-3xl",
158
+ overlay: "absolute inset-0 bg-primary/10 border-2 border-dashed border-primary rounded-3xl flex items-center justify-center gap-1.5 z-20 backdrop-blur-sm",
159
+ text: "text-primary font-medium text-sm",
160
+ icon: "[&>svg]:w-4 [&>svg]:h-4 text-primary"
161
+ }
162
+ },
163
+ suggestions: {
164
+ base: "flex flex-wrap gap-2 mt-4",
165
+ item: {
166
+ base: [
167
+ "rounded-full! max-w-full py-2 px-4",
168
+ "bg-gray-100 border-gray-200 hover:bg-gray-200 hover:border-gray-300 text-gray-700",
169
+ "dark:bg-gray-800/50 dark:border-gray-700 dark:hover:bg-gray-700/70 dark:hover:border-gray-600 dark:text-gray-200",
170
+ "[&>svg]:w-4 [&>svg]:h-4 [&>svg]:text-blue-500 [&>svg]:dark:text-blue-400 [&>svg]:flex-shrink-0"
171
+ ].join(" "),
172
+ icon: "w-4 h-4 text-blue-500 dark:text-blue-400 flex-shrink-0",
173
+ text: "text-sm truncate"
121
174
  }
122
175
  }
123
176
  };
@@ -291,12 +344,60 @@ const ChatBubble = memo(
291
344
  );
292
345
  const SvgSend = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M15.3451 0.654003C15.0011 0.310003 14.5001 0.191003 14.0401 0.339003L1.11712 4.493C0.626116 4.651 0.286116 5.067 0.230116 5.58C0.174116 6.092 0.417116 6.572 0.862116 6.831L5.43812 9.5L9.39112 5.546C9.68412 5.253 10.1591 5.253 10.4521 5.546C10.7451 5.839 10.7451 6.314 10.4521 6.607L6.49812 10.561L9.16712 15.137C9.40211 15.539 9.81712 15.776 10.2741 15.776C10.3221 15.776 10.3711 15.773 10.4201 15.768C10.9321 15.712 11.3491 15.372 11.5061 14.882L15.6611 1.96C15.8091 1.497 15.6881 0.997003 15.3451 0.654003Z", fill: "currentColor" }));
293
346
  const SvgStop = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 50 50", width: "24px", height: "24px", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M 25 2 C 12.309534 2 2 12.309534 2 25 C 2 37.690466 12.309534 48 25 48 C 37.690466 48 48 37.690466 48 25 C 48 12.309534 37.690466 2 25 2 z M 25 4 C 36.609534 4 46 13.390466 46 25 C 46 36.609534 36.609534 46 25 46 C 13.390466 46 4 36.609534 4 25 C 4 13.390466 13.390466 4 25 4 z M 32.990234 15.986328 A 1.0001 1.0001 0 0 0 32.292969 16.292969 L 25 23.585938 L 17.707031 16.292969 A 1.0001 1.0001 0 0 0 16.990234 15.990234 A 1.0001 1.0001 0 0 0 16.292969 17.707031 L 23.585938 25 L 16.292969 32.292969 A 1.0001 1.0001 0 1 0 17.707031 33.707031 L 25 26.414062 L 32.292969 33.707031 A 1.0001 1.0001 0 1 0 33.707031 32.292969 L 26.414062 25 L 33.707031 17.707031 A 1.0001 1.0001 0 0 0 32.990234 15.986328 z", fill: "currentColor" }));
347
+ const SvgUpload = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1, strokeLinecap: "round", strokeLinejoin: "round", className: "lucide lucide-upload", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), /* @__PURE__ */ React.createElement("polyline", { points: "17 8 12 3 7 8" }), /* @__PURE__ */ React.createElement("line", { x1: 12, x2: 12, y1: 3, y2: 15 }));
348
+ const FileDropzone = ({
349
+ allowedFiles,
350
+ multiple = false,
351
+ disabled,
352
+ dropIcon = /* @__PURE__ */ jsx(SvgUpload, {}),
353
+ dropText = "Drop files here to upload",
354
+ children,
355
+ onFileDrop
356
+ }) => {
357
+ const { theme } = useContext(ChatContext);
358
+ const onDrop = useCallback(
359
+ (acceptedFiles) => {
360
+ if (onFileDrop && acceptedFiles.length > 0) {
361
+ if (multiple) {
362
+ acceptedFiles.forEach((file) => onFileDrop(file));
363
+ } else {
364
+ onFileDrop(acceptedFiles[0]);
365
+ }
366
+ }
367
+ },
368
+ [onFileDrop, multiple]
369
+ );
370
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
371
+ onDrop,
372
+ multiple,
373
+ noClick: true,
374
+ noKeyboard: true,
375
+ disabled: disabled || !allowedFiles?.length
376
+ });
377
+ return /* @__PURE__ */ jsxs(
378
+ "div",
379
+ {
380
+ ...getRootProps(),
381
+ className: cn(
382
+ theme.input.dropzone.base,
383
+ isDragActive && theme.input.dropzone.active
384
+ ),
385
+ children: [
386
+ /* @__PURE__ */ jsx("input", { ...getInputProps() }),
387
+ isDragActive && /* @__PURE__ */ jsxs("div", { className: cn(theme.input.dropzone.overlay), children: [
388
+ dropIcon && /* @__PURE__ */ jsx("span", { className: cn(theme.input.dropzone.icon), children: dropIcon }),
389
+ /* @__PURE__ */ jsx("span", { className: cn(theme.input.dropzone.text), children: dropText })
390
+ ] }),
391
+ children
392
+ ]
393
+ }
394
+ );
395
+ };
294
396
  const SvgPaperclip = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 64 64", width: "24px", height: "24px", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M25.3,56.007c-3.409,0-6.818-1.297-9.414-3.893c-2.595-2.596-3.893-6.005-3.893-9.415c0-3.409,1.298-6.819,3.893-9.414 l20.4-20.4c1.847-1.847,4.302-2.864,6.914-2.864c2.611,0,5.067,1.017,6.914,2.864c3.812,3.813,3.812,10.016,0,13.829l-20.4,20.4 c-2.438,2.439-6.385,2.441-8.828,0c-2.434-2.435-2.434-6.395,0-8.829l13.7-13.7c0.781-0.781,2.047-0.781,2.828,0 c0.781,0.781,0.781,2.047,0,2.828l-13.7,13.7c-0.874,0.875-0.874,2.297,0.001,3.172c0.847,0.846,2.324,0.846,3.171,0l20.4-20.4 c2.252-2.253,2.252-5.919,0-8.172c-1.092-1.091-2.543-1.692-4.086-1.692c-1.544,0-2.994,0.601-4.086,1.692l-20.4,20.4 c-1.815,1.816-2.723,4.201-2.723,6.586c0,2.385,0.908,4.77,2.723,6.586c1.816,1.815,4.201,2.723,6.586,2.723 c2.385,0,4.771-0.907,6.586-2.723l12.7-12.7c0.781-0.781,2.047-0.781,2.828,0c0.781,0.781,0.781,2.047,0,2.828l-12.7,12.7 C32.119,54.709,28.71,56.007,25.3,56.007z", fill: "currentColor" }));
295
397
  const FileInput = ({
296
398
  allowedFiles,
297
399
  multiple,
298
400
  onFileUpload,
299
- isLoading,
300
401
  disabled,
301
402
  attachIcon = /* @__PURE__ */ jsx(SvgPaperclip, {})
302
403
  }) => {
@@ -324,7 +425,7 @@ const FileInput = ({
324
425
  {
325
426
  title: "Upload",
326
427
  variant: "text",
327
- disabled: isLoading || disabled,
428
+ disabled,
328
429
  className: cn(theme.input.upload),
329
430
  onClick: () => fileInputRef.current?.click(),
330
431
  children: attachIcon
@@ -343,6 +444,8 @@ const ChatInput = forwardRef(
343
444
  sendIcon = /* @__PURE__ */ jsx(SvgSend, {}),
344
445
  stopIcon = /* @__PURE__ */ jsx(SvgStop, {}),
345
446
  attachIcon,
447
+ dropIcon,
448
+ dropText,
346
449
  onMessageChange
347
450
  }, ref) => {
348
451
  const {
@@ -357,7 +460,7 @@ const ChatInput = forwardRef(
357
460
  const [internalMessage, setInternalMessage] = useState("");
358
461
  const inputRef = useRef(null);
359
462
  useEffect(() => {
360
- setInternalMessage(message || "");
463
+ setInternalMessage(message);
361
464
  }, [message]);
362
465
  useEffect(() => {
363
466
  if (inputRef.current) {
@@ -411,58 +514,120 @@ const ChatInput = forwardRef(
411
514
  },
412
515
  [onMessageChange]
413
516
  );
414
- return /* @__PURE__ */ jsxs("div", { className: cn(theme.input.base, className), children: [
415
- /* @__PURE__ */ jsx(
416
- Textarea,
417
- {
418
- ref: inputRef,
419
- containerClassName: cn(theme.input.input),
420
- minRows: 1,
421
- autoFocus: true,
422
- value: internalMessage,
423
- defaultValue,
424
- onKeyPress: handleKeyPress,
425
- placeholder,
426
- disabled: isLoading || disabled,
427
- onChange: handleMessageChange
428
- }
429
- ),
430
- /* @__PURE__ */ jsxs("div", { className: cn(theme.input.actions.base), children: [
431
- allowedFiles?.length > 0 && /* @__PURE__ */ jsx(
432
- FileInput,
433
- {
434
- allowedFiles,
435
- multiple: allowMultipleFiles,
436
- onFileUpload: handleFileUpload,
437
- isLoading,
438
- disabled,
439
- attachIcon
440
- }
441
- ),
442
- isLoading && /* @__PURE__ */ jsx(
443
- Button,
444
- {
445
- title: "Stop",
446
- className: cn(theme.input.actions.stop),
447
- onClick: stopMessage,
448
- disabled,
449
- children: stopIcon
450
- }
451
- ),
452
- /* @__PURE__ */ jsx(
453
- Button,
454
- {
455
- title: "Send",
456
- className: cn(theme.input.actions.send),
457
- onClick: handleSendMessage,
458
- disabled: isLoading || disabled,
459
- children: sendIcon
460
- }
461
- )
462
- ] })
463
- ] });
517
+ return /* @__PURE__ */ jsx("div", { className: cn(theme.input.base, className), children: /* @__PURE__ */ jsxs(
518
+ FileDropzone,
519
+ {
520
+ allowedFiles,
521
+ multiple: allowMultipleFiles,
522
+ disabled: disabled || isLoading,
523
+ dropIcon,
524
+ dropText,
525
+ onFileDrop: fileUpload,
526
+ children: [
527
+ /* @__PURE__ */ jsx(
528
+ Textarea,
529
+ {
530
+ ref: inputRef,
531
+ containerClassName: cn(theme.input.input),
532
+ minRows: 1,
533
+ autoFocus: true,
534
+ value: internalMessage,
535
+ defaultValue,
536
+ onKeyPress: handleKeyPress,
537
+ placeholder,
538
+ disabled: isLoading || disabled,
539
+ onChange: handleMessageChange
540
+ }
541
+ ),
542
+ /* @__PURE__ */ jsxs("div", { className: cn(theme.input.actions.base), children: [
543
+ allowedFiles?.length > 0 && /* @__PURE__ */ jsx(
544
+ FileInput,
545
+ {
546
+ allowedFiles,
547
+ multiple: allowMultipleFiles,
548
+ onFileUpload: handleFileUpload,
549
+ disabled: disabled || isLoading,
550
+ attachIcon
551
+ }
552
+ ),
553
+ isLoading && /* @__PURE__ */ jsx(
554
+ Button,
555
+ {
556
+ title: "Stop",
557
+ className: cn(theme.input.actions.stop),
558
+ onClick: stopMessage,
559
+ disabled,
560
+ children: stopIcon
561
+ }
562
+ ),
563
+ /* @__PURE__ */ jsx(
564
+ Button,
565
+ {
566
+ title: "Send",
567
+ className: cn(theme.input.actions.send),
568
+ onClick: handleSendMessage,
569
+ disabled: isLoading || disabled,
570
+ children: sendIcon
571
+ }
572
+ )
573
+ ] })
574
+ ]
575
+ }
576
+ ) });
464
577
  }
465
578
  );
579
+ const ChatSuggestion = ({
580
+ content,
581
+ onClick
582
+ }) => {
583
+ const { theme, disabled, isLoading } = useContext(ChatContext);
584
+ const handleClick = () => {
585
+ if (disabled || isLoading) return;
586
+ onClick?.(content);
587
+ };
588
+ return /* @__PURE__ */ jsx(
589
+ Button,
590
+ {
591
+ type: "button",
592
+ variant: "outline",
593
+ disableMargins: true,
594
+ className: cn(theme.suggestions.item.base, {
595
+ "opacity-50 cursor-not-allowed": disabled || isLoading
596
+ }),
597
+ onClick: handleClick,
598
+ disabled: disabled || isLoading,
599
+ children: /* @__PURE__ */ jsx("span", { className: cn(theme.suggestions.item.text), children: content })
600
+ }
601
+ );
602
+ };
603
+ const ChatSuggestions = ({
604
+ suggestions,
605
+ className,
606
+ onSuggestionClick,
607
+ children
608
+ }) => {
609
+ const { theme } = useContext(ChatContext);
610
+ if (!suggestions || suggestions.length === 0) {
611
+ return null;
612
+ }
613
+ return /* @__PURE__ */ jsx("div", { className: cn(theme.suggestions.base, className), children: suggestions.map((suggestion) => {
614
+ if (children && isValidElement(children)) {
615
+ return cloneElement(children, {
616
+ key: suggestion.id,
617
+ ...suggestion,
618
+ onClick: onSuggestionClick
619
+ });
620
+ }
621
+ return /* @__PURE__ */ jsx(
622
+ ChatSuggestion,
623
+ {
624
+ ...suggestion,
625
+ onClick: onSuggestionClick
626
+ },
627
+ suggestion.id
628
+ );
629
+ }) });
630
+ };
466
631
  const SvgCopy = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 18, height: 18, viewBox: "0 0 18 18", fill: "none", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M1.75 6C2.164 6 2.5 6.336 2.5 6.75V13.25C2.5 13.939 3.061 14.5 3.75 14.5H12.25C12.664 14.5 13 14.836 13 15.25C13 15.664 12.664 16 12.25 16H3.75C2.233 16 1 14.767 1 13.25V6.75C1 6.336 1.336 6 1.75 6ZM14.25 2C15.7688 2 17 3.23122 17 4.75V10.25C17 11.7688 15.7688 13 14.25 13H6.75C5.23122 13 4 11.7688 4 10.25V4.75C4 3.23122 5.23122 2 6.75 2H14.25Z", fill: "currentColor" }));
467
632
  const dark = {
468
633
  'code[class*="language-"]': {
@@ -1582,6 +1747,131 @@ function replaceCve(value) {
1582
1747
  children: [{ type: "text", value: value.trim() }]
1583
1748
  };
1584
1749
  }
1750
+ const SvgSpinner = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }));
1751
+ const SvgCheck = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", ...props }, /* @__PURE__ */ React.createElement("polyline", { points: "20 6 9 17 4 12" }));
1752
+ const SvgError = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", ...props }, /* @__PURE__ */ React.createElement("circle", { cx: 12, cy: 12, r: 10 }), /* @__PURE__ */ React.createElement("line", { x1: 15, y1: 9, x2: 9, y2: 15 }), /* @__PURE__ */ React.createElement("line", { x1: 9, y1: 9, x2: 15, y2: 15 }));
1753
+ const StatusIcon = ({
1754
+ state,
1755
+ className,
1756
+ colorClassName
1757
+ }) => {
1758
+ switch (state) {
1759
+ case "loading":
1760
+ return /* @__PURE__ */ jsx(
1761
+ motion.div,
1762
+ {
1763
+ animate: { rotate: 360 },
1764
+ transition: { duration: 1, repeat: Infinity, ease: "linear" },
1765
+ className: cn(className, colorClassName),
1766
+ children: /* @__PURE__ */ jsx(SvgSpinner, { className: "w-full h-full" })
1767
+ }
1768
+ );
1769
+ case "complete":
1770
+ return /* @__PURE__ */ jsx(
1771
+ motion.div,
1772
+ {
1773
+ initial: { scale: 0, opacity: 0 },
1774
+ animate: { scale: 1, opacity: 1 },
1775
+ transition: { type: "spring", stiffness: 500, damping: 25 },
1776
+ className: cn(className, colorClassName),
1777
+ children: /* @__PURE__ */ jsx(SvgCheck, { className: "w-full h-full" })
1778
+ }
1779
+ );
1780
+ case "error":
1781
+ return /* @__PURE__ */ jsx(
1782
+ motion.div,
1783
+ {
1784
+ initial: { scale: 0, opacity: 0 },
1785
+ animate: { scale: 1, opacity: 1 },
1786
+ transition: { type: "spring", stiffness: 500, damping: 25 },
1787
+ className: cn(className, colorClassName),
1788
+ children: /* @__PURE__ */ jsx(SvgError, { className: "w-full h-full" })
1789
+ }
1790
+ );
1791
+ }
1792
+ };
1793
+ const MessageStatusItem = ({ step }) => {
1794
+ const { theme: chatTheme2 } = useContext(ChatContext);
1795
+ const theme = chatTheme2.status.steps.step;
1796
+ const stepStatus = step.status || "loading";
1797
+ return /* @__PURE__ */ jsxs(
1798
+ motion.div,
1799
+ {
1800
+ initial: { opacity: 0, x: -10 },
1801
+ animate: { opacity: 1, x: 0 },
1802
+ transition: { duration: 0.2 },
1803
+ className: theme.base,
1804
+ children: [
1805
+ /* @__PURE__ */ jsx(
1806
+ StatusIcon,
1807
+ {
1808
+ state: stepStatus,
1809
+ className: theme.icon,
1810
+ colorClassName: theme[stepStatus]
1811
+ }
1812
+ ),
1813
+ /* @__PURE__ */ jsx(
1814
+ "span",
1815
+ {
1816
+ className: cn(
1817
+ theme.text,
1818
+ stepStatus === "loading" && theme.loading,
1819
+ stepStatus === "complete" && theme.complete,
1820
+ stepStatus === "error" && theme.error
1821
+ ),
1822
+ children: step.text
1823
+ }
1824
+ )
1825
+ ]
1826
+ }
1827
+ );
1828
+ };
1829
+ const MessageStatus = ({
1830
+ status = "loading",
1831
+ text,
1832
+ steps,
1833
+ icon,
1834
+ className,
1835
+ children
1836
+ }) => {
1837
+ const { theme: chatTheme2 } = useContext(ChatContext);
1838
+ const theme = chatTheme2.status;
1839
+ const Comp = children ? Slot : "div";
1840
+ return /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
1841
+ motion.div,
1842
+ {
1843
+ initial: { opacity: 0, y: -10 },
1844
+ animate: { opacity: 1, y: 0 },
1845
+ exit: { opacity: 0, y: -10 },
1846
+ transition: { duration: 0.2 },
1847
+ children: /* @__PURE__ */ jsx(Comp, { className: cn(theme.base, className), children: children || /* @__PURE__ */ jsxs(Fragment, { children: [
1848
+ /* @__PURE__ */ jsxs("div", { className: theme.header, children: [
1849
+ icon || /* @__PURE__ */ jsx(
1850
+ StatusIcon,
1851
+ {
1852
+ state: status,
1853
+ className: theme.icon.base,
1854
+ colorClassName: theme.icon[status]
1855
+ }
1856
+ ),
1857
+ /* @__PURE__ */ jsx(
1858
+ "span",
1859
+ {
1860
+ className: cn(
1861
+ theme.text.base,
1862
+ status === "loading" && theme.text.loading,
1863
+ status === "complete" && theme.text.complete,
1864
+ status === "error" && theme.text.error
1865
+ ),
1866
+ children: text
1867
+ }
1868
+ )
1869
+ ] }),
1870
+ steps && steps.length > 0 && /* @__PURE__ */ jsx("div", { className: theme.steps.base, children: steps.map((step) => /* @__PURE__ */ jsx(MessageStatusItem, { step }, step.id)) })
1871
+ ] }) })
1872
+ }
1873
+ ) });
1874
+ };
1585
1875
  const SessionEmpty = ({ children }) => {
1586
1876
  const { theme } = useContext(ChatContext);
1587
1877
  return /* @__PURE__ */ jsx("div", { className: cn(theme.empty), children });
@@ -1664,9 +1954,9 @@ ${response}`),
1664
1954
  };
1665
1955
  const SvgFile = (props) => /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 16, height: 16, viewBox: "0 0 16 16", fill: "currentColor", ...props }, /* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M2.7036 1.37034C3.04741 1.02653 3.51373 0.833374 3.99996 0.833374H9.33329H9.33331C9.47275 0.833374 9.59885 0.890449 9.68954 0.98251L13.6843 4.97722C13.7763 5.0679 13.8333 5.19398 13.8333 5.33337L13.8333 5.3379V13.3334C13.8333 13.8196 13.6401 14.2859 13.2963 14.6297C12.9525 14.9736 12.4862 15.1667 12 15.1667H3.99996C3.51373 15.1667 3.04741 14.9736 2.7036 14.6297C2.35978 14.2859 2.16663 13.8196 2.16663 13.3334V2.66671C2.16663 2.18048 2.35978 1.71416 2.7036 1.37034ZM3.99996 1.83337H8.83331V5.33337C8.83331 5.60952 9.05717 5.83337 9.33331 5.83337H12.8333V13.3334C12.8333 13.5544 12.7455 13.7663 12.5892 13.9226C12.4329 14.0789 12.221 14.1667 12 14.1667H3.99996C3.77895 14.1667 3.56698 14.0789 3.4107 13.9226C3.25442 13.7663 3.16663 13.5544 3.16663 13.3334V2.66671C3.16663 2.44569 3.25442 2.23373 3.4107 2.07745C3.56698 1.92117 3.77895 1.83337 3.99996 1.83337ZM9.83331 2.5405L12.1262 4.83337H9.83331V2.5405ZM5.33331 8.16663C5.05717 8.16663 4.83331 8.39048 4.83331 8.66663C4.83331 8.94277 5.05717 9.16663 5.33331 9.16663H10.6666C10.9428 9.16663 11.1666 8.94277 11.1666 8.66663C11.1666 8.39048 10.9428 8.16663 10.6666 8.16663H5.33331ZM4.83331 11.3334C4.83331 11.0572 5.05717 10.8334 5.33331 10.8334H10.6666C10.9428 10.8334 11.1666 11.0572 11.1666 11.3334C11.1666 11.6095 10.9428 11.8334 10.6666 11.8334H5.33331C5.05717 11.8334 4.83331 11.6095 4.83331 11.3334ZM5.33331 5.5C5.05717 5.5 4.83331 5.72386 4.83331 6C4.83331 6.27614 5.05717 6.5 5.33331 6.5H6.66665C6.94279 6.5 7.16665 6.27614 7.16665 6C7.16665 5.72386 6.94279 5.5 6.66665 5.5H5.33331Z" }));
1666
1956
  const DefaultFileRenderer = lazy(
1667
- () => import("./DefaultFileRenderer-CUcl0kc2.js")
1957
+ () => import("./DefaultFileRenderer-3oHDLIk4.js")
1668
1958
  );
1669
- const CSVFileRenderer = lazy(() => import("./CSVFileRenderer-GYEEFRXD.js"));
1959
+ const CSVFileRenderer = lazy(() => import("./CSVFileRenderer-DC2ZhtNx.js"));
1670
1960
  const ImageFileRenderer = lazy(() => import("./ImageFileRenderer-C8tVW3I8.js"));
1671
1961
  const PDFFileRenderer = lazy(() => import("./PDFFileRenderer-BBn2EVrV.js"));
1672
1962
  const FILE_TYPE_RENDERER_MAP = {
@@ -1999,6 +2289,7 @@ const SessionMessagePanel = ({
1999
2289
  }
2000
2290
  );
2001
2291
  };
2292
+ const SvgArrowDown = (props) => /* @__PURE__ */ React.createElement("svg", { width: 20, height: 20, viewBox: "0 0 20 20", fill: "currentColor", xmlns: "http://www.w3.org/2000/svg", className: "shrink-0 mix-blend-luminosity", "aria-hidden": "true", ...props }, /* @__PURE__ */ React.createElement("path", { d: "M10 3C10.2761 3.00006 10.5 3.2239 10.5 3.5V15.293L14.6465 11.1465C14.8418 10.9514 15.1583 10.9513 15.3536 11.1465C15.5487 11.3417 15.5486 11.6583 15.3536 11.8535L10.3535 16.8535C10.2598 16.9473 10.1326 17 10 17C9.90062 17 9.8042 16.9703 9.72268 16.916L9.64651 16.8535L4.6465 11.8535C4.45138 11.6582 4.45128 11.3417 4.6465 11.1465C4.84172 10.9513 5.15827 10.9514 5.35353 11.1465L9.50003 15.293V3.5C9.50003 3.22386 9.72389 3 10 3Z" }));
2002
2293
  const containerVariants = {
2003
2294
  hidden: {},
2004
2295
  visible: {
@@ -2015,11 +2306,29 @@ const SessionMessages = ({
2015
2306
  className,
2016
2307
  showMoreText = "Show more",
2017
2308
  autoScroll = true,
2309
+ showScrollBottomButton = false,
2018
2310
  onScroll
2019
2311
  }) => {
2020
2312
  const { activeSession, theme } = useContext(ChatContext);
2021
2313
  const contentRef = useRef(null);
2314
+ const messagesRef = useRef(null);
2022
2315
  const [isAnimating, setIsAnimating] = useState(true);
2316
+ const [iAtBottom, setIsAtBottom] = useState(true);
2317
+ useEffect(() => {
2318
+ if (!contentRef.current || !showScrollBottomButton) {
2319
+ return;
2320
+ }
2321
+ const handleScroll = debounce(() => {
2322
+ if (contentRef.current) {
2323
+ setIsAtBottom(
2324
+ contentRef.current.scrollHeight - contentRef.current.clientHeight === contentRef.current.scrollTop
2325
+ );
2326
+ }
2327
+ }, 50);
2328
+ const currentRef = contentRef.current;
2329
+ currentRef.addEventListener("scroll", handleScroll);
2330
+ return () => currentRef.removeEventListener("scroll", handleScroll);
2331
+ }, [showScrollBottomButton]);
2023
2332
  useEffect(() => {
2024
2333
  if (contentRef.current && autoScroll) {
2025
2334
  requestAnimationFrame(
@@ -2027,10 +2336,18 @@ const SessionMessages = ({
2027
2336
  );
2028
2337
  }
2029
2338
  }, [activeSession, autoScroll, isAnimating]);
2030
- function handleShowMore() {
2339
+ const handleShowMore = () => {
2031
2340
  showNext(limit);
2032
2341
  requestAnimationFrame(() => contentRef.current.scrollTop = 0);
2033
- }
2342
+ };
2343
+ const handleScrollToBottom = () => {
2344
+ if (contentRef.current) {
2345
+ contentRef.current.scrollTo({
2346
+ top: contentRef.current.scrollHeight,
2347
+ behavior: "smooth"
2348
+ });
2349
+ }
2350
+ };
2034
2351
  const reversedConvos = useMemo(
2035
2352
  () => [...activeSession?.conversations ?? []].reverse(),
2036
2353
  [activeSession]
@@ -2044,46 +2361,72 @@ const SessionMessages = ({
2044
2361
  if (!activeSession) {
2045
2362
  return /* @__PURE__ */ jsx(SessionEmpty, { children: newSessionContent });
2046
2363
  }
2047
- return /* @__PURE__ */ jsxs(
2048
- "div",
2049
- {
2050
- className: cn(theme.messages.content, className),
2051
- ref: contentRef,
2052
- onScrollCapture: onScroll,
2053
- children: [
2054
- hasMore && /* @__PURE__ */ jsx(
2055
- Button,
2364
+ return /* @__PURE__ */ jsxs("div", { className: "relative flex-1 overflow-y-hidden", children: [
2365
+ /* @__PURE__ */ jsxs(
2366
+ "div",
2367
+ {
2368
+ className: cn(theme.messages.content, className, "h-full"),
2369
+ ref: contentRef,
2370
+ id: activeSession?.id,
2371
+ onScrollCapture: onScroll,
2372
+ children: [
2373
+ hasMore && /* @__PURE__ */ jsx(
2374
+ Button,
2375
+ {
2376
+ variant: "outline",
2377
+ className: cn(theme.messages.showMore),
2378
+ fullWidth: true,
2379
+ onClick: handleShowMore,
2380
+ children: showMoreText
2381
+ }
2382
+ ),
2383
+ /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(
2384
+ motion.div,
2385
+ {
2386
+ ref: messagesRef,
2387
+ variants: containerVariants,
2388
+ initial: "hidden",
2389
+ animate: "visible",
2390
+ onAnimationComplete: () => requestAnimationFrame(() => {
2391
+ setIsAnimating(false);
2392
+ if (contentRef.current && autoScroll) {
2393
+ contentRef.current.scrollTop = contentRef.current.scrollHeight;
2394
+ }
2395
+ }),
2396
+ children: children ? children(convosToRender) : convosToRender.map((conversation, index) => /* @__PURE__ */ jsx(
2397
+ SessionMessage,
2398
+ {
2399
+ conversation,
2400
+ isLast: index === convosToRender.length - 1
2401
+ },
2402
+ conversation.id
2403
+ ))
2404
+ },
2405
+ activeSession?.id
2406
+ ) })
2407
+ ]
2408
+ }
2409
+ ),
2410
+ /* @__PURE__ */ jsx(AnimatePresence, { children: !iAtBottom && showScrollBottomButton && /* @__PURE__ */ jsx(
2411
+ motion.div,
2412
+ {
2413
+ initial: { y: 100, opacity: 0 },
2414
+ animate: { y: 0, opacity: 1 },
2415
+ exit: { y: 100, opacity: 0 },
2416
+ transition: { duration: 0.3, ease: "easeOut" },
2417
+ className: theme.messages?.message?.scrollToBottom?.container,
2418
+ children: /* @__PURE__ */ jsx(
2419
+ IconButton,
2056
2420
  {
2057
- variant: "outline",
2058
- className: cn(theme.messages.showMore),
2059
- fullWidth: true,
2060
- onClick: handleShowMore,
2061
- children: showMoreText
2421
+ onClick: handleScrollToBottom,
2422
+ className: theme.messages?.message?.scrollToBottom?.button,
2423
+ size: "sm",
2424
+ children: /* @__PURE__ */ jsx(SvgArrowDown, {})
2062
2425
  }
2063
- ),
2064
- /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(
2065
- motion.div,
2066
- {
2067
- variants: containerVariants,
2068
- initial: "hidden",
2069
- animate: "visible",
2070
- onAnimationComplete: () => {
2071
- requestAnimationFrame(() => setIsAnimating(false));
2072
- },
2073
- children: children ? children(convosToRender) : convosToRender.map((conversation, index) => /* @__PURE__ */ jsx(
2074
- SessionMessage,
2075
- {
2076
- conversation,
2077
- isLast: index === conversation.length - 1
2078
- },
2079
- conversation.id
2080
- ))
2081
- },
2082
- activeSession?.id
2083
- ) })
2084
- ]
2085
- }
2086
- );
2426
+ )
2427
+ }
2428
+ ) })
2429
+ ] });
2087
2430
  };
2088
2431
  const SessionMessagesHeader = ({ children }) => {
2089
2432
  const { activeSession, theme } = useContext(ChatContext);
@@ -2293,10 +2636,16 @@ const SessionsList = ({
2293
2636
  };
2294
2637
  export {
2295
2638
  AppBar as A,
2296
- chatTheme as B,
2639
+ SessionMessages as B,
2297
2640
  ChatContext as C,
2298
- groupSessionsByDate as D,
2299
- FileInput as F,
2641
+ SessionMessagesHeader as D,
2642
+ SessionGroups as E,
2643
+ FileDropzone as F,
2644
+ SessionListItem as G,
2645
+ SessionsGroup as H,
2646
+ SessionsList as I,
2647
+ chatTheme as J,
2648
+ groupSessionsByDate as K,
2300
2649
  Markdown as M,
2301
2650
  NewSessionButton as N,
2302
2651
  SvgFile as S,
@@ -2305,27 +2654,27 @@ export {
2305
2654
  Chat as b,
2306
2655
  ChatBubble as c,
2307
2656
  ChatInput as d,
2308
- CodeHighlighter as e,
2309
- TableHeaderCell as f,
2310
- TableDataCell as g,
2311
- dark as h,
2312
- SessionEmpty as i,
2313
- MessageActions as j,
2314
- MessageFile as k,
2657
+ FileInput as e,
2658
+ ChatSuggestions as f,
2659
+ ChatSuggestion as g,
2660
+ CodeHighlighter as h,
2661
+ TableHeaderCell as i,
2662
+ TableDataCell as j,
2663
+ dark as k,
2315
2664
  light as l,
2316
- MessageFiles as m,
2317
- MessageQuestion as n,
2318
- MessageResponse as o,
2319
- MessageSource as p,
2320
- MessageSources as q,
2665
+ MessageStatus as m,
2666
+ MessageStatusItem as n,
2667
+ StatusIcon as o,
2668
+ SessionEmpty as p,
2669
+ MessageActions as q,
2321
2670
  remarkCve as r,
2322
- SessionMessage as s,
2323
- SessionMessagePanel as t,
2324
- SessionMessages as u,
2325
- SessionMessagesHeader as v,
2326
- SessionGroups as w,
2327
- SessionListItem as x,
2328
- SessionsGroup as y,
2329
- SessionsList as z
2671
+ MessageFile as s,
2672
+ MessageFiles as t,
2673
+ MessageQuestion as u,
2674
+ MessageResponse as v,
2675
+ MessageSource as w,
2676
+ MessageSources as x,
2677
+ SessionMessage as y,
2678
+ SessionMessagePanel as z
2330
2679
  };
2331
- //# sourceMappingURL=index-CwH75cwk.js.map
2680
+ //# sourceMappingURL=index-CWCW-OiG.js.map