reachat 2.1.0-alpha.21 → 2.1.0-alpha.23

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-CnRHD92k.js} +2 -2
  2. package/dist/{CSVFileRenderer-GYEEFRXD.js.map → CSVFileRenderer-CnRHD92k.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-Du1R14oj.js} +2 -2
  11. package/dist/{DefaultFileRenderer-CUcl0kc2.js.map → DefaultFileRenderer-Du1R14oj.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 +16 -0
  17. package/dist/docs.json +612 -16
  18. package/dist/{index-CwH75cwk.js → index-tgcYBaMs.js} +476 -123
  19. package/dist/index-tgcYBaMs.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 +449 -98
  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
@@ -11,8 +11,8 @@
11
11
  }
12
12
  })();
13
13
  (function(global, factory) {
14
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("motion/react"), require("reablocks"), require("react"), require("reakeys"), require("remark-gfm"), require("remark-math"), require("remark-youtube"), require("@floating-ui/react"), require("react-syntax-highlighter"), require("react-markdown"), require("rehype-katex"), require("mdast-util-find-and-replace"), require("@radix-ui/react-slot"), require("date-fns")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "motion/react", "reablocks", "react", "reakeys", "remark-gfm", "remark-math", "remark-youtube", "@floating-ui/react", "react-syntax-highlighter", "react-markdown", "rehype-katex", "mdast-util-find-and-replace", "@radix-ui/react-slot", "date-fns"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.reachat = {}, global.jsxRuntime, global.react, global.reablocks, global.React, global.reakeys, global.remarkGfm, global.remarkMath, global.remarkYoutube, global.react$1, global.reactSyntaxHighlighter, global.ReactMarkdown, global.rehypeKatex, global.mdastUtilFindAndReplace, global.reactSlot, global.dateFns));
15
- })(this, (function(exports2, jsxRuntime, react, reablocks, React, reakeys, remarkGfm, remarkMath, remarkYoutube, react$1, reactSyntaxHighlighter, ReactMarkdown, rehypeKatex, mdastUtilFindAndReplace, reactSlot, dateFns) {
14
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("react/jsx-runtime"), require("motion/react"), require("reablocks"), require("react"), require("reakeys"), require("remark-gfm"), require("remark-math"), require("remark-youtube"), require("@floating-ui/react"), require("react-dropzone"), require("react-syntax-highlighter"), require("react-markdown"), require("rehype-katex"), require("mdast-util-find-and-replace"), require("@radix-ui/react-slot"), require("lodash/debounce"), require("date-fns")) : typeof define === "function" && define.amd ? define(["exports", "react/jsx-runtime", "motion/react", "reablocks", "react", "reakeys", "remark-gfm", "remark-math", "remark-youtube", "@floating-ui/react", "react-dropzone", "react-syntax-highlighter", "react-markdown", "rehype-katex", "mdast-util-find-and-replace", "@radix-ui/react-slot", "lodash/debounce", "date-fns"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.reachat = {}, global.jsxRuntime, global.react, global.reablocks, global.React, global.reakeys, global.remarkGfm, global.remarkMath, global.remarkYoutube, global.react$1, global.reactDropzone, global.reactSyntaxHighlighter, global.ReactMarkdown, global.rehypeKatex, global.mdastUtilFindAndReplace, global.reactSlot, global.debounce, global.dateFns));
15
+ })(this, (function(exports2, jsxRuntime, react, reablocks, React, reakeys, remarkGfm, remarkMath, remarkYoutube, react$1, reactDropzone, reactSyntaxHighlighter, ReactMarkdown, rehypeKatex, mdastUtilFindAndReplace, reactSlot, debounce, dateFns) {
16
16
  "use strict";
17
17
  function _interopNamespaceDefault(e) {
18
18
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -37,6 +37,33 @@
37
37
  companion: "w-full h-full overflow-hidden",
38
38
  empty: "text-center flex-1",
39
39
  appbar: "flex p-5",
40
+ status: {
41
+ base: "py-2 px-3 rounded-lg bg-gray-100/50 dark:bg-gray-800/30",
42
+ header: "flex items-center gap-2",
43
+ icon: {
44
+ base: "flex-shrink-0 w-4 h-4",
45
+ loading: "text-blue-500 dark:text-blue-400",
46
+ complete: "text-green-500 dark:text-green-400",
47
+ error: "text-red-500 dark:text-red-400"
48
+ },
49
+ text: {
50
+ base: "text-sm",
51
+ loading: "text-gray-600 dark:text-gray-400",
52
+ complete: "text-gray-600 dark:text-gray-400",
53
+ error: "text-red-600 dark:text-red-400"
54
+ },
55
+ steps: {
56
+ base: "mt-1 ml-6 space-y-0.5",
57
+ step: {
58
+ base: "flex items-center gap-2",
59
+ icon: "flex-shrink-0 w-3.5 h-3.5",
60
+ text: "text-sm",
61
+ loading: "text-gray-500 dark:text-gray-500",
62
+ complete: "text-gray-500 dark:text-gray-500",
63
+ error: "text-red-500 dark:text-red-400"
64
+ }
65
+ }
66
+ },
40
67
  sessions: {
41
68
  base: "overflow-auto",
42
69
  console: "min-w-[150px] w-[30%] max-w-[300px] bg-gradient-neutral-200 p-5 rounded-3xl",
@@ -51,7 +78,7 @@
51
78
  },
52
79
  messages: {
53
80
  base: "",
54
- console: "flex flex-col mx-5 flex-1 overflow-hidden",
81
+ console: "flex flex-col mx-5 flex-1 min-h-0",
55
82
  companion: "flex w-full h-full",
56
83
  back: "self-start pl-0 my-2 ",
57
84
  inner: "flex-1 h-full flex flex-col",
@@ -66,7 +93,11 @@
66
93
  response: "relative data-[compact=false]:px-4 text-content-text-neutral-base",
67
94
  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",
68
95
  cursor: "inline-block w-1 h-4 bg-current",
69
- expand: "absolute bottom-0 right-1 z-10",
96
+ expand: "absolute bottom-1 right-1 z-10",
97
+ scrollToBottom: {
98
+ container: "absolute bottom-2 left-1/2 transform -translate-x-1/2 z-10",
99
+ button: "rounded-full p-2 shadow-lg"
100
+ },
70
101
  files: {
71
102
  base: "mb-2 flex flex-wrap gap-3 ",
72
103
  file: {
@@ -135,6 +166,26 @@
135
166
  base: "absolute flex gap-2 items-center right-2 inset-y-1/2 -translate-y-1/2 z-10",
136
167
  send: "px-3 py-3 hover:bg-primary-hover rounded-full size-8",
137
168
  stop: "px-2 py-2 bg-content-assets-semantic-error-base text-white rounded-full hover:bg-content-assets-semantic-error-1 size-8"
169
+ },
170
+ dropzone: {
171
+ base: "relative w-full",
172
+ active: "ring-1 ring-primary ring-offset-2 rounded-3xl",
173
+ 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",
174
+ text: "text-primary font-medium text-sm",
175
+ icon: "[&>svg]:w-4 [&>svg]:h-4 text-primary"
176
+ }
177
+ },
178
+ suggestions: {
179
+ base: "flex flex-wrap gap-2 mt-4",
180
+ item: {
181
+ base: [
182
+ "rounded-full! max-w-full py-2 px-4",
183
+ "bg-gray-100 border-gray-200 hover:bg-gray-200 hover:border-gray-300 text-gray-700",
184
+ "dark:bg-gray-800/50 dark:border-gray-700 dark:hover:bg-gray-700/70 dark:hover:border-gray-600 dark:text-gray-200",
185
+ "[&>svg]:w-4 [&>svg]:h-4 [&>svg]:text-blue-500 [&>svg]:dark:text-blue-400 [&>svg]:flex-shrink-0"
186
+ ].join(" "),
187
+ icon: "w-4 h-4 text-blue-500 dark:text-blue-400 flex-shrink-0",
188
+ text: "text-sm truncate"
138
189
  }
139
190
  }
140
191
  };
@@ -308,12 +359,60 @@
308
359
  );
309
360
  const SvgSend = (props) => /* @__PURE__ */ React__namespace.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", ...props }, /* @__PURE__ */ React__namespace.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" }));
310
361
  const SvgStop = (props) => /* @__PURE__ */ React__namespace.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 50 50", width: "24px", height: "24px", ...props }, /* @__PURE__ */ React__namespace.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" }));
362
+ const SvgUpload = (props) => /* @__PURE__ */ React__namespace.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__namespace.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), /* @__PURE__ */ React__namespace.createElement("polyline", { points: "17 8 12 3 7 8" }), /* @__PURE__ */ React__namespace.createElement("line", { x1: 12, x2: 12, y1: 3, y2: 15 }));
363
+ const FileDropzone = ({
364
+ allowedFiles,
365
+ multiple = false,
366
+ disabled,
367
+ dropIcon = /* @__PURE__ */ jsxRuntime.jsx(SvgUpload, {}),
368
+ dropText = "Drop files here to upload",
369
+ children,
370
+ onFileDrop
371
+ }) => {
372
+ const { theme } = React.useContext(ChatContext);
373
+ const onDrop = React.useCallback(
374
+ (acceptedFiles) => {
375
+ if (onFileDrop && acceptedFiles.length > 0) {
376
+ if (multiple) {
377
+ acceptedFiles.forEach((file) => onFileDrop(file));
378
+ } else {
379
+ onFileDrop(acceptedFiles[0]);
380
+ }
381
+ }
382
+ },
383
+ [onFileDrop, multiple]
384
+ );
385
+ const { getRootProps, getInputProps, isDragActive } = reactDropzone.useDropzone({
386
+ onDrop,
387
+ multiple,
388
+ noClick: true,
389
+ noKeyboard: true,
390
+ disabled: disabled || !allowedFiles?.length
391
+ });
392
+ return /* @__PURE__ */ jsxRuntime.jsxs(
393
+ "div",
394
+ {
395
+ ...getRootProps(),
396
+ className: reablocks.cn(
397
+ theme.input.dropzone.base,
398
+ isDragActive && theme.input.dropzone.active
399
+ ),
400
+ children: [
401
+ /* @__PURE__ */ jsxRuntime.jsx("input", { ...getInputProps() }),
402
+ isDragActive && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: reablocks.cn(theme.input.dropzone.overlay), children: [
403
+ dropIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: reablocks.cn(theme.input.dropzone.icon), children: dropIcon }),
404
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: reablocks.cn(theme.input.dropzone.text), children: dropText })
405
+ ] }),
406
+ children
407
+ ]
408
+ }
409
+ );
410
+ };
311
411
  const SvgPaperclip = (props) => /* @__PURE__ */ React__namespace.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 64 64", width: "24px", height: "24px", ...props }, /* @__PURE__ */ React__namespace.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" }));
312
412
  const FileInput = ({
313
413
  allowedFiles,
314
414
  multiple,
315
415
  onFileUpload,
316
- isLoading,
317
416
  disabled,
318
417
  attachIcon = /* @__PURE__ */ jsxRuntime.jsx(SvgPaperclip, {})
319
418
  }) => {
@@ -341,7 +440,7 @@
341
440
  {
342
441
  title: "Upload",
343
442
  variant: "text",
344
- disabled: isLoading || disabled,
443
+ disabled,
345
444
  className: reablocks.cn(theme.input.upload),
346
445
  onClick: () => fileInputRef.current?.click(),
347
446
  children: attachIcon
@@ -360,6 +459,8 @@
360
459
  sendIcon = /* @__PURE__ */ jsxRuntime.jsx(SvgSend, {}),
361
460
  stopIcon = /* @__PURE__ */ jsxRuntime.jsx(SvgStop, {}),
362
461
  attachIcon,
462
+ dropIcon,
463
+ dropText,
363
464
  onMessageChange
364
465
  }, ref) => {
365
466
  const {
@@ -374,7 +475,7 @@
374
475
  const [internalMessage, setInternalMessage] = React.useState("");
375
476
  const inputRef = React.useRef(null);
376
477
  React.useEffect(() => {
377
- setInternalMessage(message || "");
478
+ setInternalMessage(message);
378
479
  }, [message]);
379
480
  React.useEffect(() => {
380
481
  if (inputRef.current) {
@@ -428,58 +529,120 @@
428
529
  },
429
530
  [onMessageChange]
430
531
  );
431
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: reablocks.cn(theme.input.base, className), children: [
432
- /* @__PURE__ */ jsxRuntime.jsx(
433
- reablocks.Textarea,
434
- {
435
- ref: inputRef,
436
- containerClassName: reablocks.cn(theme.input.input),
437
- minRows: 1,
438
- autoFocus: true,
439
- value: internalMessage,
440
- defaultValue,
441
- onKeyPress: handleKeyPress,
442
- placeholder,
443
- disabled: isLoading || disabled,
444
- onChange: handleMessageChange
445
- }
446
- ),
447
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: reablocks.cn(theme.input.actions.base), children: [
448
- allowedFiles?.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
449
- FileInput,
450
- {
451
- allowedFiles,
452
- multiple: allowMultipleFiles,
453
- onFileUpload: handleFileUpload,
454
- isLoading,
455
- disabled,
456
- attachIcon
457
- }
458
- ),
459
- isLoading && /* @__PURE__ */ jsxRuntime.jsx(
460
- reablocks.Button,
461
- {
462
- title: "Stop",
463
- className: reablocks.cn(theme.input.actions.stop),
464
- onClick: stopMessage,
465
- disabled,
466
- children: stopIcon
467
- }
468
- ),
469
- /* @__PURE__ */ jsxRuntime.jsx(
470
- reablocks.Button,
471
- {
472
- title: "Send",
473
- className: reablocks.cn(theme.input.actions.send),
474
- onClick: handleSendMessage,
475
- disabled: isLoading || disabled,
476
- children: sendIcon
477
- }
478
- )
479
- ] })
480
- ] });
532
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: reablocks.cn(theme.input.base, className), children: /* @__PURE__ */ jsxRuntime.jsxs(
533
+ FileDropzone,
534
+ {
535
+ allowedFiles,
536
+ multiple: allowMultipleFiles,
537
+ disabled: disabled || isLoading,
538
+ dropIcon,
539
+ dropText,
540
+ onFileDrop: fileUpload,
541
+ children: [
542
+ /* @__PURE__ */ jsxRuntime.jsx(
543
+ reablocks.Textarea,
544
+ {
545
+ ref: inputRef,
546
+ containerClassName: reablocks.cn(theme.input.input),
547
+ minRows: 1,
548
+ autoFocus: true,
549
+ value: internalMessage,
550
+ defaultValue,
551
+ onKeyPress: handleKeyPress,
552
+ placeholder,
553
+ disabled: isLoading || disabled,
554
+ onChange: handleMessageChange
555
+ }
556
+ ),
557
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: reablocks.cn(theme.input.actions.base), children: [
558
+ allowedFiles?.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
559
+ FileInput,
560
+ {
561
+ allowedFiles,
562
+ multiple: allowMultipleFiles,
563
+ onFileUpload: handleFileUpload,
564
+ disabled: disabled || isLoading,
565
+ attachIcon
566
+ }
567
+ ),
568
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(
569
+ reablocks.Button,
570
+ {
571
+ title: "Stop",
572
+ className: reablocks.cn(theme.input.actions.stop),
573
+ onClick: stopMessage,
574
+ disabled,
575
+ children: stopIcon
576
+ }
577
+ ),
578
+ /* @__PURE__ */ jsxRuntime.jsx(
579
+ reablocks.Button,
580
+ {
581
+ title: "Send",
582
+ className: reablocks.cn(theme.input.actions.send),
583
+ onClick: handleSendMessage,
584
+ disabled: isLoading || disabled,
585
+ children: sendIcon
586
+ }
587
+ )
588
+ ] })
589
+ ]
590
+ }
591
+ ) });
481
592
  }
482
593
  );
594
+ const ChatSuggestion = ({
595
+ content,
596
+ onClick
597
+ }) => {
598
+ const { theme, disabled, isLoading } = React.useContext(ChatContext);
599
+ const handleClick = () => {
600
+ if (disabled || isLoading) return;
601
+ onClick?.(content);
602
+ };
603
+ return /* @__PURE__ */ jsxRuntime.jsx(
604
+ reablocks.Button,
605
+ {
606
+ type: "button",
607
+ variant: "outline",
608
+ disableMargins: true,
609
+ className: reablocks.cn(theme.suggestions.item.base, {
610
+ "opacity-50 cursor-not-allowed": disabled || isLoading
611
+ }),
612
+ onClick: handleClick,
613
+ disabled: disabled || isLoading,
614
+ children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: reablocks.cn(theme.suggestions.item.text), children: content })
615
+ }
616
+ );
617
+ };
618
+ const ChatSuggestions = ({
619
+ suggestions,
620
+ className,
621
+ onSuggestionClick,
622
+ children
623
+ }) => {
624
+ const { theme } = React.useContext(ChatContext);
625
+ if (!suggestions || suggestions.length === 0) {
626
+ return null;
627
+ }
628
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: reablocks.cn(theme.suggestions.base, className), children: suggestions.map((suggestion) => {
629
+ if (children && React.isValidElement(children)) {
630
+ return React.cloneElement(children, {
631
+ key: suggestion.id,
632
+ ...suggestion,
633
+ onClick: onSuggestionClick
634
+ });
635
+ }
636
+ return /* @__PURE__ */ jsxRuntime.jsx(
637
+ ChatSuggestion,
638
+ {
639
+ ...suggestion,
640
+ onClick: onSuggestionClick
641
+ },
642
+ suggestion.id
643
+ );
644
+ }) });
645
+ };
483
646
  const SvgCopy = (props) => /* @__PURE__ */ React__namespace.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: 18, height: 18, viewBox: "0 0 18 18", fill: "none", ...props }, /* @__PURE__ */ React__namespace.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" }));
484
647
  const dark = {
485
648
  'code[class*="language-"]': {
@@ -1599,6 +1762,131 @@
1599
1762
  children: [{ type: "text", value: value.trim() }]
1600
1763
  };
1601
1764
  }
1765
+ const SvgSpinner = (props) => /* @__PURE__ */ React__namespace.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__namespace.createElement("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }));
1766
+ const SvgCheck = (props) => /* @__PURE__ */ React__namespace.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__namespace.createElement("polyline", { points: "20 6 9 17 4 12" }));
1767
+ const SvgError = (props) => /* @__PURE__ */ React__namespace.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__namespace.createElement("circle", { cx: 12, cy: 12, r: 10 }), /* @__PURE__ */ React__namespace.createElement("line", { x1: 15, y1: 9, x2: 9, y2: 15 }), /* @__PURE__ */ React__namespace.createElement("line", { x1: 9, y1: 9, x2: 15, y2: 15 }));
1768
+ const StatusIcon = ({
1769
+ state,
1770
+ className,
1771
+ colorClassName
1772
+ }) => {
1773
+ switch (state) {
1774
+ case "loading":
1775
+ return /* @__PURE__ */ jsxRuntime.jsx(
1776
+ react.motion.div,
1777
+ {
1778
+ animate: { rotate: 360 },
1779
+ transition: { duration: 1, repeat: Infinity, ease: "linear" },
1780
+ className: reablocks.cn(className, colorClassName),
1781
+ children: /* @__PURE__ */ jsxRuntime.jsx(SvgSpinner, { className: "w-full h-full" })
1782
+ }
1783
+ );
1784
+ case "complete":
1785
+ return /* @__PURE__ */ jsxRuntime.jsx(
1786
+ react.motion.div,
1787
+ {
1788
+ initial: { scale: 0, opacity: 0 },
1789
+ animate: { scale: 1, opacity: 1 },
1790
+ transition: { type: "spring", stiffness: 500, damping: 25 },
1791
+ className: reablocks.cn(className, colorClassName),
1792
+ children: /* @__PURE__ */ jsxRuntime.jsx(SvgCheck, { className: "w-full h-full" })
1793
+ }
1794
+ );
1795
+ case "error":
1796
+ return /* @__PURE__ */ jsxRuntime.jsx(
1797
+ react.motion.div,
1798
+ {
1799
+ initial: { scale: 0, opacity: 0 },
1800
+ animate: { scale: 1, opacity: 1 },
1801
+ transition: { type: "spring", stiffness: 500, damping: 25 },
1802
+ className: reablocks.cn(className, colorClassName),
1803
+ children: /* @__PURE__ */ jsxRuntime.jsx(SvgError, { className: "w-full h-full" })
1804
+ }
1805
+ );
1806
+ }
1807
+ };
1808
+ const MessageStatusItem = ({ step }) => {
1809
+ const { theme: chatTheme2 } = React.useContext(ChatContext);
1810
+ const theme = chatTheme2.status.steps.step;
1811
+ const stepStatus = step.status || "loading";
1812
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1813
+ react.motion.div,
1814
+ {
1815
+ initial: { opacity: 0, x: -10 },
1816
+ animate: { opacity: 1, x: 0 },
1817
+ transition: { duration: 0.2 },
1818
+ className: theme.base,
1819
+ children: [
1820
+ /* @__PURE__ */ jsxRuntime.jsx(
1821
+ StatusIcon,
1822
+ {
1823
+ state: stepStatus,
1824
+ className: theme.icon,
1825
+ colorClassName: theme[stepStatus]
1826
+ }
1827
+ ),
1828
+ /* @__PURE__ */ jsxRuntime.jsx(
1829
+ "span",
1830
+ {
1831
+ className: reablocks.cn(
1832
+ theme.text,
1833
+ stepStatus === "loading" && theme.loading,
1834
+ stepStatus === "complete" && theme.complete,
1835
+ stepStatus === "error" && theme.error
1836
+ ),
1837
+ children: step.text
1838
+ }
1839
+ )
1840
+ ]
1841
+ }
1842
+ );
1843
+ };
1844
+ const MessageStatus = ({
1845
+ status = "loading",
1846
+ text,
1847
+ steps,
1848
+ icon,
1849
+ className,
1850
+ children
1851
+ }) => {
1852
+ const { theme: chatTheme2 } = React.useContext(ChatContext);
1853
+ const theme = chatTheme2.status;
1854
+ const Comp = children ? reactSlot.Slot : "div";
1855
+ return /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxRuntime.jsx(
1856
+ react.motion.div,
1857
+ {
1858
+ initial: { opacity: 0, y: -10 },
1859
+ animate: { opacity: 1, y: 0 },
1860
+ exit: { opacity: 0, y: -10 },
1861
+ transition: { duration: 0.2 },
1862
+ children: /* @__PURE__ */ jsxRuntime.jsx(Comp, { className: reablocks.cn(theme.base, className), children: children || /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1863
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: theme.header, children: [
1864
+ icon || /* @__PURE__ */ jsxRuntime.jsx(
1865
+ StatusIcon,
1866
+ {
1867
+ state: status,
1868
+ className: theme.icon.base,
1869
+ colorClassName: theme.icon[status]
1870
+ }
1871
+ ),
1872
+ /* @__PURE__ */ jsxRuntime.jsx(
1873
+ "span",
1874
+ {
1875
+ className: reablocks.cn(
1876
+ theme.text.base,
1877
+ status === "loading" && theme.text.loading,
1878
+ status === "complete" && theme.text.complete,
1879
+ status === "error" && theme.text.error
1880
+ ),
1881
+ children: text
1882
+ }
1883
+ )
1884
+ ] }),
1885
+ steps && steps.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: theme.steps.base, children: steps.map((step) => /* @__PURE__ */ jsxRuntime.jsx(MessageStatusItem, { step }, step.id)) })
1886
+ ] }) })
1887
+ }
1888
+ ) });
1889
+ };
1602
1890
  const SessionEmpty = ({ children }) => {
1603
1891
  const { theme } = React.useContext(ChatContext);
1604
1892
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: reablocks.cn(theme.empty), children });
@@ -2184,6 +2472,7 @@ ${response}`),
2184
2472
  }
2185
2473
  );
2186
2474
  };
2475
+ const SvgArrowDown = (props) => /* @__PURE__ */ React__namespace.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__namespace.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" }));
2187
2476
  const containerVariants = {
2188
2477
  hidden: {},
2189
2478
  visible: {
@@ -2200,11 +2489,32 @@ ${response}`),
2200
2489
  className,
2201
2490
  showMoreText = "Show more",
2202
2491
  autoScroll = true,
2203
- onScroll
2492
+ showLoadMoreButton = false,
2493
+ showScrollBottomButton,
2494
+ loadMoreButtonDisabled,
2495
+ onScroll,
2496
+ onLoadMore
2204
2497
  }) => {
2205
2498
  const { activeSession, theme } = React.useContext(ChatContext);
2206
2499
  const contentRef = React.useRef(null);
2500
+ const messagesRef = React.useRef(null);
2207
2501
  const [isAnimating, setIsAnimating] = React.useState(true);
2502
+ const [iAtBottom, setIsAtBottom] = React.useState(true);
2503
+ React.useEffect(() => {
2504
+ if (!contentRef.current || !showScrollBottomButton) {
2505
+ return;
2506
+ }
2507
+ const handleScroll = debounce(() => {
2508
+ if (contentRef.current) {
2509
+ setIsAtBottom(
2510
+ contentRef.current.scrollHeight - contentRef.current.clientHeight === contentRef.current.scrollTop
2511
+ );
2512
+ }
2513
+ }, 50);
2514
+ const currentRef = contentRef.current;
2515
+ currentRef.addEventListener("scroll", handleScroll);
2516
+ return () => currentRef.removeEventListener("scroll", handleScroll);
2517
+ }, [showScrollBottomButton]);
2208
2518
  React.useEffect(() => {
2209
2519
  if (contentRef.current && autoScroll) {
2210
2520
  requestAnimationFrame(
@@ -2212,10 +2522,18 @@ ${response}`),
2212
2522
  );
2213
2523
  }
2214
2524
  }, [activeSession, autoScroll, isAnimating]);
2215
- function handleShowMore() {
2525
+ const handleShowMore = () => {
2216
2526
  showNext(limit);
2217
2527
  requestAnimationFrame(() => contentRef.current.scrollTop = 0);
2218
- }
2528
+ };
2529
+ const handleScrollToBottom = () => {
2530
+ if (contentRef.current) {
2531
+ contentRef.current.scrollTo({
2532
+ top: contentRef.current.scrollHeight,
2533
+ behavior: "smooth"
2534
+ });
2535
+ }
2536
+ };
2219
2537
  const reversedConvos = React.useMemo(
2220
2538
  () => [...activeSession?.conversations ?? []].reverse(),
2221
2539
  [activeSession]
@@ -2229,46 +2547,73 @@ ${response}`),
2229
2547
  if (!activeSession) {
2230
2548
  return /* @__PURE__ */ jsxRuntime.jsx(SessionEmpty, { children: newSessionContent });
2231
2549
  }
2232
- return /* @__PURE__ */ jsxRuntime.jsxs(
2233
- "div",
2234
- {
2235
- className: reablocks.cn(theme.messages.content, className),
2236
- ref: contentRef,
2237
- onScrollCapture: onScroll,
2238
- children: [
2239
- hasMore && /* @__PURE__ */ jsxRuntime.jsx(
2240
- reablocks.Button,
2550
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 overflow-y-hidden", children: [
2551
+ /* @__PURE__ */ jsxRuntime.jsxs(
2552
+ "div",
2553
+ {
2554
+ className: reablocks.cn(theme.messages.content, className, "h-full"),
2555
+ ref: contentRef,
2556
+ id: activeSession?.id,
2557
+ onScrollCapture: onScroll,
2558
+ children: [
2559
+ (showLoadMoreButton || hasMore) && /* @__PURE__ */ jsxRuntime.jsx(
2560
+ reablocks.Button,
2561
+ {
2562
+ disabled: loadMoreButtonDisabled,
2563
+ variant: "outline",
2564
+ className: reablocks.cn(theme.messages.showMore),
2565
+ fullWidth: true,
2566
+ onClick: onLoadMore ?? handleShowMore,
2567
+ children: showMoreText
2568
+ }
2569
+ ),
2570
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
2571
+ react.motion.div,
2572
+ {
2573
+ ref: messagesRef,
2574
+ variants: containerVariants,
2575
+ initial: "hidden",
2576
+ animate: "visible",
2577
+ onAnimationComplete: () => requestAnimationFrame(() => {
2578
+ setIsAnimating(false);
2579
+ if (contentRef.current && autoScroll) {
2580
+ contentRef.current.scrollTop = contentRef.current.scrollHeight;
2581
+ }
2582
+ }),
2583
+ children: children ? children(convosToRender) : convosToRender.map((conversation, index) => /* @__PURE__ */ jsxRuntime.jsx(
2584
+ SessionMessage,
2585
+ {
2586
+ conversation,
2587
+ isLast: index === convosToRender.length - 1
2588
+ },
2589
+ conversation.id
2590
+ ))
2591
+ },
2592
+ activeSession?.id
2593
+ ) })
2594
+ ]
2595
+ }
2596
+ ),
2597
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: !iAtBottom && showScrollBottomButton && /* @__PURE__ */ jsxRuntime.jsx(
2598
+ react.motion.div,
2599
+ {
2600
+ initial: { y: 100, opacity: 0 },
2601
+ animate: { y: 0, opacity: 1 },
2602
+ exit: { y: 100, opacity: 0 },
2603
+ transition: { duration: 0.3, ease: "easeOut" },
2604
+ className: theme.messages?.message?.scrollToBottom?.container,
2605
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2606
+ reablocks.IconButton,
2241
2607
  {
2242
- variant: "outline",
2243
- className: reablocks.cn(theme.messages.showMore),
2244
- fullWidth: true,
2245
- onClick: handleShowMore,
2246
- children: showMoreText
2608
+ onClick: handleScrollToBottom,
2609
+ className: theme.messages?.message?.scrollToBottom?.button,
2610
+ size: "sm",
2611
+ children: /* @__PURE__ */ jsxRuntime.jsx(SvgArrowDown, {})
2247
2612
  }
2248
- ),
2249
- /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
2250
- react.motion.div,
2251
- {
2252
- variants: containerVariants,
2253
- initial: "hidden",
2254
- animate: "visible",
2255
- onAnimationComplete: () => {
2256
- requestAnimationFrame(() => setIsAnimating(false));
2257
- },
2258
- children: children ? children(convosToRender) : convosToRender.map((conversation, index) => /* @__PURE__ */ jsxRuntime.jsx(
2259
- SessionMessage,
2260
- {
2261
- conversation,
2262
- isLast: index === conversation.length - 1
2263
- },
2264
- conversation.id
2265
- ))
2266
- },
2267
- activeSession?.id
2268
- ) })
2269
- ]
2270
- }
2271
- );
2613
+ )
2614
+ }
2615
+ ) })
2616
+ ] });
2272
2617
  };
2273
2618
  const SessionMessagesHeader = ({ children }) => {
2274
2619
  const { activeSession, theme } = React.useContext(ChatContext);
@@ -2481,7 +2826,10 @@ ${response}`),
2481
2826
  exports2.ChatBubble = ChatBubble;
2482
2827
  exports2.ChatContext = ChatContext;
2483
2828
  exports2.ChatInput = ChatInput;
2829
+ exports2.ChatSuggestion = ChatSuggestion;
2830
+ exports2.ChatSuggestions = ChatSuggestions;
2484
2831
  exports2.CodeHighlighter = CodeHighlighter;
2832
+ exports2.FileDropzone = FileDropzone;
2485
2833
  exports2.FileInput = FileInput;
2486
2834
  exports2.Markdown = Markdown;
2487
2835
  exports2.MessageActions = MessageActions;
@@ -2491,6 +2839,8 @@ ${response}`),
2491
2839
  exports2.MessageResponse = MessageResponse;
2492
2840
  exports2.MessageSource = MessageSource;
2493
2841
  exports2.MessageSources = MessageSources;
2842
+ exports2.MessageStatus = MessageStatus;
2843
+ exports2.MessageStatusItem = MessageStatusItem;
2494
2844
  exports2.NewSessionButton = NewSessionButton;
2495
2845
  exports2.SessionEmpty = SessionEmpty;
2496
2846
  exports2.SessionGroups = SessionGroups;
@@ -2501,6 +2851,7 @@ ${response}`),
2501
2851
  exports2.SessionMessagesHeader = SessionMessagesHeader;
2502
2852
  exports2.SessionsGroup = SessionsGroup;
2503
2853
  exports2.SessionsList = SessionsList;
2854
+ exports2.StatusIcon = StatusIcon;
2504
2855
  exports2.TableComponent = TableComponent;
2505
2856
  exports2.TableDataCell = TableDataCell;
2506
2857
  exports2.TableHeaderCell = TableHeaderCell;