reachat 2.1.0-alpha.20 → 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-B7eSDub6.js → CSVFileRenderer-DC2ZhtNx.js} +2 -2
  2. package/dist/{CSVFileRenderer-B7eSDub6.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-CszY8p_0.js → DefaultFileRenderer-3oHDLIk4.js} +2 -2
  11. package/dist/{DefaultFileRenderer-CszY8p_0.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-DNefh8rs.js → index-CWCW-OiG.js} +477 -124
  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 +450 -99
  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-DNefh8rs.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-CszY8p_0.js")
1957
+ () => import("./DefaultFileRenderer-3oHDLIk4.js")
1668
1958
  );
1669
- const CSVFileRenderer = lazy(() => import("./CSVFileRenderer-B7eSDub6.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 = {
@@ -1768,11 +2058,15 @@ const MessageQuestion = ({
1768
2058
  const ref = useRef(null);
1769
2059
  const calculateTruncation = useCallback(() => {
1770
2060
  const el = ref.current;
1771
- setTruncated(el.scrollHeight > el.clientHeight);
2061
+ if (el) {
2062
+ setTruncated(el.scrollHeight > el.clientHeight);
2063
+ }
1772
2064
  }, []);
1773
2065
  useEffect(() => {
1774
2066
  const el = ref.current;
1775
- if (!el || previewLineClamp === false) return;
2067
+ if (!el || previewLineClamp === false) {
2068
+ return;
2069
+ }
1776
2070
  calculateTruncation();
1777
2071
  const resizeObserver = new ResizeObserver(() => {
1778
2072
  calculateTruncation();
@@ -1995,6 +2289,7 @@ const SessionMessagePanel = ({
1995
2289
  }
1996
2290
  );
1997
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" }));
1998
2293
  const containerVariants = {
1999
2294
  hidden: {},
2000
2295
  visible: {
@@ -2011,11 +2306,29 @@ const SessionMessages = ({
2011
2306
  className,
2012
2307
  showMoreText = "Show more",
2013
2308
  autoScroll = true,
2309
+ showScrollBottomButton = false,
2014
2310
  onScroll
2015
2311
  }) => {
2016
2312
  const { activeSession, theme } = useContext(ChatContext);
2017
2313
  const contentRef = useRef(null);
2314
+ const messagesRef = useRef(null);
2018
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]);
2019
2332
  useEffect(() => {
2020
2333
  if (contentRef.current && autoScroll) {
2021
2334
  requestAnimationFrame(
@@ -2023,10 +2336,18 @@ const SessionMessages = ({
2023
2336
  );
2024
2337
  }
2025
2338
  }, [activeSession, autoScroll, isAnimating]);
2026
- function handleShowMore() {
2339
+ const handleShowMore = () => {
2027
2340
  showNext(limit);
2028
2341
  requestAnimationFrame(() => contentRef.current.scrollTop = 0);
2029
- }
2342
+ };
2343
+ const handleScrollToBottom = () => {
2344
+ if (contentRef.current) {
2345
+ contentRef.current.scrollTo({
2346
+ top: contentRef.current.scrollHeight,
2347
+ behavior: "smooth"
2348
+ });
2349
+ }
2350
+ };
2030
2351
  const reversedConvos = useMemo(
2031
2352
  () => [...activeSession?.conversations ?? []].reverse(),
2032
2353
  [activeSession]
@@ -2040,46 +2361,72 @@ const SessionMessages = ({
2040
2361
  if (!activeSession) {
2041
2362
  return /* @__PURE__ */ jsx(SessionEmpty, { children: newSessionContent });
2042
2363
  }
2043
- return /* @__PURE__ */ jsxs(
2044
- "div",
2045
- {
2046
- className: cn(theme.messages.content, className),
2047
- ref: contentRef,
2048
- onScrollCapture: onScroll,
2049
- children: [
2050
- hasMore && /* @__PURE__ */ jsx(
2051
- 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,
2052
2420
  {
2053
- variant: "outline",
2054
- className: cn(theme.messages.showMore),
2055
- fullWidth: true,
2056
- onClick: handleShowMore,
2057
- children: showMoreText
2421
+ onClick: handleScrollToBottom,
2422
+ className: theme.messages?.message?.scrollToBottom?.button,
2423
+ size: "sm",
2424
+ children: /* @__PURE__ */ jsx(SvgArrowDown, {})
2058
2425
  }
2059
- ),
2060
- /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsx(
2061
- motion.div,
2062
- {
2063
- variants: containerVariants,
2064
- initial: "hidden",
2065
- animate: "visible",
2066
- onAnimationComplete: () => {
2067
- requestAnimationFrame(() => setIsAnimating(false));
2068
- },
2069
- children: children ? children(convosToRender) : convosToRender.map((conversation, index) => /* @__PURE__ */ jsx(
2070
- SessionMessage,
2071
- {
2072
- conversation,
2073
- isLast: index === conversation.length - 1
2074
- },
2075
- conversation.id
2076
- ))
2077
- },
2078
- activeSession?.id
2079
- ) })
2080
- ]
2081
- }
2082
- );
2426
+ )
2427
+ }
2428
+ ) })
2429
+ ] });
2083
2430
  };
2084
2431
  const SessionMessagesHeader = ({ children }) => {
2085
2432
  const { activeSession, theme } = useContext(ChatContext);
@@ -2289,10 +2636,16 @@ const SessionsList = ({
2289
2636
  };
2290
2637
  export {
2291
2638
  AppBar as A,
2292
- chatTheme as B,
2639
+ SessionMessages as B,
2293
2640
  ChatContext as C,
2294
- groupSessionsByDate as D,
2295
- 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,
2296
2649
  Markdown as M,
2297
2650
  NewSessionButton as N,
2298
2651
  SvgFile as S,
@@ -2301,27 +2654,27 @@ export {
2301
2654
  Chat as b,
2302
2655
  ChatBubble as c,
2303
2656
  ChatInput as d,
2304
- CodeHighlighter as e,
2305
- TableHeaderCell as f,
2306
- TableDataCell as g,
2307
- dark as h,
2308
- SessionEmpty as i,
2309
- MessageActions as j,
2310
- 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,
2311
2664
  light as l,
2312
- MessageFiles as m,
2313
- MessageQuestion as n,
2314
- MessageResponse as o,
2315
- MessageSource as p,
2316
- MessageSources as q,
2665
+ MessageStatus as m,
2666
+ MessageStatusItem as n,
2667
+ StatusIcon as o,
2668
+ SessionEmpty as p,
2669
+ MessageActions as q,
2317
2670
  remarkCve as r,
2318
- SessionMessage as s,
2319
- SessionMessagePanel as t,
2320
- SessionMessages as u,
2321
- SessionMessagesHeader as v,
2322
- SessionGroups as w,
2323
- SessionListItem as x,
2324
- SessionsGroup as y,
2325
- 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
2326
2679
  };
2327
- //# sourceMappingURL=index-DNefh8rs.js.map
2680
+ //# sourceMappingURL=index-CWCW-OiG.js.map