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
@@ -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 });
@@ -1953,11 +2241,15 @@ ${response}`),
1953
2241
  const ref = React.useRef(null);
1954
2242
  const calculateTruncation = React.useCallback(() => {
1955
2243
  const el = ref.current;
1956
- setTruncated(el.scrollHeight > el.clientHeight);
2244
+ if (el) {
2245
+ setTruncated(el.scrollHeight > el.clientHeight);
2246
+ }
1957
2247
  }, []);
1958
2248
  React.useEffect(() => {
1959
2249
  const el = ref.current;
1960
- if (!el || previewLineClamp === false) return;
2250
+ if (!el || previewLineClamp === false) {
2251
+ return;
2252
+ }
1961
2253
  calculateTruncation();
1962
2254
  const resizeObserver = new ResizeObserver(() => {
1963
2255
  calculateTruncation();
@@ -2180,6 +2472,7 @@ ${response}`),
2180
2472
  }
2181
2473
  );
2182
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" }));
2183
2476
  const containerVariants = {
2184
2477
  hidden: {},
2185
2478
  visible: {
@@ -2196,11 +2489,29 @@ ${response}`),
2196
2489
  className,
2197
2490
  showMoreText = "Show more",
2198
2491
  autoScroll = true,
2492
+ showScrollBottomButton = false,
2199
2493
  onScroll
2200
2494
  }) => {
2201
2495
  const { activeSession, theme } = React.useContext(ChatContext);
2202
2496
  const contentRef = React.useRef(null);
2497
+ const messagesRef = React.useRef(null);
2203
2498
  const [isAnimating, setIsAnimating] = React.useState(true);
2499
+ const [iAtBottom, setIsAtBottom] = React.useState(true);
2500
+ React.useEffect(() => {
2501
+ if (!contentRef.current || !showScrollBottomButton) {
2502
+ return;
2503
+ }
2504
+ const handleScroll = debounce(() => {
2505
+ if (contentRef.current) {
2506
+ setIsAtBottom(
2507
+ contentRef.current.scrollHeight - contentRef.current.clientHeight === contentRef.current.scrollTop
2508
+ );
2509
+ }
2510
+ }, 50);
2511
+ const currentRef = contentRef.current;
2512
+ currentRef.addEventListener("scroll", handleScroll);
2513
+ return () => currentRef.removeEventListener("scroll", handleScroll);
2514
+ }, [showScrollBottomButton]);
2204
2515
  React.useEffect(() => {
2205
2516
  if (contentRef.current && autoScroll) {
2206
2517
  requestAnimationFrame(
@@ -2208,10 +2519,18 @@ ${response}`),
2208
2519
  );
2209
2520
  }
2210
2521
  }, [activeSession, autoScroll, isAnimating]);
2211
- function handleShowMore() {
2522
+ const handleShowMore = () => {
2212
2523
  showNext(limit);
2213
2524
  requestAnimationFrame(() => contentRef.current.scrollTop = 0);
2214
- }
2525
+ };
2526
+ const handleScrollToBottom = () => {
2527
+ if (contentRef.current) {
2528
+ contentRef.current.scrollTo({
2529
+ top: contentRef.current.scrollHeight,
2530
+ behavior: "smooth"
2531
+ });
2532
+ }
2533
+ };
2215
2534
  const reversedConvos = React.useMemo(
2216
2535
  () => [...activeSession?.conversations ?? []].reverse(),
2217
2536
  [activeSession]
@@ -2225,46 +2544,72 @@ ${response}`),
2225
2544
  if (!activeSession) {
2226
2545
  return /* @__PURE__ */ jsxRuntime.jsx(SessionEmpty, { children: newSessionContent });
2227
2546
  }
2228
- return /* @__PURE__ */ jsxRuntime.jsxs(
2229
- "div",
2230
- {
2231
- className: reablocks.cn(theme.messages.content, className),
2232
- ref: contentRef,
2233
- onScrollCapture: onScroll,
2234
- children: [
2235
- hasMore && /* @__PURE__ */ jsxRuntime.jsx(
2236
- reablocks.Button,
2547
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 overflow-y-hidden", children: [
2548
+ /* @__PURE__ */ jsxRuntime.jsxs(
2549
+ "div",
2550
+ {
2551
+ className: reablocks.cn(theme.messages.content, className, "h-full"),
2552
+ ref: contentRef,
2553
+ id: activeSession?.id,
2554
+ onScrollCapture: onScroll,
2555
+ children: [
2556
+ hasMore && /* @__PURE__ */ jsxRuntime.jsx(
2557
+ reablocks.Button,
2558
+ {
2559
+ variant: "outline",
2560
+ className: reablocks.cn(theme.messages.showMore),
2561
+ fullWidth: true,
2562
+ onClick: handleShowMore,
2563
+ children: showMoreText
2564
+ }
2565
+ ),
2566
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
2567
+ react.motion.div,
2568
+ {
2569
+ ref: messagesRef,
2570
+ variants: containerVariants,
2571
+ initial: "hidden",
2572
+ animate: "visible",
2573
+ onAnimationComplete: () => requestAnimationFrame(() => {
2574
+ setIsAnimating(false);
2575
+ if (contentRef.current && autoScroll) {
2576
+ contentRef.current.scrollTop = contentRef.current.scrollHeight;
2577
+ }
2578
+ }),
2579
+ children: children ? children(convosToRender) : convosToRender.map((conversation, index) => /* @__PURE__ */ jsxRuntime.jsx(
2580
+ SessionMessage,
2581
+ {
2582
+ conversation,
2583
+ isLast: index === convosToRender.length - 1
2584
+ },
2585
+ conversation.id
2586
+ ))
2587
+ },
2588
+ activeSession?.id
2589
+ ) })
2590
+ ]
2591
+ }
2592
+ ),
2593
+ /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: !iAtBottom && showScrollBottomButton && /* @__PURE__ */ jsxRuntime.jsx(
2594
+ react.motion.div,
2595
+ {
2596
+ initial: { y: 100, opacity: 0 },
2597
+ animate: { y: 0, opacity: 1 },
2598
+ exit: { y: 100, opacity: 0 },
2599
+ transition: { duration: 0.3, ease: "easeOut" },
2600
+ className: theme.messages?.message?.scrollToBottom?.container,
2601
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2602
+ reablocks.IconButton,
2237
2603
  {
2238
- variant: "outline",
2239
- className: reablocks.cn(theme.messages.showMore),
2240
- fullWidth: true,
2241
- onClick: handleShowMore,
2242
- children: showMoreText
2604
+ onClick: handleScrollToBottom,
2605
+ className: theme.messages?.message?.scrollToBottom?.button,
2606
+ size: "sm",
2607
+ children: /* @__PURE__ */ jsxRuntime.jsx(SvgArrowDown, {})
2243
2608
  }
2244
- ),
2245
- /* @__PURE__ */ jsxRuntime.jsx(react.AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
2246
- react.motion.div,
2247
- {
2248
- variants: containerVariants,
2249
- initial: "hidden",
2250
- animate: "visible",
2251
- onAnimationComplete: () => {
2252
- requestAnimationFrame(() => setIsAnimating(false));
2253
- },
2254
- children: children ? children(convosToRender) : convosToRender.map((conversation, index) => /* @__PURE__ */ jsxRuntime.jsx(
2255
- SessionMessage,
2256
- {
2257
- conversation,
2258
- isLast: index === conversation.length - 1
2259
- },
2260
- conversation.id
2261
- ))
2262
- },
2263
- activeSession?.id
2264
- ) })
2265
- ]
2266
- }
2267
- );
2609
+ )
2610
+ }
2611
+ ) })
2612
+ ] });
2268
2613
  };
2269
2614
  const SessionMessagesHeader = ({ children }) => {
2270
2615
  const { activeSession, theme } = React.useContext(ChatContext);
@@ -2477,7 +2822,10 @@ ${response}`),
2477
2822
  exports2.ChatBubble = ChatBubble;
2478
2823
  exports2.ChatContext = ChatContext;
2479
2824
  exports2.ChatInput = ChatInput;
2825
+ exports2.ChatSuggestion = ChatSuggestion;
2826
+ exports2.ChatSuggestions = ChatSuggestions;
2480
2827
  exports2.CodeHighlighter = CodeHighlighter;
2828
+ exports2.FileDropzone = FileDropzone;
2481
2829
  exports2.FileInput = FileInput;
2482
2830
  exports2.Markdown = Markdown;
2483
2831
  exports2.MessageActions = MessageActions;
@@ -2487,6 +2835,8 @@ ${response}`),
2487
2835
  exports2.MessageResponse = MessageResponse;
2488
2836
  exports2.MessageSource = MessageSource;
2489
2837
  exports2.MessageSources = MessageSources;
2838
+ exports2.MessageStatus = MessageStatus;
2839
+ exports2.MessageStatusItem = MessageStatusItem;
2490
2840
  exports2.NewSessionButton = NewSessionButton;
2491
2841
  exports2.SessionEmpty = SessionEmpty;
2492
2842
  exports2.SessionGroups = SessionGroups;
@@ -2497,6 +2847,7 @@ ${response}`),
2497
2847
  exports2.SessionMessagesHeader = SessionMessagesHeader;
2498
2848
  exports2.SessionsGroup = SessionsGroup;
2499
2849
  exports2.SessionsList = SessionsList;
2850
+ exports2.StatusIcon = StatusIcon;
2500
2851
  exports2.TableComponent = TableComponent;
2501
2852
  exports2.TableDataCell = TableDataCell;
2502
2853
  exports2.TableHeaderCell = TableHeaderCell;