@algolia/satellite 2.3.0-rc.1 → 2.3.0-rc.3

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 (38) hide show
  1. package/dist/cjs/Fields/AutoComplete/AutoComplete.js +7 -5
  2. package/dist/cjs/Fields/AutoComplete/types.d.ts +8 -0
  3. package/dist/cjs/Fields/AutoComplete/utils.d.ts +1 -1
  4. package/dist/cjs/Fields/AutoComplete/utils.js +3 -2
  5. package/dist/cjs/Helpers/ShimmerText/ShimmerText.tailwind.js +1 -1
  6. package/dist/cjs/Layout/Chat/ChatContextAccordion/ChatContextAccordion.styles.d.ts +3 -3
  7. package/dist/cjs/Layout/Chat/ChatMessage/ChatMessage.styles.js +8 -2
  8. package/dist/cjs/Layout/Chat/ChatMessageError/ChatMessageError.js +2 -2
  9. package/dist/cjs/Layout/Chat/ChatMessageLoader/ChatMessageLoader.styles.js +1 -1
  10. package/dist/cjs/Layout/Chat/ChatMessages/ChatMessages.d.ts +6 -4
  11. package/dist/cjs/Layout/Chat/ChatMessages/ChatMessages.js +46 -18
  12. package/dist/cjs/Layout/Chat/ChatMessages/useLastMessageHeight.d.ts +2 -4
  13. package/dist/cjs/Layout/Chat/ChatMessages/useLastMessageHeight.js +15 -14
  14. package/dist/cjs/Layout/Chat/ChatPrompt/ChatPrompt.d.ts +19 -11
  15. package/dist/cjs/Layout/Chat/ChatPrompt/ChatPrompt.js +17 -3
  16. package/dist/cjs/Layout/Chat/ChatPrompt/ChatPrompt.styles.js +1 -1
  17. package/dist/cjs/Layout/Chat/ChatPrompt/useFocusTyping.d.ts +12 -0
  18. package/dist/cjs/Layout/Chat/ChatPrompt/useFocusTyping.js +60 -0
  19. package/dist/esm/Fields/AutoComplete/AutoComplete.js +7 -5
  20. package/dist/esm/Fields/AutoComplete/types.d.ts +8 -0
  21. package/dist/esm/Fields/AutoComplete/utils.d.ts +1 -1
  22. package/dist/esm/Fields/AutoComplete/utils.js +3 -2
  23. package/dist/esm/Helpers/ShimmerText/ShimmerText.tailwind.js +1 -1
  24. package/dist/esm/Layout/Chat/ChatContextAccordion/ChatContextAccordion.styles.d.ts +3 -3
  25. package/dist/esm/Layout/Chat/ChatMessage/ChatMessage.styles.js +8 -2
  26. package/dist/esm/Layout/Chat/ChatMessageError/ChatMessageError.js +1 -1
  27. package/dist/esm/Layout/Chat/ChatMessageLoader/ChatMessageLoader.styles.js +1 -1
  28. package/dist/esm/Layout/Chat/ChatMessages/ChatMessages.d.ts +6 -4
  29. package/dist/esm/Layout/Chat/ChatMessages/ChatMessages.js +46 -20
  30. package/dist/esm/Layout/Chat/ChatMessages/useLastMessageHeight.d.ts +2 -4
  31. package/dist/esm/Layout/Chat/ChatMessages/useLastMessageHeight.js +16 -15
  32. package/dist/esm/Layout/Chat/ChatPrompt/ChatPrompt.d.ts +19 -11
  33. package/dist/esm/Layout/Chat/ChatPrompt/ChatPrompt.js +17 -3
  34. package/dist/esm/Layout/Chat/ChatPrompt/ChatPrompt.styles.js +1 -1
  35. package/dist/esm/Layout/Chat/ChatPrompt/useFocusTyping.d.ts +12 -0
  36. package/dist/esm/Layout/Chat/ChatPrompt/useFocusTyping.js +54 -0
  37. package/dist/satellite.min.css +1 -1
  38. package/package.json +2 -2
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useFocusOnTyping = useFocusOnTyping;
7
+ var _react = require("react");
8
+ var isTextualElement = function isTextualElement(el) {
9
+ if (el.isContentEditable) return true;
10
+ var tag = el.tagName.toUpperCase();
11
+ if (tag === "TEXTAREA") return true;
12
+ if (tag === "INPUT") {
13
+ // Empty `type` defaults to "text"
14
+ var t = (el.type || "text").toLowerCase();
15
+ return ["text", "search", "url", "email", "tel", "password", "number", "date", "datetime-local", "month", "week", "time"].includes(t);
16
+ }
17
+ return false;
18
+ };
19
+ var shouldFocusEditable = function shouldFocusEditable(target) {
20
+ var el = target;
21
+ if (!el) return true;
22
+ return !isTextualElement(el);
23
+ };
24
+
25
+ // e.g. <div contentEditable>
26
+
27
+ /**
28
+ * Focus `editableRef` the moment the user types anywhere on the page,
29
+ * *unless* their cursor is already inside an editable control.
30
+ */
31
+ function useFocusOnTyping(editableRef) {
32
+ var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
33
+ _ref$enabled = _ref.enabled,
34
+ enabled = _ref$enabled === void 0 ? true : _ref$enabled,
35
+ _ref$strictChars = _ref.strictChars,
36
+ strictChars = _ref$strictChars === void 0 ? false : _ref$strictChars;
37
+ var handleKeyDown = (0, _react.useCallback)(function (e) {
38
+ if (!enabled || e.ctrlKey || e.altKey || e.metaKey || e.isComposing || e.repeat || (
39
+ // Printable-character test
40
+ strictChars ? !/^[A-Za-z0-9]$/.test(e.key) : e.key.length !== 1)) {
41
+ return;
42
+ }
43
+ var node = editableRef.current;
44
+ if (!node) return;
45
+ if (node.contains(document.activeElement)) return;
46
+ if (shouldFocusEditable(e.target)) {
47
+ node.focus();
48
+ }
49
+ }, [enabled, strictChars, editableRef]);
50
+ (0, _react.useEffect)(function () {
51
+ if (typeof window === "undefined") return; // SSR/test guard
52
+
53
+ window.addEventListener("keydown", handleKeyDown, {
54
+ passive: true
55
+ });
56
+ return function () {
57
+ return window.removeEventListener("keydown", handleKeyDown);
58
+ };
59
+ }, [handleKeyDown]);
60
+ }
@@ -84,6 +84,8 @@ export var AutoComplete = /*#__PURE__*/forwardRef(function (_ref, ref) {
84
84
  value = _ref.value,
85
85
  selectOnBlur = _ref.selectOnBlur,
86
86
  onChange = _ref.onChange,
87
+ _ref$allowDuplicates = _ref.allowDuplicates,
88
+ allowDuplicates = _ref$allowDuplicates === void 0 ? false : _ref$allowDuplicates,
87
89
  options = _ref.options,
88
90
  optionItemComponent = _ref.optionItemComponent,
89
91
  creatable = _ref.creatable,
@@ -151,7 +153,7 @@ export var AutoComplete = /*#__PURE__*/forwardRef(function (_ref, ref) {
151
153
  return !value ? [] : Array.isArray(value) ? value : [value];
152
154
  }, [value]);
153
155
  var items = useMemo(function () {
154
- var results = filter(options !== null && options !== void 0 ? options : [], multiple ? selectedItems : [], inputValue);
156
+ var results = filter(options !== null && options !== void 0 ? options : [], multiple ? selectedItems : [], inputValue, allowDuplicates);
155
157
  var emptyInputValue = !inputValue || inputValue.trim().length === 0;
156
158
  if (creatable && !emptyInputValue) {
157
159
  results.unshift.apply(results, _toConsumableArray(createFromInputValue(options, inputValue)));
@@ -165,7 +167,7 @@ export var AutoComplete = /*#__PURE__*/forwardRef(function (_ref, ref) {
165
167
  });
166
168
  }
167
169
  return results;
168
- }, [creatable, createFromInputValue, inputValue, locale, maxItems, multiple, options, selectedItems, showAllItems]);
170
+ }, [creatable, createFromInputValue, inputValue, locale, maxItems, multiple, options, selectedItems, showAllItems, allowDuplicates]);
169
171
  var hasValue = !!selectedItems.length;
170
172
  var showClearButton = !disabled && clearable && hasValue;
171
173
  var multipleSelection = useMultipleSelection({
@@ -174,7 +176,7 @@ export var AutoComplete = /*#__PURE__*/forwardRef(function (_ref, ref) {
174
176
  if (typeof changes.selectedItems === "undefined") {
175
177
  onChange(null, changes.type);
176
178
  } else if (multiple) {
177
- onChange(uniqBy(changes.selectedItems, function (i) {
179
+ onChange(allowDuplicates ? changes.selectedItems : uniqBy(changes.selectedItems, function (i) {
178
180
  return i.value;
179
181
  }), changes.type);
180
182
  } else {
@@ -370,7 +372,7 @@ export var AutoComplete = /*#__PURE__*/forwardRef(function (_ref, ref) {
370
372
  option: option,
371
373
  editable: !disabled
372
374
  })
373
- }), String(option.value)) : /*#__PURE__*/_jsx(Tag, _objectSpread(_objectSpread({
375
+ }), "".concat(option.value, "-").concat(index)) : /*#__PURE__*/_jsx(Tag, _objectSpread(_objectSpread({
374
376
  onRemove: disabled ? undefined : function (evt) {
375
377
  evt.stopPropagation();
376
378
  multipleSelection.removeSelectedItem(option);
@@ -381,7 +383,7 @@ export var AutoComplete = /*#__PURE__*/forwardRef(function (_ref, ref) {
381
383
  index: index
382
384
  })), {}, {
383
385
  children: option.label
384
- }), String(option.value));
386
+ }), "".concat(option.value, "-").concat(index));
385
387
  }), shouldRenderCustomTemplate && renderValueTemplate({
386
388
  option: value,
387
389
  editable: !disabled
@@ -173,6 +173,10 @@ export interface AutoCompleteSingleProps<T extends Option = Option> extends Auto
173
173
  * The callback function that is called when the value of the `AutoComplete` changes.
174
174
  */
175
175
  onChange: (option: T | null, changeType?: AutoCompleteChangeTypesValue) => void;
176
+ /**
177
+ * Whether the `AutoComplete` allows duplicate selections (only in `multiple` mode).
178
+ */
179
+ allowDuplicates?: never;
176
180
  }
177
181
  declare type UseMultipleSelectionStateChangeTypesKey = keyof typeof UseMultipleSelectionStateChangeTypes;
178
182
  export declare type AutoCompleteChangeTypesValue = (typeof UseMultipleSelectionStateChangeTypes)[UseMultipleSelectionStateChangeTypesKey];
@@ -189,6 +193,10 @@ export interface AutoCompleteMultiProps<T extends Option = Option> extends AutoC
189
193
  * The callback function that is called when the value of the `AutoComplete` changes.
190
194
  */
191
195
  onChange: (option: T[] | null, changeType?: AutoCompleteChangeTypesValue) => void;
196
+ /**
197
+ * Whether the `AutoComplete` allows duplicate selections (only in `multiple` mode).
198
+ */
199
+ allowDuplicates?: boolean;
192
200
  }
193
201
  export declare type AutoCompleteProps<T extends Option = Option> = AutoCompleteMultiProps<T> | AutoCompleteSingleProps<T>;
194
202
  declare type OptionsValue = string | number | boolean;
@@ -4,5 +4,5 @@ export declare const defaultCreateFromInputValue: <T extends Option = Option>(op
4
4
  export declare const caseInsensitiveCreateFromInputValue: <T extends Option = Option>(options: T[] | undefined, inputValue: string) => Option[];
5
5
  export declare const DEFAULT_AUTOCOMPLETE_LOCALE: Required<AutoCompleteLocale>;
6
6
  export declare const optionToString: (option: Option | null | undefined) => string;
7
- export declare function filter<T extends Option = Option>(options: T[], selectedItems: T[], itemValue: string): T[];
7
+ export declare function filter<T extends Option = Option>(options: T[], selectedItems: T[], itemValue: string, allowDuplicates?: boolean): T[];
8
8
  export declare function inputValueFromProps<T extends Option = Option>(props: AutoCompleteProps<T>): string;
@@ -55,12 +55,13 @@ function needlesMatch(needles, option) {
55
55
  });
56
56
  }
57
57
  export function filter(options, selectedItems, itemValue) {
58
+ var allowDuplicates = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
58
59
  var needles = itemValue.toLowerCase().trim().split(/\s+/);
59
60
  return options.filter(function (option) {
60
- return !selectedItems.some(function (_ref) {
61
+ return (allowDuplicates || !selectedItems.some(function (_ref) {
61
62
  var value = _ref.value;
62
63
  return value === option.value;
63
- }) && (option.bypassNeedleMatch || needlesMatch(needles, option));
64
+ })) && (option.bypassNeedleMatch || needlesMatch(needles, option));
64
65
  });
65
66
  }
66
67
  export function inputValueFromProps(props) {
@@ -13,7 +13,7 @@ var shimmerTextPlugin = plugin(function (_ref) {
13
13
  "animation-duration": "3s",
14
14
  "animation-iteration-count": "infinite",
15
15
  "animation-name": "shimmer-text",
16
- background: "".concat(theme("colors.grey.900"), " linear-gradient(to right, ").concat(theme("colors.grey.900"), " 0%, #ffffffbf 40%, #ffffffbf 60%, ").concat(theme("colors.grey.900"), " 100%)"),
16
+ background: "".concat(theme("colors.grey.600"), " linear-gradient(to right, ").concat(theme("colors.grey.600"), " 0%, #ffffffbf 40%, #ffffffbf 60%, ").concat(theme("colors.grey.600"), " 100%)"),
17
17
  "-webkit-background-clip": "text",
18
18
  "background-clip": "text",
19
19
  "background-repeat": "no-repeat",
@@ -6,8 +6,8 @@ export declare const chatContextAccordionStyles: import("tailwind-variants").TVR
6
6
  title?: import("tailwind-merge").ClassNameValue;
7
7
  content?: import("tailwind-merge").ClassNameValue;
8
8
  item?: import("tailwind-merge").ClassNameValue;
9
- trigger?: import("tailwind-merge").ClassNameValue;
10
9
  iconBg?: import("tailwind-merge").ClassNameValue;
10
+ trigger?: import("tailwind-merge").ClassNameValue;
11
11
  titleWrapper?: import("tailwind-merge").ClassNameValue;
12
12
  };
13
13
  };
@@ -19,8 +19,8 @@ export declare const chatContextAccordionStyles: import("tailwind-variants").TVR
19
19
  title?: import("tailwind-merge").ClassNameValue;
20
20
  content?: import("tailwind-merge").ClassNameValue;
21
21
  item?: import("tailwind-merge").ClassNameValue;
22
- trigger?: import("tailwind-merge").ClassNameValue;
23
22
  iconBg?: import("tailwind-merge").ClassNameValue;
23
+ trigger?: import("tailwind-merge").ClassNameValue;
24
24
  titleWrapper?: import("tailwind-merge").ClassNameValue;
25
25
  };
26
26
  };
@@ -41,8 +41,8 @@ export declare const chatContextAccordionStyles: import("tailwind-variants").TVR
41
41
  title?: import("tailwind-merge").ClassNameValue;
42
42
  content?: import("tailwind-merge").ClassNameValue;
43
43
  item?: import("tailwind-merge").ClassNameValue;
44
- trigger?: import("tailwind-merge").ClassNameValue;
45
44
  iconBg?: import("tailwind-merge").ClassNameValue;
45
+ trigger?: import("tailwind-merge").ClassNameValue;
46
46
  titleWrapper?: import("tailwind-merge").ClassNameValue;
47
47
  };
48
48
  };
@@ -1,5 +1,5 @@
1
1
  import _taggedTemplateLiteral from "@babel/runtime/helpers/taggedTemplateLiteral";
2
- var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7, _templateObject8, _templateObject9, _templateObject10, _templateObject11, _templateObject12, _templateObject13, _templateObject14, _templateObject15, _templateObject16, _templateObject17;
2
+ var _templateObject, _templateObject2, _templateObject3, _templateObject4, _templateObject5, _templateObject6, _templateObject7, _templateObject8, _templateObject9, _templateObject10, _templateObject11, _templateObject12, _templateObject13, _templateObject14, _templateObject15, _templateObject16, _templateObject17, _templateObject18;
3
3
  import stl from "./../../../styles/helpers/satellitePrefixer";
4
4
  import { tv } from "./../../../styles/helpers/tv";
5
5
  export var chatMessageStyles = tv({
@@ -7,7 +7,7 @@ export var chatMessageStyles = tv({
7
7
  base: stl(_templateObject || (_templateObject = _taggedTemplateLiteral(["relative w-full scroll-mt-4 group/message"]))),
8
8
  container: stl(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["relative flex items-start group-data-[role=user]/message:max-w-[70%] gap-3"]))),
9
9
  leading: stl(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["inline-flex items-center justify-center"]))),
10
- content: stl(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["flex flex-col gap-3 group-data-[role=assistant]/message:grow overflow-x-auto"]))),
10
+ content: stl(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["flex flex-col gap-3 group-data-[role=assistant]/message:grow overflow-y-hidden overflow-x-auto"]))),
11
11
  message: stl(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["relative text-pretty typo-display-body"]))),
12
12
  actions: stl(_templateObject6 || (_templateObject6 = _taggedTemplateLiteral(["flex items-center gap-1"])))
13
13
  },
@@ -56,6 +56,12 @@ export var chatMessageStyles = tv({
56
56
  className: {
57
57
  actions: stl(_templateObject17 || (_templateObject17 = _taggedTemplateLiteral(["opacity-0 group-hover/message:opacity-100 transition-opacity"])))
58
58
  }
59
+ }, {
60
+ actions: true,
61
+ side: "right",
62
+ className: {
63
+ actions: stl(_templateObject18 || (_templateObject18 = _taggedTemplateLiteral(["justify-end"])))
64
+ }
59
65
  }],
60
66
  defaultVariants: {
61
67
  variant: "subtle",
@@ -3,10 +3,10 @@ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProper
3
3
  var _excluded = ["renderError", "actions", "renderFooter", "locale", "onReload", "rtl", "className"];
4
4
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
5
5
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
6
- import { RotateCwIcon } from "lucide-react";
7
6
  import { forwardRef } from "react";
8
7
  import { chatMessageErrorStyles } from "./ChatMessageError.styles";
9
8
  import { Button } from "./../../../Actions";
9
+ import { RotateCwIcon } from "./../../../Icons";
10
10
  import { useLocale } from "./../../../Satellite";
11
11
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  var DEFAULT_CHAT_MESSAGE_ERROR_LOCALE = {
@@ -7,7 +7,7 @@ export var chatMessageLoaderStyles = tv({
7
7
  extend: chatMessageStyles,
8
8
  slots: {
9
9
  content: stl(_templateObject || (_templateObject = _taggedTemplateLiteral(["w-full"]))),
10
- message: stl(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["flex flex-col gap-2 typo-subdued"]))),
10
+ message: stl(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["flex flex-col gap-2"]))),
11
11
  skeletonWrapper: stl(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["flex flex-col gap-1"]))),
12
12
  skeleton: stl(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["h-4"])))
13
13
  }
@@ -6,10 +6,11 @@ import type { ChatMessageBase, ChatStatus } from "../../../Layout/Chat/types";
6
6
  export declare type ChatMessageLocale = {
7
7
  scrollToBottomText?: string;
8
8
  };
9
+ declare type ChatMessageWithoutContent<Message extends ChatMessageBase> = Omit<ChatMessageProps<Message>, "content">;
9
10
  export declare type ChatMessagesProps<Message extends ChatMessageBase = ChatMessageBase> = Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
10
11
  messages: Message[];
11
- userMessageProps?: Omit<ChatMessageProps<Message>, "content">;
12
- assistantMessageProps?: Omit<ChatMessageProps<Message>, "content">;
12
+ userMessageProps?: ChatMessageWithoutContent<Message> | ((message: Message) => ChatMessageWithoutContent<Message>);
13
+ assistantMessageProps?: ChatMessageWithoutContent<Message> | ((message: Message) => ChatMessageWithoutContent<Message>);
13
14
  loaderProps?: ChatMessageLoaderProps;
14
15
  errorProps?: Omit<ChatMessageErrorProps, "onReload">;
15
16
  renderMessage?: (message: Message) => ReactNode;
@@ -24,8 +25,8 @@ export declare type ChatMessagesProps<Message extends ChatMessageBase = ChatMess
24
25
  };
25
26
  export declare const ChatMessages: <Message extends ChatMessageBase = ChatMessageBase>(props: Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
26
27
  messages: Message[];
27
- userMessageProps?: Omit<ChatMessageProps<Message>, "content"> | undefined;
28
- assistantMessageProps?: Omit<ChatMessageProps<Message>, "content"> | undefined;
28
+ userMessageProps?: ChatMessageWithoutContent<Message> | ((message: Message) => ChatMessageWithoutContent<Message>) | undefined;
29
+ assistantMessageProps?: ChatMessageWithoutContent<Message> | ((message: Message) => ChatMessageWithoutContent<Message>) | undefined;
29
30
  loaderProps?: ChatMessageLoaderProps | undefined;
30
31
  errorProps?: Omit<ChatMessageErrorProps, "onReload"> | undefined;
31
32
  renderMessage?: ((message: Message) => ReactNode) | undefined;
@@ -40,3 +41,4 @@ export declare const ChatMessages: <Message extends ChatMessageBase = ChatMessag
40
41
  } & {
41
42
  ref?: Ref<HTMLDivElement> | undefined;
42
43
  }) => ReactElement | null;
44
+ export {};
@@ -5,14 +5,12 @@ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProper
5
5
  var _excluded = ["messages", "userMessageProps", "assistantMessageProps", "loaderProps", "errorProps", "renderMessage", "renderLoader", "renderError", "status", "onReload", "hideScrollToBottom", "className", "scrollClassName", "contentClassName", "locale"];
6
6
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
7
7
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
8
- import { ChevronDownIcon } from "lucide-react";
9
- import { useEffect } from "react";
10
- import { useRef, useState } from "react";
11
- import { forwardRef } from "react";
8
+ import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
12
9
  import { useStickToBottom } from "use-stick-to-bottom";
13
10
  import { chatMessagesStyles } from "./ChatMessages.styles";
14
11
  import { useLastMessageHeight } from "./useLastMessageHeight";
15
12
  import { IconButton } from "./../../../Actions";
13
+ import { ChevronDownIcon } from "./../../../Icons";
16
14
  import { ChatMessage } from "./../ChatMessage";
17
15
  import { ChatMessageError } from "./../ChatMessageError";
18
16
  import { ChatMessageLoader } from "./../ChatMessageLoader";
@@ -21,6 +19,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
21
19
  var DEFAULT_CHAT_MESSAGE_LOCALE = {
22
20
  scrollToBottomText: "Scroll to bottom"
23
21
  };
22
+ function resolveProps(propsOrFn, message) {
23
+ if (typeof propsOrFn === "function") {
24
+ return propsOrFn(message);
25
+ }
26
+ return propsOrFn;
27
+ }
24
28
  var ChatMessagesBase = function ChatMessagesBase(_ref, forwardedRef) {
25
29
  var messages = _ref.messages,
26
30
  userMessageProps = _ref.userMessageProps,
@@ -42,15 +46,16 @@ var ChatMessagesBase = function ChatMessagesBase(_ref, forwardedRef) {
42
46
  var contextLocale = useLocale("chatMessage");
43
47
  var locale = _objectSpread(_objectSpread(_objectSpread({}, DEFAULT_CHAT_MESSAGE_LOCALE), contextLocale), propsLocale);
44
48
  var _useStickToBottom = useStickToBottom({
45
- resize: "smooth",
46
- initial: "smooth"
49
+ // @ts-expect-error - Types are wrong in use-stick-to-bottom
50
+ initial: "instant",
51
+ resize: "smooth"
47
52
  }),
48
53
  scrollRef = _useStickToBottom.scrollRef,
49
54
  contentRef = _useStickToBottom.contentRef,
50
55
  isAtBottom = _useStickToBottom.isAtBottom,
51
56
  scrollToBottom = _useStickToBottom.scrollToBottom;
52
57
  var styles = chatMessagesStyles();
53
- var messageRefs = useRef(new Map());
58
+ var lastUserMessageRef = useRef(null);
54
59
  var _useState = useState(false),
55
60
  _useState2 = _slicedToArray(_useState, 2),
56
61
  lastMessageSubmitted = _useState2[0],
@@ -58,19 +63,41 @@ var ChatMessagesBase = function ChatMessagesBase(_ref, forwardedRef) {
58
63
  var lastUserMessage = _toConsumableArray(messages).reverse().find(function (m) {
59
64
  return m.role === "user";
60
65
  });
61
- var lastUserMessageEl = lastUserMessage ? messageRefs.current.get(lastUserMessage.id) : undefined;
62
- var lastMessageHeight = useLastMessageHeight(scrollRef, messages, lastUserMessageEl, lastMessageSubmitted, 24);
66
+ var lastMessageHeight = useLastMessageHeight(scrollRef, lastUserMessageRef, messages, lastMessageSubmitted, 24);
67
+ var lastMessageId = useMemo(function () {
68
+ var _messages;
69
+ return (_messages = messages[messages.length - 1]) === null || _messages === void 0 ? void 0 : _messages.id;
70
+ }, [messages]);
71
+ var getMessageProps = useCallback(function (message) {
72
+ var propsResult;
73
+ if (message.role === "assistant") {
74
+ propsResult = resolveProps(assistantMessageProps, message);
75
+
76
+ // If the last assistant message is streaming, we don't want to render actions or footer
77
+ if (status === "streaming" && lastMessageId === message.id) {
78
+ propsResult = _objectSpread(_objectSpread({}, propsResult), {}, {
79
+ renderFooter: undefined,
80
+ renderActions: undefined,
81
+ actions: undefined
82
+ });
83
+ }
84
+ } else {
85
+ propsResult = resolveProps(userMessageProps, message);
86
+ }
87
+ return propsResult;
88
+ }, [assistantMessageProps, userMessageProps, status, lastMessageId]);
63
89
  useEffect(function () {
64
- if (status !== "submitted" || !lastUserMessage) {
65
- return;
90
+ if (status === "submitted" && lastUserMessage) {
91
+ setLastMessageSubmitted(true);
66
92
  }
67
- setLastMessageSubmitted(true);
68
- }, [status, messages, lastUserMessage]);
93
+ }, [status, lastUserMessage]);
69
94
  return /*#__PURE__*/_jsxs("div", _objectSpread(_objectSpread({}, props), {}, {
70
95
  ref: forwardedRef,
71
96
  className: styles.base({
72
97
  className: className
73
98
  }),
99
+ role: "log",
100
+ "aria-live": "polite",
74
101
  children: [/*#__PURE__*/_jsx("div", {
75
102
  ref: scrollRef,
76
103
  className: styles.scroll({
@@ -86,16 +113,15 @@ var ChatMessagesBase = function ChatMessagesBase(_ref, forwardedRef) {
86
113
  },
87
114
  children: [messages.map(function (message) {
88
115
  var isAssistant = message.role === "assistant";
116
+ var isLastUserMessage = (lastUserMessage === null || lastUserMessage === void 0 ? void 0 : lastUserMessage.id) === message.id;
89
117
  return /*#__PURE__*/_jsx(ChatMessage, _objectSpread(_objectSpread({
90
- ref: function ref(el) {
91
- if (el) messageRefs.current.set(message.id, el);else messageRefs.current["delete"](message.id);
92
- },
118
+ ref: isLastUserMessage ? lastUserMessageRef : undefined,
93
119
  side: isAssistant ? "left" : "right",
94
- variant: isAssistant ? "subtle" : "neutral",
95
- content: renderMessage ? renderMessage(message) : message.content
96
- }, isAssistant ? assistantMessageProps : userMessageProps), {}, {
97
- "data-role": message.role,
120
+ variant: isAssistant ? "subtle" : "neutral"
121
+ }, getMessageProps(message)), {}, {
122
+ content: renderMessage ? renderMessage(message) : message.content,
98
123
  actionsExtraData: message,
124
+ "data-role": message.role,
99
125
  "data-key": message.id
100
126
  }), message.id);
101
127
  }), status === "submitted" && (renderLoader !== null && renderLoader !== void 0 ? renderLoader : /*#__PURE__*/_jsx(ChatMessageLoader, _objectSpread({}, loaderProps))), status === "error" && (renderError !== null && renderError !== void 0 ? renderError : /*#__PURE__*/_jsx(ChatMessageError, _objectSpread(_objectSpread({}, errorProps), {}, {
@@ -1,10 +1,8 @@
1
1
  import type { RefObject } from "react";
2
+ import type { ChatMessageBase } from "../../../Layout/Chat/types";
2
3
  /**
3
4
  * React hook to compute the “fill” height below the last user message,
4
5
  * based on the scroll container’s height, the last user‐message element’s height,
5
6
  * and any CSS gap/padding offsets.
6
7
  */
7
- export declare function useLastMessageHeight(scrollRef: RefObject<HTMLElement>, messages: Array<{
8
- id: string;
9
- role: string;
10
- }>, lastUserMessageEl: HTMLElement | undefined, lastMessageSubmitted: boolean, spacingOffset?: number): number;
8
+ export declare function useLastMessageHeight(scrollRef: RefObject<HTMLElement>, lastUserMessageRef: RefObject<HTMLDivElement | null>, messages: ChatMessageBase[], lastMessageSubmitted: boolean, spacingOffset?: number): number;
@@ -1,34 +1,35 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import { useRef } from "react";
2
3
  import { useLayoutEffect, useState } from "react";
3
-
4
4
  /**
5
5
  * React hook to compute the “fill” height below the last user message,
6
6
  * based on the scroll container’s height, the last user‐message element’s height,
7
7
  * and any CSS gap/padding offsets.
8
8
  */
9
- export function useLastMessageHeight(scrollRef, messages, lastUserMessageEl, lastMessageSubmitted) {
9
+ export function useLastMessageHeight(scrollRef, lastUserMessageRef, messages, lastMessageSubmitted) {
10
10
  var spacingOffset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
11
11
  var _useState = useState(0),
12
12
  _useState2 = _slicedToArray(_useState, 2),
13
13
  lastMessageHeight = _useState2[0],
14
14
  setLastMessageHeight = _useState2[1];
15
- var updateLastMessageHeight = function updateLastMessageHeight() {
16
- var scrollEl = scrollRef.current;
17
- if (!scrollEl || messages.length === 0 || !lastMessageSubmitted || !lastUserMessageEl) return;
18
-
19
- // Measure parent/container height
20
- var parentHeight = scrollEl.clientHeight;
15
+ var offsetsRef = useRef(0);
16
+ function getStaticOffsets() {
17
+ if (offsetsRef.current || !scrollRef.current) return offsetsRef.current;
21
18
 
22
19
  // Compute any CSS gap/rowGap/padding offsets by reading computed style
23
- var computed = window.getComputedStyle(scrollEl);
20
+ var computed = window.getComputedStyle(scrollRef.current);
24
21
  var rowGapValue = parseFloat(computed.rowGap) || parseFloat(computed.gap) || 0;
25
22
  var paddingTop = parseFloat(computed.paddingTop) || 0;
26
23
  var paddingBottom = parseFloat(computed.paddingBottom) || 0;
27
- var totalOffset = spacingOffset + rowGapValue + paddingTop + paddingBottom;
28
-
29
- // The “remaining” height is parentHeight − lastMsgElem.offsetHeight − totalOffset
30
- var remaining = parentHeight - lastUserMessageEl.offsetHeight - totalOffset;
31
- setLastMessageHeight(Math.max(remaining, 0));
24
+ offsetsRef.current = spacingOffset + rowGapValue + paddingTop + paddingBottom;
25
+ return offsetsRef.current;
26
+ }
27
+ var updateLastMessageHeight = function updateLastMessageHeight() {
28
+ var scrollEl = scrollRef.current;
29
+ var lastUserMessageEl = lastUserMessageRef.current;
30
+ if (!scrollEl || messages.length === 0 || !lastMessageSubmitted || !lastUserMessageEl) return;
31
+ var height = Math.max(scrollEl.clientHeight - lastUserMessageEl.offsetHeight - getStaticOffsets(), 0);
32
+ setLastMessageHeight(height);
32
33
  };
33
34
 
34
35
  // Recompute whenever messages change and a new user message has just been submitted
@@ -36,6 +37,6 @@ export function useLastMessageHeight(scrollRef, messages, lastUserMessageEl, las
36
37
  if (!lastMessageSubmitted) return;
37
38
  updateLastMessageHeight();
38
39
  // eslint-disable-next-line react-hooks/exhaustive-deps
39
- }, [messages, lastMessageSubmitted]);
40
+ }, [messages.length, lastMessageSubmitted]);
40
41
  return lastMessageHeight;
41
42
  }
@@ -1,5 +1,5 @@
1
1
  import type { FormEvent, KeyboardEvent, ReactNode } from "react";
2
- import type { TextAreaProps } from "../../../Fields";
2
+ import type { TextAreaAutoSizeProps } from "../../../Fields/TextAreaAutoSize/TextAreaAutoSize";
3
3
  import type { ChatStatus } from "../../../Layout/Chat/types";
4
4
  export declare type ChatPromptLocale = {
5
5
  textareaLabel?: string;
@@ -7,7 +7,7 @@ export declare type ChatPromptLocale = {
7
7
  stopResponseTooltip?: string;
8
8
  sendMessageTooltip?: string;
9
9
  };
10
- export declare type ChatPromptProps = Omit<TextAreaProps, "onSubmit"> & {
10
+ export declare type ChatPromptProps = Omit<TextAreaAutoSizeProps, "onSubmit"> & {
11
11
  /**
12
12
  * Content to render above the textarea (e.g., a title or instructions).
13
13
  */
@@ -30,6 +30,14 @@ export declare type ChatPromptProps = Omit<TextAreaProps, "onSubmit"> & {
30
30
  * - "error": An error occurred during the API request, preventing successful completion.
31
31
  */
32
32
  status?: ChatStatus;
33
+ /**
34
+ * Whether to automatically focus the textarea when the user starts typing.
35
+ */
36
+ autoFocusOnTyping?: boolean;
37
+ /**
38
+ * Optional locale.
39
+ */
40
+ locale?: ChatPromptLocale;
33
41
  /**
34
42
  * Callback invoked when the user submits the form, either by clicking
35
43
  * the submit button or pressing Enter (without Shift) inside the textarea.
@@ -39,12 +47,8 @@ export declare type ChatPromptProps = Omit<TextAreaProps, "onSubmit"> & {
39
47
  * Callback invoked when the user requests to stop the current chat response stream.
40
48
  */
41
49
  onStop?: () => void;
42
- /**
43
- * Optional locale.
44
- */
45
- locale?: ChatPromptLocale;
46
50
  };
47
- export declare const ChatPrompt: import("react").ForwardRefExoticComponent<Omit<TextAreaProps, "onSubmit"> & {
51
+ export declare const ChatPrompt: import("react").ForwardRefExoticComponent<Omit<TextAreaAutoSizeProps, "onSubmit"> & {
48
52
  /**
49
53
  * Content to render above the textarea (e.g., a title or instructions).
50
54
  */
@@ -67,6 +71,14 @@ export declare const ChatPrompt: import("react").ForwardRefExoticComponent<Omit<
67
71
  * - "error": An error occurred during the API request, preventing successful completion.
68
72
  */
69
73
  status?: ChatStatus | undefined;
74
+ /**
75
+ * Whether to automatically focus the textarea when the user starts typing.
76
+ */
77
+ autoFocusOnTyping?: boolean | undefined;
78
+ /**
79
+ * Optional locale.
80
+ */
81
+ locale?: ChatPromptLocale | undefined;
70
82
  /**
71
83
  * Callback invoked when the user submits the form, either by clicking
72
84
  * the submit button or pressing Enter (without Shift) inside the textarea.
@@ -76,8 +88,4 @@ export declare const ChatPrompt: import("react").ForwardRefExoticComponent<Omit<
76
88
  * Callback invoked when the user requests to stop the current chat response stream.
77
89
  */
78
90
  onStop?: (() => void) | undefined;
79
- /**
80
- * Optional locale.
81
- */
82
- locale?: ChatPromptLocale | undefined;
83
91
  } & import("react").RefAttributes<HTMLTextAreaElement>>;
@@ -1,18 +1,20 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
4
- var _excluded = ["className", "renderHeader", "children", "renderFooter", "onSubmit", "onStop", "placeholder", "placeholders", "status", "defaultValue", "value", "locale"];
4
+ var _excluded = ["className", "renderHeader", "children", "renderFooter", "onSubmit", "onStop", "placeholder", "placeholders", "status", "autoFocusOnTyping", "defaultValue", "value", "locale", "id"];
5
5
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
6
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
- import { ArrowUpIcon, CircleStopIcon } from "lucide-react";
8
7
  import { useState } from "react";
9
8
  import { useRef } from "react";
10
9
  import { forwardRef } from "react";
11
10
  import { chatPromptStyles } from "./ChatPrompt.styles";
11
+ import { useFocusOnTyping } from "./useFocusTyping";
12
12
  import { useTypewriter } from "./useTypewriter";
13
13
  import { IconButton } from "./../../../Actions";
14
14
  import { TextAreaAutoSize } from "./../../../Fields/TextAreaAutoSize/TextAreaAutoSize";
15
+ import { ArrowUpIcon, StopCircleIcon } from "./../../../Icons";
15
16
  import { useLocale } from "./../../../Satellite";
17
+ import { uniqueId } from "./../../../utils";
16
18
  import { mergeRefs } from "./../../../utils/mergeRefs";
17
19
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
18
20
  var DEFAULT_CHAT_PROMPT_LOCALE = {
@@ -34,12 +36,18 @@ export var ChatPrompt = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
34
36
  placeholders = _ref$placeholders === void 0 ? [] : _ref$placeholders,
35
37
  _ref$status = _ref.status,
36
38
  status = _ref$status === void 0 ? "ready" : _ref$status,
39
+ autoFocusOnTyping = _ref.autoFocusOnTyping,
37
40
  defaultValue = _ref.defaultValue,
38
41
  valueProp = _ref.value,
39
42
  propsLocale = _ref.locale,
43
+ propId = _ref.id,
40
44
  props = _objectWithoutProperties(_ref, _excluded);
41
45
  var contextLocale = useLocale("chatPrompt");
42
46
  var locale = _objectSpread(_objectSpread(_objectSpread({}, DEFAULT_CHAT_PROMPT_LOCALE), contextLocale), propsLocale);
47
+
48
+ // eslint-disable-next-line @algolia/stl/prefer-stl-helper
49
+ var generatedId = uniqueId("stl-chat-prompt-");
50
+ var textareaId = propId !== null && propId !== void 0 ? propId : generatedId;
43
51
  var internalRef = useRef(null);
44
52
  var styles = chatPromptStyles();
45
53
  var _useState = useState(defaultValue !== null && defaultValue !== void 0 ? defaultValue : ""),
@@ -53,6 +61,10 @@ export var ChatPrompt = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
53
61
  var buttonDisabled = !hasValue && !canStop;
54
62
  var dynamicPlaceholder = useTypewriter(placeholders, !hasValue);
55
63
  var computedPlaceholder = placeholders.length > 0 ? dynamicPlaceholder : staticPlaceholder;
64
+ useFocusOnTyping(internalRef, {
65
+ enabled: autoFocusOnTyping,
66
+ strictChars: true
67
+ });
56
68
  var submit = function submit(e) {
57
69
  e.preventDefault();
58
70
  if (!hasValue || canStop) {
@@ -74,6 +86,7 @@ export var ChatPrompt = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
74
86
  (_internalRef$current = internalRef.current) === null || _internalRef$current === void 0 || _internalRef$current.focus();
75
87
  },
76
88
  children: /*#__PURE__*/_jsx(TextAreaAutoSize, _objectSpread(_objectSpread({
89
+ id: textareaId,
77
90
  ref: mergeRefs([forwardedRef, internalRef]),
78
91
  rows: 2,
79
92
  maxRows: 8,
@@ -81,6 +94,7 @@ export var ChatPrompt = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
81
94
  value: value,
82
95
  "aria-label": (_ref2 = placeholders[0] ? placeholders[0] : staticPlaceholder) !== null && _ref2 !== void 0 ? _ref2 : locale.textareaLabel,
83
96
  translate: "no",
97
+ autoComplete: "off",
84
98
  onInput: function onInput(e) {
85
99
  var _props$onInput;
86
100
  if (!isControlled) {
@@ -112,7 +126,7 @@ export var ChatPrompt = /*#__PURE__*/forwardRef(function (_ref, forwardedRef) {
112
126
  onStop === null || onStop === void 0 || onStop();
113
127
  }
114
128
  },
115
- icon: canStop ? CircleStopIcon : ArrowUpIcon,
129
+ icon: canStop ? StopCircleIcon : ArrowUpIcon,
116
130
  variant: canStop ? "neutral" : "primary",
117
131
  size: "small",
118
132
  title: buttonDisabled ? locale.emptyMessageTooltip : canStop ? locale.stopResponseTooltip : locale.sendMessageTooltip,
@@ -8,7 +8,7 @@ export var chatPromptStyles = tv({
8
8
  header: stl(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["flex items-center gap-1.5"]))),
9
9
  body: stl(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral([""]))),
10
10
  textarea: stl(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["[&>textarea]:p-3 [&>textarea]:pb-0 [&>textarea]:thin-scrollbar"]))),
11
- actions: stl(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["flex flex-row p-3 pt-2 justify-end"]))),
11
+ actions: stl(_templateObject5 || (_templateObject5 = _taggedTemplateLiteral(["flex flex-row p-3 pt-2 justify-end cursor-text"]))),
12
12
  submit: stl(_templateObject6 || (_templateObject6 = _taggedTemplateLiteral([""]))),
13
13
  footer: stl(_templateObject7 || (_templateObject7 = _taggedTemplateLiteral(["flex items-center justify-between gap-1.5"])))
14
14
  }