UNPKG

@finos/legend-application

Version:
296 lines 29.1 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; /** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { clsx, TimesIcon, SearchIcon, MapMarkerIcon, CloseIcon, ContextMenu, MenuContent, MenuContentItem, MarkdownTextViewer, ChevronDownIcon, ChevronRightIcon, BlankPanelContent, BeardIcon, SunglassesIcon, WizardHatIcon, FaceLaughWinkIcon, ThinVerticalDragHandleIcon, PanelLoadingIndicator, BasePopover, FaceSadTearIcon, CogIcon, Draggable, BaseRadioGroup, QuestionCircleIcon, EmptyWindowRestoreIcon, WindowMaximizeIcon, MinusCircleIcon, } from '@finos/legend-art'; import { ADVANCED_FUZZY_SEARCH_MODE, ContentType, debounce, downloadFileUsingDataURI, formatDate, isString, uuid, } from '@finos/legend-shared'; import { observer } from 'mobx-react-lite'; import { useEffect, useMemo, useRef, useState } from 'react'; import { DEFAULT_DATE_TIME_FORMAT, DEFAULT_TAB_SIZE, } from '../stores/ApplicationConfig.js'; import { VIRTUAL_ASSISTANT_TAB, } from '../stores/AssistantService.js'; import { useApplicationStore } from './ApplicationStoreProvider.js'; import { LegendApplicationTelemetryHelper } from '../__lib__/LegendApplicationTelemetry.js'; import { LEGEND_APPLICATION_DOCUMENTATION_KEY } from '../__lib__/LegendApplicationDocumentation.js'; const WIZARD_GREETING = `Bonjour, It's Pierre!`; const VirtualAssistantDocumentationEntryViewer = observer((props) => { const { entry } = props; const applicationStore = useApplicationStore(); const toggleExpand = () => { if (!entry.isOpen) { LegendApplicationTelemetryHelper.logEvent_VirtualAssistantDocumentationEntryAccessed(applicationStore.telemetryService, { key: entry.documentationKey, }); } entry.setIsOpen(!entry.isOpen); }; const onDocumentationLinkClick = () => { LegendApplicationTelemetryHelper.logEvent_VirtualAssistantDocumentationEntryAccessed(applicationStore.telemetryService, { key: entry.documentationKey, }); }; const copyDocumentationKey = applicationStore.guardUnhandledError(() => applicationStore.clipboardService.copyTextToClipboard(entry.documentationKey)); return (_jsx(ContextMenu, { className: "virtual-assistant__doc-entry", menuProps: { elevation: 7, classes: { root: 'virtual-assistant__context-menu', }, }, content: _jsx(MenuContent, { children: _jsx(MenuContentItem, { onClick: copyDocumentationKey, children: "Copy Documentation Key" }) }), children: _jsxs("div", { className: "virtual-assistant__doc-entry", children: [_jsxs("div", { className: "virtual-assistant__doc-entry__header", children: [_jsx("button", { className: clsx('virtual-assistant__doc-entry__expand-icon', { 'virtual-assistant__doc-entry__expand-icon--disabled': !entry.content, }), disabled: !entry.content, tabIndex: -1, onClick: toggleExpand, children: entry.isOpen ? _jsx(ChevronDownIcon, {}) : _jsx(ChevronRightIcon, {}) }), entry.url ? (_jsx("a", { className: "virtual-assistant__doc-entry__title virtual-assistant__doc-entry__title--link", rel: "noopener noreferrer", target: "_blank", href: entry.url, onClick: onDocumentationLinkClick, title: "Click to see external documentation", children: entry.title })) : (_jsx("div", { className: "virtual-assistant__doc-entry__title", onClick: toggleExpand, children: entry.title }))] }), entry.isOpen && entry.content && (_jsx("div", { className: "virtual-assistant__doc-entry__content", children: isString(entry.content) ? (_jsx("div", { className: "virtual-assistant__doc-entry__content__text", children: entry.content })) : (_jsx(MarkdownTextViewer, { className: "virtual-assistant__doc-entry__content__markdown-text", value: entry.content })) }))] }) })); }); const VirtualAssistantContextualSupportPanel = observer(() => { const applicationStore = useApplicationStore(); const assistantService = applicationStore.assistantService; const contextualEntry = assistantService.currentContextualDocumentationEntry; const copyContextIDToClipboard = applicationStore.guardUnhandledError(() => applicationStore.clipboardService.copyTextToClipboard(contextualEntry?.context ?? '')); const copyCurrentContextIDToClipboard = applicationStore.guardUnhandledError(() => applicationStore.clipboardService.copyTextToClipboard(applicationStore.navigationContextService.currentContext?.key ?? '')); const copyContextStackToClipboard = applicationStore.guardUnhandledError(() => applicationStore.clipboardService.copyTextToClipboard(applicationStore.navigationContextService.contextStack .map((context) => context.key) .join(' > '))); return (_jsxs(ContextMenu, { className: "virtual-assistant__contextual-support", disabled: !contextualEntry, menuProps: { elevation: 7, classes: { root: 'virtual-assistant__context-menu', }, }, content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { onClick: copyContextIDToClipboard, children: "Copy Context ID" }), _jsx(MenuContentItem, { onClick: copyCurrentContextIDToClipboard, children: "Copy Current Context ID" }), _jsx(MenuContentItem, { onClick: copyContextStackToClipboard, children: "Copy Context Stack" })] }), children: [contextualEntry && (_jsxs("div", { className: "virtual-assistant__contextual-support__content", children: [contextualEntry.title && (_jsx("div", { className: "virtual-assistant__contextual-support__title", children: contextualEntry.title })), contextualEntry.content && (_jsx(_Fragment, { children: isString(contextualEntry.content) ? (_jsx("div", { className: "virtual-assistant__contextual-support__text", children: contextualEntry.content })) : (_jsx(MarkdownTextViewer, { className: "virtual-assistant__contextual-support__markdown-text", value: contextualEntry.content })) })), contextualEntry.related.length && (_jsxs("div", { className: "virtual-assistant__contextual-support__relevant-entries", children: [_jsxs("div", { className: "virtual-assistant__contextual-support__relevant-entries__title", children: ["Related entries (", contextualEntry.related.length, ")"] }), contextualEntry.related.map((entry) => (_jsx(VirtualAssistantDocumentationEntryViewer, { entry: entry }, entry.uuid)))] }))] })), !contextualEntry && (_jsx(BlankPanelContent, { children: _jsxs("div", { className: "virtual-assistant__panel__placeholder", children: [_jsx(FaceLaughWinkIcon, { className: "virtual-assistant__panel__placeholder__icon" }), _jsx("div", { className: "virtual-assistant__panel__placeholder__message", children: "No contextual documentation found!" }), _jsx("div", { className: "virtual-assistant__panel__placeholder__instruction", children: "Keep using the app. When contextual help is available, we will let you know!" })] }) }))] })); }); const VirtualAssistantSearchPanel = observer(() => { const applicationStore = useApplicationStore(); const searchInputRef = useRef(null); const assistantService = applicationStore.assistantService; // search text const searchText = assistantService.searchText; const debouncedSearch = useMemo(() => debounce(() => assistantService.search(), 100), [assistantService]); const onSearchTextChange = (event) => { assistantService.setSearchText(event.target.value); debouncedSearch(); }; const clearSearchText = () => { assistantService.resetSearch(); assistantService.currentDocumentationEntry = undefined; searchInputRef.current?.focus(); }; const toggleSearchConfigMenu = () => assistantService.setShowSearchConfigurationMenu(!assistantService.showSearchConfigurationMenu); const downloadDocRegistry = () => { downloadFileUsingDataURI(`documentation-registry_${formatDate(new Date(Date.now()), DEFAULT_DATE_TIME_FORMAT)}.json`, JSON.stringify(applicationStore.documentationService.publishDocRegistry(), undefined, DEFAULT_TAB_SIZE), ContentType.APPLICATION_JSON); }; const downloadContextualDocIndex = () => { downloadFileUsingDataURI(`documentation-registry_${formatDate(new Date(Date.now()), DEFAULT_DATE_TIME_FORMAT)}.json`, JSON.stringify(applicationStore.documentationService.publishContextualDocIndex(), undefined, DEFAULT_TAB_SIZE), ContentType.APPLICATION_JSON); }; useEffect(() => { searchInputRef.current?.focus(); }, []); return (_jsxs("div", { className: "virtual-assistant__search", children: [_jsxs("div", { className: "virtual-assistant__search__header", children: [_jsx("input", { ref: searchInputRef, /** * NOTE: In the scenario where another modal is opened at the same time the assistant panel * is open, the focus will be stolen by the newly opened modal. In that case, we need * to take back the focus. The trick here is to remount the whole panel (modal/popover) * by refreshing the `key` prop of the panel. This will cause `mui` to recompute the * focus-trap and allow the input field to be selectable again. Basically, we are stealing * back the focus for the assistant. * * However, the caveat is that this will cause the component states, such as scroll positions, * to be reset as such, we need to do this really sparingly. In fact, the only * scenario where we need to do this is when a new modal is opened when the assistant panel * is already opened. Basically, Other scenarios, such as when the assistant is opened after the modal * is opened seem to pose no issue. */ onClick: () => { if ( // only when there are dialogs being opened // NOTE: this seems rather hacky, but querying by role is the least // vendor-dependent approach we can think of at the moment document.querySelectorAll('[role="dialog"]').length && // only when the focus is not already with the input field // this means the focus is being stolen from the assistant because // the newly opened modal is opened more recently than the assistant // // once the focus has been gained by the assistant // we will not need to do this anymore searchInputRef.current !== document.activeElement) { assistantService.refreshPanelRendering(); } }, className: clsx('virtual-assistant__search__input input--dark', { 'virtual-assistant__search__input--searching': searchText, }), spellCheck: false, onChange: onSearchTextChange, value: searchText, placeholder: "Ask me a question" }), searchText && (_jsx("div", { className: "virtual-assistant__search__input__search__count", children: assistantService.searchResults.length + (assistantService.isOverSearchLimit ? '+' : '') })), _jsx("button", { className: clsx('virtual-assistant__search__input__config__trigger', { 'virtual-assistant__search__input__config__trigger--toggled': assistantService.showSearchConfigurationMenu, 'virtual-assistant__search__input__config__trigger--active': assistantService.searchConfigurationState.isAdvancedSearchActive, }), tabIndex: -1, onClick: toggleSearchConfigMenu, title: `${assistantService.searchConfigurationState.isAdvancedSearchActive ? 'Advanced search is currently active\n' : ''}Click to toggle search config menu`, children: _jsx(CogIcon, {}) }), !searchText ? (_jsx("div", { className: "virtual-assistant__search__input__search__icon", children: _jsx(SearchIcon, {}) })) : (_jsx("button", { className: "virtual-assistant__search__input__clear-btn", tabIndex: -1, onClick: clearSearchText, title: "Clear", children: _jsx(TimesIcon, {}) }))] }), _jsxs("div", { className: "virtual-assistant__search__content", children: [_jsx(PanelLoadingIndicator, { isLoading: assistantService.searchState.isInProgress }), _jsx("div", { className: clsx('virtual-assistant__search__input__config__panel', { 'virtual-assistant__search__input__config__panel--toggled': assistantService.showSearchConfigurationMenu, }), children: _jsxs("div", { className: "virtual-assistant__search__input__advanced-config__panel", children: [_jsxs("div", { className: "virtual-assistant__search__input__advanced-config__panel__header__label", children: ["search config", applicationStore.documentationService.hasDocEntry(LEGEND_APPLICATION_DOCUMENTATION_KEY.QUESTION_HOW_TO_USE_ADVANCED_SEARCH_SYNTAX) && (_jsx("div", { onClick: () => assistantService.openDocumentationEntryLink(LEGEND_APPLICATION_DOCUMENTATION_KEY.QUESTION_HOW_TO_USE_ADVANCED_SEARCH_SYNTAX), title: "Click to see documentation", className: "virtual-assistant__search__input__advanced-config__panel__header__label__hint", children: _jsx(QuestionCircleIcon, {}) }))] }), _jsx("div", { children: _jsx(BaseRadioGroup, { value: assistantService.searchConfigurationState.currentMode, onChange: (event) => { const searchMode = event.target .value; assistantService.searchConfigurationState.setCurrentMode(searchMode); }, row: false, options: [ ADVANCED_FUZZY_SEARCH_MODE.STANDARD, ADVANCED_FUZZY_SEARCH_MODE.INCLUDE, ADVANCED_FUZZY_SEARCH_MODE.EXACT, ADVANCED_FUZZY_SEARCH_MODE.INVERSE, ], size: 1 }) })] }) }), assistantService.currentDocumentationEntry && (_jsx("div", { className: "virtual-assistant__search__results", children: _jsx(VirtualAssistantDocumentationEntryViewer, { entry: assistantService.currentDocumentationEntry }, assistantService.currentDocumentationEntry.uuid) })), !assistantService.currentDocumentationEntry && (_jsxs(_Fragment, { children: [Boolean(assistantService.searchResults.length) && (_jsx("div", { className: "virtual-assistant__search__results", children: assistantService.searchResults.map((result) => (_jsx(VirtualAssistantDocumentationEntryViewer, { entry: result }, result.uuid))) })), searchText && !assistantService.searchResults.length && (_jsx(BlankPanelContent, { children: _jsxs("div", { className: "virtual-assistant__panel__placeholder", children: [_jsx(FaceSadTearIcon, { className: "virtual-assistant__panel__placeholder__icon" }), _jsx("div", { className: "virtual-assistant__panel__placeholder__message", children: "No result..." })] }) })), !searchText && !assistantService.searchResults.length && (_jsx(ContextMenu, { className: "virtual-assistant__character__container", menuProps: { elevation: 7, classes: { root: 'virtual-assistant__context-menu', }, }, content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { onClick: downloadDocRegistry, children: "Download documentation registry" }), _jsx(MenuContentItem, { onClick: downloadContextualDocIndex, children: "Download contextual documentation mapping" })] }), children: _jsxs("div", { className: "virtual-assistant__character", children: [_jsxs("div", { className: "virtual-assistant__character__figure", children: [_jsx(WizardHatIcon, { className: "virtual-assistant__character__hat" }), _jsx(SunglassesIcon, { className: "virtual-assistant__character__glasses" }), _jsx(BeardIcon, { className: "virtual-assistant__character__beard" })] }), _jsx("div", { className: "virtual-assistant__character__greeting", children: WIZARD_GREETING }), _jsx("div", { className: "virtual-assistant__character__question", children: "How can I help today?" })] }) }))] }))] })] })); }); const VirtualAssistantPanel = observer((props) => { const { triggerElement } = props; const applicationStore = useApplicationStore(); const assistantService = applicationStore.assistantService; const currentContextualDocumentationEntry = assistantService.currentContextualDocumentationEntry; const selectedTab = assistantService.selectedTab; const extraViewConfigurations = applicationStore.pluginManager .getApplicationPlugins() .flatMap((plugin) => plugin.getExtraVirtualAssistantViewConfigurations?.() ?? []); const currentExtensionView = extraViewConfigurations.find((config) => config.key === selectedTab); const toggleMaximize = () => assistantService.setIsPanelMaximized(!assistantService.isPanelMaximized); const selectSearch = () => assistantService.setSelectedTab(VIRTUAL_ASSISTANT_TAB.SEARCH); const selectContextualDoc = () => assistantService.setSelectedTab(VIRTUAL_ASSISTANT_TAB.CONTEXTUAL_SUPPORT); const closeAssistantPanel = () => assistantService.setIsOpen(false); return ( /** * The most appropriate component to use is `Popper` * as it does not block click-away * See https://mui.com/material-ui/react-popper/ * * However, the caveat is that in the implementation of mui Popper * focus trap is not supported. As such, we could end up in a situation * where the assistant input fields will not be focusable * when another modal is being opened, as that newly opened modal will * **steal** the focus * * See https://github.com/finos/legend-studio/issues/1255 * See https://mui.com/material-ui/react-modal/#focus-trap * See https://github.com/mui/material-ui/issues/17497 */ _jsx(BasePopover, { open: assistantService.isOpen, className: "virtual-assistant__panel__container", anchorEl: triggerElement, // we need to get rid of the backdrop and the click-away trap // to make this popover behave like a popper // NOTE: we will cancel the effect of click-away trap using CSS hideBackdrop: true, PaperProps: { classes: { root: 'virtual-assistant__panel__container__root' }, }, // allow other modals to take the focus from the virtual assistant disableEnforceFocus: true, // NOTE: make sure the assistant is always fully displayed (not cropped) anchorOrigin: { vertical: 'bottom', horizontal: 'left', }, transformOrigin: { vertical: 'bottom', horizontal: 'right', }, children: _jsxs("div", { className: clsx('virtual-assistant__panel', { 'virtual-assistant__panel--maximized': assistantService.isPanelMaximized, }), // NOTE: here we block `tabbing` (to move focus). This is to counter the effect of // `disableEnforceFocus={true}` set in the assistant panel popover // this is the poor-man focus trap for the assistant to ensure // that we don't leak focus tab down to other parts of the app // // Especially, due to the hack we do to compete for focus when another modal // is opened, we need to do this to avoid leaking of focus to components // beneath the modal via assistant // // setting `tabIndex={0}` is a hack to make this DOM node focusable // and hence, we could trap the focus here using `onKeyDown` // See https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex tabIndex: 0, onKeyDown: (event) => { if (event.key === 'Tab') { event.preventDefault(); event.stopPropagation(); } }, children: [_jsxs("div", { className: "virtual-assistant__panel__header", children: [_jsxs("div", { className: "virtual-assistant__panel__header__tabs", children: [_jsx("div", { className: clsx('virtual-assistant__panel__header__tab', { 'virtual-assistant__panel__header__tab--active': selectedTab === VIRTUAL_ASSISTANT_TAB.SEARCH, }), onClick: selectSearch, title: "Search", children: _jsx("div", { className: "virtual-assistant__panel__header__tab__content", children: _jsx(SearchIcon, {}) }) }), _jsx("div", { className: clsx('virtual-assistant__panel__header__tab', { 'virtual-assistant__panel__header__tab--active': selectedTab === VIRTUAL_ASSISTANT_TAB.CONTEXTUAL_SUPPORT, }), onClick: selectContextualDoc, title: "Contextual Support", children: _jsxs("div", { className: "virtual-assistant__panel__header__tab__content", children: [_jsx(MapMarkerIcon, {}), currentContextualDocumentationEntry && (_jsx("div", { className: "virtual-assistant__panel__header__tab__indicator", title: "Contextual support available" }))] }) }), extraViewConfigurations.map((config) => (_jsx("div", { className: clsx('virtual-assistant__panel__header__tab', { 'virtual-assistant__panel__header__tab--active': selectedTab === config.key, }), onClick: () => { assistantService.setSelectedTab(config.key); if (config.autoExpandOnOpen) { assistantService.setIsPanelMaximized(true); } }, title: config.title, children: _jsx("div", { className: "virtual-assistant__panel__header__tab__content", children: config.icon ?? _jsx(QuestionCircleIcon, {}) }) }, config.key)))] }), _jsxs("div", { className: "virtual-assistant__panel__header__actions", children: [_jsx("button", { className: "virtual-assistant__panel__header__action", tabIndex: -1, onClick: toggleMaximize, title: assistantService.isPanelMaximized ? 'Minimize' : 'Maximize', children: assistantService.isPanelMaximized ? (_jsx(EmptyWindowRestoreIcon, {})) : (_jsx(WindowMaximizeIcon, {})) }), _jsx("button", { className: "virtual-assistant__panel__header__action", tabIndex: -1, onClick: closeAssistantPanel, title: "Close", children: _jsx(CloseIcon, { className: "virtual-assistant__panel__icon__close" }) })] })] }), _jsxs("div", { className: "virtual-assistant__panel__content", children: [selectedTab === VIRTUAL_ASSISTANT_TAB.SEARCH && (_jsx(VirtualAssistantSearchPanel, {})), selectedTab === VIRTUAL_ASSISTANT_TAB.CONTEXTUAL_SUPPORT && (_jsx(VirtualAssistantContextualSupportPanel, {})), currentExtensionView?.renderer()] })] }) }, assistantService.panelRenderingKey)); }); export const VirtualAssistant = observer(() => { const [isDragging, setIsDragging] = useState(false); const [_key, _setKey] = useState(uuid()); const applicationStore = useApplicationStore(); const assistantRef = useRef(null); const assistantService = applicationStore.assistantService; const currentContextualDocumentationEntry = assistantService.currentContextualDocumentationEntry; const toggleAssistantPanel = () => { const newVal = !assistantService.isOpen; // open the contextual help tab when contextual help is available if (newVal && currentContextualDocumentationEntry) { assistantService.setSelectedTab(VIRTUAL_ASSISTANT_TAB.CONTEXTUAL_SUPPORT); } assistantService.setIsOpen(!assistantService.isOpen); }; const hideAssistant = () => applicationStore.assistantService.hideAssistant(); const resetPosition = () => { // close the panel since assistantService.setIsOpen(false); _setKey(uuid()); }; // drag and drop const onDragEnd = () => setIsDragging(false); const onDragStart = () => setIsDragging(true); useEffect(() => { if (assistantService.isHidden) { // reset to default position when we hide the assistant // so that when we open it the position is reset _setKey(uuid()); } }, [assistantService.isHidden]); return (_jsx(Draggable // this is a trick so we could reset the default position of the assistant // See https://github.com/react-grid-layout/react-draggable/issues/214#issuecomment-270021423 , { // make sure we cannot drag and drop outside of the screen bounds: "parent", onStart: onDragStart, onStop: onDragEnd, // Avoid using deprecated findDOMNode method to rid of React warning // See https://github.com/react-grid-layout/react-draggable/issues/749 nodeRef: assistantRef, // limit the dnd trigger to only the drag handle handle: ".virtual-assistant__station__drag-handle", children: _jsxs("div", { className: "virtual-assistant", // NOTE: we have to set the `ref` at this level so even when the assistant is hidden // the element is still in the DOM so when we programmatically show the assistant panel // the anchor is available in time ref: assistantRef, children: [_jsxs("div", { className: clsx('virtual-assistant__station', { 'virtual-assistant__station--hidden': assistantService.isHidden, 'virtual-assistant__station--active': Boolean(currentContextualDocumentationEntry), }), children: [_jsx("button", { className: "virtual-assistant__station__hide-button", title: "Hide assistant", onClick: () => { applicationStore.assistantService.toggleAssistant(); }, children: _jsx(MinusCircleIcon, {}) }), _jsx("button", { className: "virtual-assistant__station__trigger", tabIndex: -1, onClick: toggleAssistantPanel, title: assistantService.isOpen ? `Click to close assistant panel` : `${currentContextualDocumentationEntry ? 'Contextual support available.\n' : ''}Click to open Assistant...`, children: _jsxs("div", { className: "virtual-assistant__station__character", children: [_jsx(WizardHatIcon, { className: "virtual-assistant__station__character__hat" }), _jsx(SunglassesIcon, { className: "virtual-assistant__station__character__glasses" }), _jsx(BeardIcon, { className: "virtual-assistant__station__character__beard" })] }) }), _jsx("div", { className: "virtual-assistant__station__label", onClick: toggleAssistantPanel, children: "Assistant" }), _jsx(ContextMenu, { className: clsx('virtual-assistant__station__drag-handle', { 'virtual-assistant__station__drag-handle--dragging': isDragging, }), menuProps: { elevation: 7, classes: { root: 'virtual-assistant__context-menu', }, }, content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { onClick: resetPosition, children: "Reset Position" }), _jsx(MenuContentItem, { onClick: hideAssistant, children: "Hide Assistant" })] }), children: _jsx("div", { className: "virtual-assistant__station__drag-handle__content", title: isDragging ? undefined : 'Grab to drag assistant', children: _jsx(ThinVerticalDragHandleIcon, {}) }) })] }, currentContextualDocumentationEntry?.uuid ?? ''), !isDragging && assistantService.isOpen && !assistantService.isHidden && assistantRef.current && (_jsx(VirtualAssistantPanel, { triggerElement: assistantRef.current }))] }) }, _key)); }); //# sourceMappingURL=VirtualAssistant.js.map