UNPKG

react-instantsearch

Version:
345 lines (342 loc) 18 kB
import { _ as _$2 } from '@swc/helpers/esm/_call_super.js'; import { _ as _$1 } from '@swc/helpers/esm/_class_call_check.js'; import { _ as _$3 } from '@swc/helpers/esm/_create_class.js'; import { _ as _$4 } from '@swc/helpers/esm/_define_property.js'; import { _ } from '@swc/helpers/esm/_inherits.js'; import { _ as _$6 } from '@swc/helpers/esm/_object_spread.js'; import { _ as _$5 } from '@swc/helpers/esm/_object_spread_props.js'; import { _ as _$8 } from '@swc/helpers/esm/_object_without_properties.js'; import { _ as _$9 } from '@swc/helpers/esm/_sliced_to_array.js'; import { _ as _$7 } from '@swc/helpers/esm/_to_array.js'; import { _ as _$a } from '@swc/helpers/esm/_to_consumable_array.js'; import { createChatComponent } from 'instantsearch-ui-components'; import { SearchIndexToolType, RecommendToolType, DisplayResultsToolType, GroupedResultsToolType, MemorizeToolType, MemorySearchToolType, PonderToolType } from 'instantsearch.js/es/lib/chat/index.js'; export { DisplayResultsToolType, GroupedResultsToolType, MemorizeToolType, MemorySearchToolType, PonderToolType, RecommendToolType, SearchIndexToolType } from 'instantsearch.js/es/lib/chat/index.js'; import { getActiveContainerAnimations, holdContainerInertUntilReveal, restoreContainerInertUntilReveal, focusAfterReveal } from 'instantsearch.js/es/lib/chat/focusAfterReveal.js'; import React, { createElement, Fragment, useMemo, useState, useEffect, useRef, useCallback, useImperativeHandle } from 'react'; import { useInstantSearch, useChat } from 'react-instantsearch-core'; import { useStickToBottom } from '../lib/useStickToBottom.js'; import { createGroupedResultsTool } from './chat/tools/DisplayResultsTool.js'; import { createCarouselTool } from './chat/tools/SearchIndexTool.js'; var ChatUiComponent = createChatComponent({ createElement: createElement, Fragment: Fragment, useMemo: useMemo, useState: useState, useEffect: useEffect }); function createDefaultTools(itemComponent, getSearchPageURL) { var _obj; return _obj = {}, _$4(_obj, SearchIndexToolType, _$5(_$6({}, createCarouselTool(true, itemComponent, getSearchPageURL)), { // The agent decides per turn whether the richer Grouped Results tool // takes over the rendering of the search results. shouldRender: isDisplayResultsDisabled })), _$4(_obj, RecommendToolType, createCarouselTool(false, itemComponent, getSearchPageURL)), _$4(_obj, DisplayResultsToolType, createGroupedResultsTool(itemComponent)), _$4(_obj, GroupedResultsToolType, createGroupedResultsTool(itemComponent)), _$4(_obj, MemorizeToolType, {}), _$4(_obj, MemorySearchToolType, {}), _$4(_obj, PonderToolType, {}), _obj; } /** * Whether the search tool renders its own results, i.e. the agent did not hand * the turn to the Grouped Results tool. Set on the message by the backend. */ function isDisplayResultsDisabled(param) { var parentMessage = param.parentMessage; var _parentMessage_metadata; return ((_parentMessage_metadata = parentMessage.metadata) === null || _parentMessage_metadata === void 0 ? void 0 : _parentMessage_metadata.displayResultsEnabled) !== true; } function mergeToolOptions(defaultTools, userTools) { if (!userTools) { return defaultTools; } var tools = _$6({}, defaultTools, userTools); Object.keys(userTools).forEach(function(toolName) { var userTool = userTools[toolName]; var defaultTool = defaultTools[toolName]; var defaultStreamInput = defaultTool === null || defaultTool === void 0 ? void 0 : defaultTool.streamInput; if (userTool.layoutComponent !== undefined && userTool.streamInput === undefined && defaultStreamInput !== undefined) { tools[toolName] = _$5(_$6({}, tools[toolName]), { streamInput: defaultStreamInput }); } // Overriding a tool's rendering shouldn't opt it out of the conditions // under which the default renders at all. if (userTool.shouldRender === undefined && (defaultTool === null || defaultTool === void 0 ? void 0 : defaultTool.shouldRender)) { tools[toolName] = _$5(_$6({}, tools[toolName]), { shouldRender: defaultTool.shouldRender }); } }); return tools; } var AnimationSnapshot = /*#__PURE__*/ function(_React_Component) { _(AnimationSnapshot, _React_Component); function AnimationSnapshot() { _$1(this, AnimationSnapshot); return _$2(this, AnimationSnapshot, arguments); } _$3(AnimationSnapshot, [ { key: "getSnapshotBeforeUpdate", value: function getSnapshotBeforeUpdate() { return getActiveContainerAnimations(this.props.promptRef.current); } }, { key: "componentDidUpdate", value: function componentDidUpdate(_previousProps, _previousState, animationsBeforeReveal) { this.props.animationsBeforeReveal.current = animationsBeforeReveal; if (!_previousProps.open && this.props.open) { holdContainerInertUntilReveal(this.props.promptRef.current); } else if (this.props.open) { restoreContainerInertUntilReveal(this.props.promptRef.current); } } }, { key: "render", value: function render() { return null; } } ]); return AnimationSnapshot; }(React.Component); function ChatInner(_0, _1) { var _ref = [ _0, _1 ], _ref1 = _$7(_ref), _ref2 = _ref1[0], _rest = _ref1.slice(1), userTools = _ref2.tools, headerProps = _ref2.headerProps, messagesProps = _ref2.messagesProps, promptProps = _ref2.promptProps, itemComponent = _ref2.itemComponent, layoutComponent = _ref2.layoutComponent, headerComponent = _ref2.headerComponent, headerTitleIconComponent = _ref2.headerTitleIconComponent, headerCloseIconComponent = _ref2.headerCloseIconComponent, headerMinimizeIconComponent = _ref2.headerMinimizeIconComponent, headerMaximizeIconComponent = _ref2.headerMaximizeIconComponent, loaderComponent = _ref2.loaderComponent, loaderPosition = _ref2.loaderPosition, shouldShowLoader = _ref2.shouldShowLoader, loaderShowDelay = _ref2.loaderShowDelay, loaderMinDuration = _ref2.loaderMinDuration, messagesErrorComponent = _ref2.messagesErrorComponent, promptComponent = _ref2.promptComponent, promptHeaderComponent = _ref2.promptHeaderComponent, promptFooterComponent = _ref2.promptFooterComponent, assistantMessageLeadingComponent = _ref2.assistantMessageLeadingComponent, assistantMessageFooterComponent = _ref2.assistantMessageFooterComponent, userMessageLeadingComponent = _ref2.userMessageLeadingComponent, userMessageFooterComponent = _ref2.userMessageFooterComponent, emptyComponent = _ref2.emptyComponent, actionsComponent = _ref2.actionsComponent, suggestionsComponent = _ref2.suggestionsComponent, classNames = _ref2.classNames, _ref_translations = _ref2.translations, translations = _ref_translations === void 0 ? {} : _ref_translations, title = _ref2.title, getSearchPageURL = _ref2.getSearchPageURL, _ref_disableTriggerValidation = _ref2.disableTriggerValidation, disableTriggerValidation = _ref_disableTriggerValidation === void 0 ? false : _ref_disableTriggerValidation, showReasoning = _ref2.showReasoning, reasoningComponent = _ref2.reasoningComponent, props = _$8(_ref2, [ "tools", "headerProps", "messagesProps", "promptProps", "itemComponent", "layoutComponent", "headerComponent", "headerTitleIconComponent", "headerCloseIconComponent", "headerMinimizeIconComponent", "headerMaximizeIconComponent", "loaderComponent", "loaderPosition", "shouldShowLoader", "loaderShowDelay", "loaderMinDuration", "messagesErrorComponent", "promptComponent", "promptHeaderComponent", "promptFooterComponent", "assistantMessageLeadingComponent", "assistantMessageFooterComponent", "userMessageLeadingComponent", "userMessageFooterComponent", "emptyComponent", "actionsComponent", "suggestionsComponent", "classNames", "translations", "title", "getSearchPageURL", "disableTriggerValidation", "showReasoning", "reasoningComponent" ]), _rest1 = _$9(_rest, 1), ref = _rest1[0]; var _ref3; var promptTranslations = translations.prompt, headerTranslations = translations.header, messageTranslations = translations.message, messagesTranslations = translations.messages; var _useInstantSearch = useInstantSearch(), indexUiState = _useInstantSearch.indexUiState, setIndexUiState = _useInstantSearch.setIndexUiState; var _useState = _$9(useState(false), 2), maximized = _useState[0], setMaximized = _useState[1]; var promptRef = useRef(null); var focusRequestId = useRef(0); var animationsBeforeReveal = useRef([]); var _useStickToBottom = useStickToBottom({ initial: 'smooth', resize: 'smooth' }), scrollRef = _useStickToBottom.scrollRef, contentRef = _useStickToBottom.contentRef, scrollToBottom = _useStickToBottom.scrollToBottom, isAtBottom = _useStickToBottom.isAtBottom; var tools = useMemo(function() { var defaults = createDefaultTools(itemComponent, getSearchPageURL); return mergeToolOptions(defaults, userTools); }, [ getSearchPageURL, itemComponent, userTools ]); // Inline layouts are always visible, so they don't require a `<ChatTrigger />` // (or AI mode) to be present. We detect this via a `$$inlineLayout` marker // set on the layout component, which is consistent across flavors. var isInlineLayoutComponent = typeof layoutComponent === 'function' && layoutComponent.$$inlineLayout === true; var effectiveDisableTriggerValidation = disableTriggerValidation || isInlineLayoutComponent; var chatState = useChat(_$5(_$6({}, props), { tools: tools, disableTriggerValidation: effectiveDisableTriggerValidation })); var messages = chatState.messages, sendMessage = chatState.sendMessage, status = chatState.status, regenerate = chatState.regenerate, stop = chatState.stop, error = chatState.error, input = chatState.input, setInput = chatState.setInput, open = chatState.open, setOpen = chatState.setOpen, clearMessages = chatState.clearMessages, toolsFromConnector = chatState.tools, suggestions = chatState.suggestions, onFeedback = chatState.sendChatMessageFeedback, feedbackState = chatState.feedbackState, consumeInputFocus = chatState['~consumeInputFocus'], isOpenStatePersistenceEnabled = chatState['~isOpenStatePersistenceEnabled']; var sendMessageAndScrollToBottom = useCallback(function() { for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ args[_key] = arguments[_key]; } scrollToBottom(); return sendMessage.apply(void 0, _$a(args)); }, [ scrollToBottom, sendMessage ]); useImperativeHandle(ref, function() { return { setOpen: setOpen, sendMessage: function sendMessage(params) { return sendMessageAndScrollToBottom(params); }, setInput: setInput }; }); useEffect(function() { if (!open) { focusRequestId.current++; return; } if (consumeInputFocus === null || consumeInputFocus === void 0 ? void 0 : consumeInputFocus()) { var currentFocusRequestId = ++focusRequestId.current; var previousAnimations = animationsBeforeReveal.current; holdContainerInertUntilReveal(promptRef.current); window.requestAnimationFrame(function() { var prompt = promptRef.current; focusAfterReveal(prompt, previousAnimations, function() { return focusRequestId.current === currentFocusRequestId && promptRef.current === prompt; }, function() { return focusRequestId.current === currentFocusRequestId; }); }); } }); // Keep the conversation pinned to the bottom while streaming. The stick-to- // bottom ResizeObserver only reacts to content *height* changes, but tool // results such as a horizontally-growing carousel stream in without changing // height — so we also re-pin on every message/status update. Passing // `preserveScrollPosition` reuses the existing "only if already at the // bottom" gate, so this never fights a user who has scrolled up to read. useEffect(function() { if (status === 'streaming' || status === 'submitted') { scrollToBottom({ preserveScrollPosition: true }); } }, [ messages, status, scrollToBottom ]); var _ref4 = messagesProps !== null && messagesProps !== void 0 ? messagesProps : {}, callerAssistantMessageProps = _ref4.assistantMessageProps, callerUserMessageProps = _ref4.userMessageProps, restMessagesProps = _$8(_ref4, [ "assistantMessageProps", "userMessageProps" ]); var chat = /*#__PURE__*/ React.createElement(ChatUiComponent, { title: title, open: open, maximized: maximized, sendMessage: sendMessageAndScrollToBottom, regenerate: regenerate, stop: stop, error: error, layoutComponent: layoutComponent, headerComponent: headerComponent, promptComponent: promptComponent, suggestionsComponent: suggestionsComponent, headerProps: _$6({ onClose: function onClose() { return setOpen(false); }, maximized: maximized, onToggleMaximize: function onToggleMaximize() { return setMaximized(!maximized); }, onClear: clearMessages, canClear: Boolean(messages === null || messages === void 0 ? void 0 : messages.length), titleIconComponent: headerTitleIconComponent, closeIconComponent: headerCloseIconComponent, minimizeIconComponent: headerMinimizeIconComponent, maximizeIconComponent: headerMaximizeIconComponent, translations: headerTranslations }, headerProps), messagesProps: _$5(_$6({ status: status, onReload: function onReload(messageId) { return regenerate({ messageId: messageId }); }, onNewConversation: clearMessages, onClose: function onClose() { return setOpen(false); }, sendMessage: sendMessageAndScrollToBottom, setInput: setInput, onFeedback: onFeedback, feedbackState: feedbackState, messages: messages, tools: toolsFromConnector, indexUiState: indexUiState, setIndexUiState: setIndexUiState, isScrollAtBottom: isAtBottom, scrollRef: scrollRef, contentRef: contentRef, onScrollToBottom: scrollToBottom, loaderComponent: loaderComponent, loaderPosition: loaderPosition, shouldShowLoader: shouldShowLoader, loaderShowDelay: loaderShowDelay, loaderMinDuration: loaderMinDuration, errorComponent: messagesErrorComponent, emptyComponent: emptyComponent, actionsComponent: actionsComponent, translations: messagesTranslations, messageTranslations: messageTranslations }, restMessagesProps), { assistantMessageProps: _$6({ leadingComponent: assistantMessageLeadingComponent, footerComponent: assistantMessageFooterComponent, showReasoning: showReasoning, reasoningComponent: reasoningComponent }, callerAssistantMessageProps), userMessageProps: _$6({ leadingComponent: userMessageLeadingComponent, footerComponent: userMessageFooterComponent }, callerUserMessageProps), error: error }), promptProps: _$5(_$6({ promptRef: promptRef, status: status, value: input, translations: promptTranslations, onInput: function onInput(event) { setInput(event.currentTarget.value); }, onSubmit: function onSubmit() { sendMessageAndScrollToBottom({ text: input }); setInput(''); }, onStop: function onStop() { stop(); }, headerComponent: promptHeaderComponent, footerComponent: promptFooterComponent }, promptProps), { autoFocus: (_ref3 = promptProps === null || promptProps === void 0 ? void 0 : promptProps.autoFocus) !== null && _ref3 !== void 0 ? _ref3 : !isOpenStatePersistenceEnabled || isInlineLayoutComponent }), suggestionsProps: { suggestions: suggestions, onSuggestionClick: function onSuggestionClick(suggestion) { sendMessageAndScrollToBottom({ text: suggestion }); } }, classNames: classNames }); return /*#__PURE__*/ React.createElement(React.Fragment, null, /*#__PURE__*/ React.createElement(AnimationSnapshot, { promptRef: promptRef, animationsBeforeReveal: animationsBeforeReveal, open: open }), chat); } var Chat = /*#__PURE__*/ React.forwardRef(ChatInner); export { Chat, createDefaultTools };