react-instantsearch
Version:
⚡ Lightning-fast search for React, by Algolia
345 lines (342 loc) • 18 kB
JavaScript
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 };