UNPKG

@finos/legend-application

Version:
255 lines 9.62 kB
/** * 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 { action, makeObservable, observable, computed } from 'mobx'; import { guaranteeNonEmptyString, uuid, isNonNullable, ActionState, FuzzySearchEngine, FuzzySearchAdvancedConfigState, } from '@finos/legend-shared'; export var VIRTUAL_ASSISTANT_TAB; (function (VIRTUAL_ASSISTANT_TAB) { VIRTUAL_ASSISTANT_TAB["SEARCH"] = "SEARCH"; VIRTUAL_ASSISTANT_TAB["CONTEXTUAL_SUPPORT"] = "CONTEXTUAL_SUPPORT"; })(VIRTUAL_ASSISTANT_TAB || (VIRTUAL_ASSISTANT_TAB = {})); export const DOCUMENTATION_SEARCH_RESULTS_LIMIT = 100; export class VirtualAssistantDocumentationEntry { uuid = uuid(); documentationKey; title; content; url; isOpen = false; constructor(docEntry) { makeObservable(this, { isOpen: observable, setIsOpen: action, }); this.documentationKey = docEntry.key; this.title = guaranteeNonEmptyString(docEntry.title); this.content = docEntry.markdownText ?? docEntry.text; this.url = docEntry.url; } setIsOpen(val) { this.isOpen = val; } } export class VirtualAssistantContextualDocumentationEntry { uuid = uuid(); context; title; content; url; related; constructor(context, docEntry, related) { this.context = context; this.title = docEntry.title; this.content = docEntry.markdownText ?? docEntry.text; this.url = docEntry.url; this.related = related; } } /** * NOTE: since we're displaying the documentation entry in virtual assistant * we want only user-friendly docs, we will discard anything that doesn't * come with a title, or does not have any content/url */ export const isValidVirtualAssistantDocumentationEntry = (entry) => Boolean(entry.title && (entry.url ?? entry.text ?? entry.markdownText)); /** * Check if the documentation entry should be displayed in virtual assistant, * i.e. it has some text content, rather just a link */ export const shouldDisplayVirtualAssistantDocumentationEntry = (entry) => isValidVirtualAssistantDocumentationEntry(entry) && Boolean(entry.text ?? entry.markdownText); export class AssistantService { applicationStore; /** * This key is used to allow programmatic re-rendering of the assistant panel */ panelRenderingKey = uuid(); isDisabled = false; isHidden = true; // hide by default unless specified by the application to show isOpen = false; isPanelMaximized = false; selectedTab = VIRTUAL_ASSISTANT_TAB.SEARCH; currentDocumentationEntry; // search text searchEngine; searchConfigurationState; searchState = ActionState.create(); searchText = ''; searchResults = []; showSearchConfigurationMenu = false; isOverSearchLimit = false; constructor(applicationStore) { makeObservable(this, { isDisabled: observable, isHidden: observable, isOpen: observable, isPanelMaximized: observable, panelRenderingKey: observable, isOverSearchLimit: observable, selectedTab: observable, searchText: observable, searchResults: observable, currentDocumentationEntry: observable, showSearchConfigurationMenu: observable, currentContextualDocumentationEntry: computed, setIsDisabled: action, setIsHidden: action, setIsOpen: action, setIsPanelMaximized: action, setSelectedTab: action, setSearchText: action, resetSearch: action, search: action, openDocumentationEntry: action, refreshPanelRendering: action, setShowSearchConfigurationMenu: action, }); this.applicationStore = applicationStore; this.searchEngine = new FuzzySearchEngine(this.applicationStore.documentationService .getAllDocEntries() .filter(isValidVirtualAssistantDocumentationEntry), { includeScore: true, shouldSort: true, // Ignore location when computing the search score // See https://fusejs.io/concepts/scoring-theory.html ignoreLocation: true, // This specifies the point the search gives up // `0.0` means exact match where `1.0` would match anything // We set a relatively low threshold to filter out irrelevant results threshold: 0.2, keys: [ { // NOTE: for now, we would weight title the most name: 'title', weight: 4, }, { name: 'text', weight: 1, }, { name: 'markdownText.value', weight: 1, }, ], // extended search allows for exact word match through single quote // See https://fusejs.io/examples.html#extended-search useExtendedSearch: true, }); this.searchConfigurationState = new FuzzySearchAdvancedConfigState(() => { this.search(); }); } get currentContextualDocumentationEntry() { if (!this.applicationStore.navigationContextService.currentContext) { return undefined; } const currentContext = this.applicationStore.navigationContextService.currentContext.key; const currentContextualDocumentationEntry = this.applicationStore.documentationService.getContextualDocEntry(currentContext); return currentContextualDocumentationEntry ? new VirtualAssistantContextualDocumentationEntry(currentContext, currentContextualDocumentationEntry, (currentContextualDocumentationEntry.related ?? []) .map((entry) => this.applicationStore.documentationService.getDocEntry(entry)) .filter(isNonNullable) .filter(isValidVirtualAssistantDocumentationEntry) .map((entry) => new VirtualAssistantDocumentationEntry(entry))) : undefined; } openDocumentationEntry(key) { const entry = this.applicationStore.documentationService.getDocEntry(key); if (entry) { this.setIsOpen(true); this.setIsHidden(false); this.currentDocumentationEntry = new VirtualAssistantDocumentationEntry(entry); this.currentDocumentationEntry.setIsOpen(true); this.resetSearch(); } } openDocumentationEntryLink(key) { const entry = this.applicationStore.documentationService.getDocEntry(key); if (entry) { if (shouldDisplayVirtualAssistantDocumentationEntry(entry)) { this.openDocumentationEntry(entry.key); } else if (entry.url) { this.applicationStore.navigationService.navigator.visitAddress(entry.url); } } } setIsDisabled(val) { this.isDisabled = val; } setIsHidden(val) { this.isHidden = val; } setIsPanelMaximized(val) { this.isPanelMaximized = val; } hideAssistant() { this.setIsHidden(true); this.setIsOpen(false); } toggleAssistant() { const newVal = !this.isHidden; if (newVal) { this.hideAssistant(); } else { this.setIsHidden(false); } } setIsOpen(val) { this.isOpen = val; } setSelectedTab(val) { this.selectedTab = val; } refreshPanelRendering() { this.panelRenderingKey = uuid(); } setSearchText(val) { this.searchText = val; } resetSearch() { this.searchText = ''; this.searchResults = []; this.searchState.complete(); } search() { if (!this.searchText) { this.searchResults = []; return; } this.currentDocumentationEntry = undefined; this.searchState.inProgress(); this.searchResults = Array.from(this.searchEngine .search(this.searchConfigurationState.generateSearchText(this.searchText), { // NOTE: search for limit + 1 item so we can know if there are more search results limit: DOCUMENTATION_SEARCH_RESULTS_LIMIT + 1, }) .values()).map((result) => new VirtualAssistantDocumentationEntry(result.item)); // check if the search results exceed the limit if (this.searchResults.length > DOCUMENTATION_SEARCH_RESULTS_LIMIT) { this.isOverSearchLimit = true; this.searchResults = this.searchResults.slice(0, DOCUMENTATION_SEARCH_RESULTS_LIMIT); } else { this.isOverSearchLimit = false; } this.searchState.complete(); } setShowSearchConfigurationMenu(val) { this.showSearchConfigurationMenu = val; } } //# sourceMappingURL=AssistantService.js.map