/** * Copyright (c) 2022 * * Full application embedding * https://developers.thoughtspot.com/docs/?pageid=full-embed * @summary Full app embed * @module * @author Ayon Ghosh */ import { logger } from '../utils/logger'; import { getQueryParamString, isUndefined, setParamIfDefined } from '../utils'; import { Param, DOMSelector, HostEvent, EmbedEvent, AllEmbedViewConfig, FullHeightViewConfig, DefaultAppInitData, VisualizationOverrides, SpotterFileUploadFileTypes, } from '../types'; import { FullHeightController } from '../full-height'; import { V1Embed } from './ts-embed'; import { SpotterChatViewConfig, SpotterSidebarViewConfig, SpotterQueryMode, SpotterShareConversationConfig, StarterPromptsConfig } from './conversation'; import { buildSpotterSidebarAppInitData, buildSpotterShareConversationAppInitData, buildStarterPromptsAppInitData } from './spotter-utils'; import { SpotterVizConfig, buildSpotterVizAppInitData } from './spotter-viz-utils'; /** * Pages within the ThoughtSpot app that can be embedded. */ export enum Page { /** * Home page */ Home = 'home', /** * Search page */ Search = 'search', /** * Saved answers listing page */ Answers = 'answers', /** * Liveboards listing page */ Liveboards = 'liveboards', /** * @hidden */ Pinboards = 'pinboards', /** * Data management page */ Data = 'data', /** * SpotIQ listing page */ SpotIQ = 'insights', /** * Monitor Alerts Page */ Monitor = 'monitor', /** * Collections listing page */ Collections = 'collections', /** * Liveboard schedules listing page */ LiveboardSchedules = 'liveboard-schedules', } /** * Define the initial state of column custom group accordions * in data panel v2. */ export enum DataPanelCustomColumnGroupsAccordionState { /** * Expand all the accordion initially in data panel v2. */ EXPAND_ALL = 'EXPAND_ALL', /** * Collapse all the accordions initially in data panel v2. */ COLLAPSE_ALL = 'COLLAPSE_ALL', /** * Expand the first accordion and collapse the rest. */ EXPAND_FIRST = 'EXPAND_FIRST', } export enum HomePageSearchBarMode { OBJECT_SEARCH = 'objectSearch', AI_ANSWER = 'aiAnswer', NONE = 'none' } /** * Define the version of the primary navbar * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl */ export enum PrimaryNavbarVersion { /** * Sliding (v3) introduces a new left-side navigation hub featuring a tab switcher, * along with updates to the top navigation bar. * It serves as the foundational version of the PrimaryNavBar. */ Sliding = 'v3', } /** * Define the version of the home page * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl */ export enum HomePage { /** * Modular (v2) introduces the updated Modular Home Experience. * It serves as the foundational version of the home page. * Use {@link HomePage.ModularWithStylingChanges} (V3) or * {@link HomePage.Focused} (V4) instead. * @deprecated V1 and V2 home page experiences are deprecated. */ Modular = 'v2', /** * ModularWithStylingChanges (v3) introduces Modular Home Experience * with styling changes. */ ModularWithStylingChanges = 'v3', /** * Focused (v4) introduces the V4 homepage experience * in which Watchlist and recents and incorporated together * to form a more focused homepage. * Pre-requisite : spotter enablement * @version SDK: 1.50.0 | ThoughtSpot Cloud: 26.7.0.cl */ Focused = 'v4', } /** * Define the version of the list page * @version SDK: 1.40.0 | ThoughtSpot: 10.12.0.cl */ export enum ListPage { /** * List (v2) is the traditional List Experience. * It serves as the foundational version of the list page. */ List = 'v2', /** * ListWithUXChanges (v3) introduces the new updated list page with UX changes. */ ListWithUXChanges = 'v3', } /** * Define the discovery experience * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl */ export interface DiscoveryExperience { /** * primaryNavbarVersion determines the version of the primary navigation bar. */ primaryNavbarVersion?: PrimaryNavbarVersion; /** * homePage determines the version of the home page. */ homePage?: HomePage; /** * listPageVersion determines the version of the list page. */ listPageVersion?: ListPage; } /** * The view configuration for full app embedding. * @group Embed components */ export interface AppViewConfig extends AllEmbedViewConfig, FullHeightViewConfig { /** * If true, the top navigation bar within the ThoughtSpot app * is displayed. By default, the navigation bar is hidden. * This flag also controls the homepage left navigation bar. * * Supported embed types: `AppEmbed` * @version SDK: 1.2.0 | ThoughtSpot: 8.4.0.cl * @default true * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * showPrimaryNavbar:true, * }) * ``` */ showPrimaryNavbar?: boolean; /** * Control the visibility of the left navigation panel on the home page * in the V3 and V4 navigation and home page experience. * If `showPrimaryNavbar` is true, that is, if the Global and Homepage * navigation bars are visible, this flag will only hide the left navigation bar * on the home page. * The `showPrimaryNavbar` flag takes precedence over the `hideHomepageLeftNav`. * * **Note**: The classic (V1) and Modular (V2) home page experiences are * deprecated. This attribute applies to V3 and V4 home page experiences. * * Supported embed types: `AppEmbed` * @version SDK: 1.28.0 | ThoughtSpot: 9.12.5.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * hideHomepageLeftNav : true, * }) * ``` */ hideHomepageLeftNav?: boolean; /** * Control the visibility of the help (?) and profile * buttons on the top navigation bar. * These buttons are visible if the * navigation bar is not hidden via `showPrimaryNavbar`. * * Supported embed types: `AppEmbed` * @version SDK: 1.2.0 | ThoughtSpot: 8.4.0.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * disableProfileAndHelp: true, * }) * ``` */ disableProfileAndHelp?: boolean; /** * Whether the help menu in the top navigation bar should be served * from Pendo or ThoughtSpot's internal help items. * * Supported embed types: `AppEmbed` * @version SDK: 1.36.3 | ThoughtSpot: 10.1.0.cl * @default true * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * enablePendoHelp: false, * }); * ``` */ enablePendoHelp?: boolean; /** * Control the visibility of the hamburger icon on * the top navigation bar in the V3 navigation experience. * * Supported embed types: `AppEmbed` * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * hideHamburger : true, * }) * ``` */ hideHamburger?: boolean; /** * Control the visibility of the object search * on the top navigation bar in the V3 navigation experience. * * **Note**: The classic (V1) and V2 navigation experiences are * deprecated. This attribute applies to the V3 navigation experience. * * Supported embed types: `AppEmbed` * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl * @default true * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * hideObjectSearch: false, * }) * ``` */ hideObjectSearch?: boolean; /** * Control the visibility of the notification icon * on the top navigation bar in V3 navigation experience. * * **Note**: The classic (V1) and V2 experience modes are deprecated. * This attribute applies to the V3 experience. * * Supported embed types: `AppEmbed` * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl * @default true * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * hideNotification: false, * }) * ``` */ hideNotification?: boolean; /** * Control the visibility of the application selection menu. * In the V3 experience, it shows or hides application selection * icons on the left navigation panel. * By default, the application selection menu and icons are * shown in the UI. * * **Note**: The classic (V1) and V2 experiences are deprecated. * This attribute applies to the V3 experience. * * Supported embed types: `AppEmbed` * @version SDK: 1.28.0 | ThoughtSpot: 9.12.5.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * hideApplicationSwitcher : true, * }) * ``` */ hideApplicationSwitcher?: boolean; /** * Control the visibility of the Org switcher button on the nav-bar. * By default, the Org switcher button is shown. * * **Note**: The classic (V1) experience is deprecated. This attribute * applies to V2 (deprecated), V3 and V4 experiences. * * Supported embed types: `AppEmbed` * @version SDK: 1.28.0 | ThoughtSpot: 9.12.5.cl * @default true * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * hideOrgSwitcher : true, * }) * ``` */ hideOrgSwitcher?: boolean; /** * A URL path to the embedded application page * If both path and pageId attributes are defined, the path definition * takes precedence. This is the path post the `#/` in the URL of the standalone * ThoughtSpot app. Use this to open the embedded view to a specific path. * * For example, if you want the component to open to a specific Liveboard * you could set the path to `pinboard//tab/`. * * Supported embed types: `AppEmbed` * @version SDK: 1.1.0 | ThoughtSpot: 9.4.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * path:"pinboard/1234/tab/7464", * }) * ``` */ path?: string; /** * The application page to set as the start page * in the embedded view. * * Use this to open to a particular page in the app. To open to a specific * path within the app, use the `path` attribute which is more flexible. * * Supported embed types: `AppEmbed` * @version SDK: 1.1.0 | ThoughtSpot: 9.4.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * pageId: Page.Answers, // or Page.Data * }) * ``` */ pageId?: Page; /** * This puts a filter tag on the application. All metadata lists in the * application, such as Liveboards and answers, would be filtered by this * tag. * * Supported embed types: `AppEmbed` * @version SDK: 1.1.0 | ThoughtSpot: 9.4.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * tag:'value', * }) * ``` */ tag?: string; /** * Hide tag filter chips that appear when content is filtered by tags. * When enabled, this automatically: * - Hides tag filter indicators/chips from the UI * * This provides a clean interface without tag-related UI elements. * * Supported embed types: `AppEmbed` * @version SDK: 1.44.0 | ThoughtSpot: 10.15.0.cl * @example * ```js * // Simple usage - automatically hides all tag-related UI * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * tag: 'Some Tag', * hideTagFilterChips: true, // This is all you need! * }); * ``` */ hideTagFilterChips?: boolean; /** * The array of GUIDs to be hidden * * Supported embed types: `AppEmbed` * @version SDK: 1.11.0 | ThoughtSpot: 8.3.0.cl, 8.4.1-sw * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * hideObjects: [ * '430496d6-6903-4601-937e-2c691821af3c', * 'f547ec54-2a37-4516-a222-2b06719af726' * ] * }) * ``` */ hideObjects?: string[]; /** * Render liveboards using the new v2 rendering mode * This is a transient flag which is primarily meant for internal use * * Supported embed types: `AppEmbed` * @version SDK: 1.11.0 | ThoughtSpot: 8.3.0.cl, 8.4.1-sw * @hidden */ liveboardV2?: boolean; /** * If set to true, the Search Assist feature is enabled. * * Supported embed types: `AppEmbed` * @version SDK: 1.13.0 | ThoughtSpot: 8.5.0.cl, 8.8.1-sw * @default true * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * enableSearchAssist: true, * }) * ``` */ enableSearchAssist?: boolean; /** * Enables the V2 navigation and modular home page experience. * For more information, * see link:https://developers.thoughtspot.com/docs/full-app-customize[full app embed documentation]. * Supported embed types: `AppEmbed` * @version SDK: 1.28.0 | ThoughtSpot: 9.12.5.cl * @default false * Use the discoveryExperience.homePage option with * {@link HomePage.ModularWithStylingChanges} (V3) or * {@link HomePage.Focused} (V4) instead. * @deprecated The V1 and V2 home page experiences are deprecated. * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * modularHomeExperience : true, * }) * ``` */ modularHomeExperience?: boolean; /** * Configures the V3 navigation and home page experience. * For more information, see * link:https://developers.thoughtspot.com/docs/full-app-customize[full app embed documentation]. * Supported embed types: `AppEmbed` * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * // Enable V3 navigation and home page experience * discoveryExperience : { * primaryNavbarVersion: PrimaryNavbarVersion.Sliding, // Enable V3 navigation * homePage: HomePage.ModularWithStylingChanges, // Enable V3 modular home page * ... // other embed view config * }, * }) * ``` */ discoveryExperience?: DiscoveryExperience; /** * To set the initial state of the search bar in case of saved-answers. Use {@link collapseSearchBar} instead. * @version SDK: 1.32.0 | ThoughtSpot: 10.0.0.cl * @deprecated This flag is deprecated. * @default false */ collapseSearchBarInitially?: boolean; /** * This controls the initial behaviour of custom column groups accordion. * It takes DataPanelCustomColumnGroupsAccordionState enum values as input. * List of different enum values:- * - EXPAND_ALL: Expand all the accordion initially in data panel v2. * - COLLAPSE_ALL: Collapse all the accordions initially in data panel v2. * - EXPAND_FIRST: Expand the first accordion and collapse the rest. * * Supported embed types: `AppEmbed` * @version SDK: 1.32.0 | ThoughtSpot: 10.0.0.cl * @default DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL * @example * ```js * const embed = new AppEmbed('#embed', { * ... // other app view config * dataPanelCustomGroupsAccordionInitialState: * DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL, * }); * ``` */ dataPanelCustomGroupsAccordionInitialState?: DataPanelCustomColumnGroupsAccordionState; /** * Flag to use home page search bar mode * * Supported embed types: `AppEmbed` * @version SDK: 1.33.0 | ThoughtSpot: 10.3.0.cl */ homePageSearchBarMode?: HomePageSearchBarMode; /** * This flag is used to enable unified search experience for full app embed. * * Supported embed types: `AppEmbed` * @version SDK: 1.34.0 | ThoughtSpot: 10.5.0.cl * @default true * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * isUnifiedSearchExperienceEnabled: true, * }) * ``` */ isUnifiedSearchExperienceEnabled?: boolean; /** * This flag is used to enable the new connection experience for AppEmbed. * * Supported embed types: `AppEmbed` * @version SDK: 1.51.0 | ThoughtSpot Cloud: 26.8.0.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * newConnectionsExperience: true, * }) * ``` */ newConnectionsExperience?: boolean; /** * This flag is used to enable/disable the styling and grouping in a Liveboard. Use {@link isLiveboardMasterpiecesEnabled} instead. * @deprecated This flag is deprecated. * * Supported embed types: `AppEmbed`, `LiveboardEmbed` * @type {boolean} * @version SDK: 1.40.0 | ThoughtSpot: 10.11.0.cl * @example * ```js * // Replace with embed component name. For example, AppEmbed or LiveboardEmbed * const embed = new ('#tsEmbed', { * ... // other embed view config * isLiveboardStylingAndGroupingEnabled: true, * }) * ``` */ isLiveboardStylingAndGroupingEnabled?: boolean; /** * This flag is used to enable/disable the png embedding of liveboard in scheduled * mails * * Supported embed types: `AppEmbed`, `LiveboardEmbed` * @type {boolean} * @version SDK: 1.42.0 | ThoughtSpot: 10.14.0.cl * @example * ```js * // Replace with embed component name. For example, AppEmbed or LiveboardEmbed * const embed = new ('#tsEmbed', { * ... // other embed view config * isPNGInScheduledEmailsEnabled: true, * }) * ``` */ isPNGInScheduledEmailsEnabled?: boolean; /** * Enables the 'what you see is what you get' PDF export for Liveboards. Each tab is rendered on a single page * following the exact UI layout, instead of splitting visualizations across multiple A4 pages. * This feature is GA from SDK version 1.51.0 and ThoughtSpot version 26.8.0.cl. * * Supported embed types: `AppEmbed`, `LiveboardEmbed` * @type {boolean} * @version SDK: 1.48.0 | ThoughtSpot: 26.5.0.cl * @default true * @example * ```js * // Replace with embed component name. For example, AppEmbed or LiveboardEmbed * const embed = new ('#tsEmbed', { * ... // other embed view config * isContinuousLiveboardPDFEnabled: true, * }) * ``` */ isContinuousLiveboardPDFEnabled?: boolean; /** * This flag is used to enable/disable the XLSX/CSV download option for Liveboards * This feature is GA from SDK version 1.51.0 and ThoughtSpot version 26.6.0.cl * * Supported embed types: `AppEmbed`, `LiveboardEmbed` * @type {boolean} * @version SDK: 1.46.0 | ThoughtSpot: 26.3.0.cl * @default true * @example * ```js * // Replace with embed component name. For example, AppEmbed or LiveboardEmbed * const embed = new ('#tsEmbed', { * ... // other embed view config * isLiveboardXLSXCSVDownloadEnabled: true, * }) * ``` */ isLiveboardXLSXCSVDownloadEnabled?: boolean; /** * This flag is used to enable/disable the granular XLSX/CSV schedules feature * This feature is GA from SDK version 1.51.0 and ThoughtSpot version 26.6.0.cl * * Supported embed types: `AppEmbed`, `LiveboardEmbed` * @type {boolean} * @version SDK: 1.46.0 | ThoughtSpot: 26.3.0.cl * @default true * @example * ```js * // Replace with embed component name. For example, AppEmbed or LiveboardEmbed * const embed = new ('#tsEmbed', { * ... // other embed view config * isGranularXLSXCSVSchedulesEnabled: true, * }) * ``` */ isGranularXLSXCSVSchedulesEnabled?: boolean; /** * updatedSpotterChatPrompt : Controls the updated spotter chat prompt. * * Supported embed types: `AppEmbed` * @version SDK: 1.45.0 | ThoughtSpot: 26.2.0.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... //other embed view config * updatedSpotterChatPrompt : true, * }) * ``` */ updatedSpotterChatPrompt?: boolean; /** * showSpotterRadiance : Controls the radiance on the Spotter page. * * Supported embed types: `AppEmbed` * @version SDK: 1.52.0 | ThoughtSpot Cloud: 26.9.0.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... //other embed view config * showSpotterRadiance : true, * }) * ``` */ showSpotterRadiance?: boolean; /** * Sets the default query mode when Spotter loads — Fast Search or * Research Mode. Applies fresh on every new session for this embed * instance only; it does not persist as a user preference and does * not affect other embeds or native ThoughtSpot usage. * Only applicable when navigating to Spotter within the app. * * Supported embed types: `AppEmbed` * @version SDK: 1.52.0 | ThoughtSpot Cloud: 26.9.0.cl * @default SpotterQueryMode.FAST_SEARCH * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... //other embed view config * defaultQueryMode: SpotterQueryMode.RESEARCH, * }) * ``` */ defaultQueryMode?: SpotterQueryMode; /** * Controls the visibility of the past conversations sidebar. * * Supported embed types: `AppEmbed` * @version SDK: 1.46.0 | ThoughtSpot: 26.3.0.cl * @deprecated from SDK: 1.47.0 | ThoughtSpot: 26.4.0.cl * Use `spotterSidebarConfig.enablePastConversationsSidebar`. * @default false */ enablePastConversationsSidebar?: boolean; /** * Configuration for the Spotter sidebar UI customization. * Only applicable when navigating to Spotter within the app. * * Supported embed types: `AppEmbed` * @version SDK: 1.47.0 | ThoughtSpot: 26.4.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * // Deprecated standalone flag (backward compatibility) * enablePastConversationsSidebar: false, * // Recommended config; this value takes precedence * spotterSidebarConfig: { * enablePastConversationsSidebar: true, * spotterSidebarTitle: 'My Conversations', * // Opt in to pin/unpin conversations in embedded Spotter * spotterChatPinConfig: { * enabled: true, * }, * }, * }) * ``` */ spotterSidebarConfig?: SpotterSidebarViewConfig; /** * Configuration for customizing Spotter chat UI * branding in tool response cards. * * Supported embed types: `AppEmbed` * @version SDK: 1.46.0 | ThoughtSpot: 26.4.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... //other embed view config * spotterChatConfig: { * hideToolResponseCardBranding: true, * toolResponseCardBrandingLabel: 'MyBrand', * }, * }) * ``` */ spotterChatConfig?: SpotterChatViewConfig; /** * Configuration for the Spotter conversation sharing feature. * * Supported embed types: `AppEmbed` * @version SDK: 1.52.0 | ThoughtSpot Cloud: 26.9.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... //other embed view config * spotterShareConversationConfig: { * enableShareConversation: true, * }, * }) * ``` */ spotterShareConversationConfig?: SpotterShareConversationConfig; /** * Sets the default data sources (Models) for the Spotter experience shown on * the home page of the full app embed. Accepts a list of Model GUIDs to * preselect the data sources available to Spotter, or the literal * `'auto_mode'` sentinel to load Spotter with the "Auto" model selected by * default. This is consistent with how `worksheetId: 'auto_mode'` enables * Auto mode in `SpotterEmbed`. * * **Note**: If `'auto_mode'` is included alongside one or more Model GUIDs, * `'auto_mode'` takes precedence and is selected by default. * * **Note**: `'auto_mode'` requires the Spotter 3 experience with data source * discovery enabled on your instance, and a Spotter-enabled home page (for * example, `discoveryExperience.homePage: HomePage.Focused`). * * Supported embed types: `AppEmbed` * @version SDK: 1.52.0 | ThoughtSpot Cloud: 26.10.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * discoveryExperience: { homePage: HomePage.Focused }, * spotterDataSources: ['model-guid-1', 'model-guid-2'], // or ['auto_mode'] * }) * ``` */ spotterDataSources?: string[]; /** * Configuration for the SpotterViz interface shown on the Liveboard. * Customize the brand name, description, chat input placeholder, * starter prompts, and visibility of starter prompts in the SpotterViz panel. * * Supported embed types: `AppEmbed`, `LiveboardEmbed` * @version SDK: 1.50.0 | ThoughtSpot Cloud: 26.7.0.cl * @example * ```js * const embed = new AppEmbed('#embed-container', { * ... // other options * spotterViz: { * brandName: 'MyBrand', * brandHeadline: 'Hi, there! I\'m', * description: 'Ask questions about your data', * inputChatPlaceholder: 'Ask a question...', * hideStarterPrompts: false, * customStarterPrompts: [{ id: '1', displayText: 'Top products', fullPrompt: 'What are the top products by revenue?' }], * // The options below require SDK: 1.51.0 * loaderHeadline: 'Crunching the numbers...', * loaderTips: [ * { label: 'Tip', text: 'try asking about revenue by region' }, * { label: 'Tip', text: 'use natural language' }, * ], * liveboardBrandName: 'Reports', * spotterBrandName: 'Analyst', * insightTileBrandName: 'Insight card', * insightTileViewPlanLabel: 'View plan', * insightTileLoaderText: 'Generating insight', * }, * }) * ``` */ spotterViz?: SpotterVizConfig; /** * Enables the stop answer generation button in the Spotter embed UI, * allowing users to interrupt an ongoing answer generation. * * Supported embed types: `AppEmbed` * @version SDK: 1.48.0 | ThoughtSpot: 26.5.0.cl * @default false */ enableStopAnswerGenerationEmbed?: boolean; /** * To enable the homepage announcement banner. * Controls the visibility of the announcement section * on the homepage. * * Supported embed types: `AppEmbed` * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * enableHomepageAnnouncement: true, * }) * ``` * @version SDK: 1.48.0 | ThoughtSpot: 26.5.0.cl */ enableHomepageAnnouncement?: boolean; /** * If set to true, enables visualization data caching on the Liveboard. * @type {boolean} * @version SDK: 1.49.0 | ThoughtSpot: 26.6.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other options * enableLiveboardDataCache: true, * }) * ``` */ enableLiveboardDataCache?: boolean; /** * Visual overrides to customize the chart or table properties. * @version SDK: 1.49.0 | ThoughtSpot: 26.6.0.cl */ visualOverrides?: VisualizationOverrides; /** * updatedSpotterExperience : Controls the updated Spotter experience. * * Supported embed types: `AppEmbed` * @version SDK: 1.52.0 | ThoughtSpot Cloud: 26.9.0.cl * @default false * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... //other embed view config * updatedSpotterExperience : true, * }) * ``` */ updatedSpotterExperience?: boolean; /** * If set to `true`, the answer edit panel is hidden. * * Supported embed types: `AppEmbed` * @version SDK: 1.54.0 | ThoughtSpot Cloud: 26.11.0.cl * @example * ```js * const embed = new AppEmbed('#tsEmbed', { * ... // other embed view config * hideAnswerEditPanel: true, * }) * ``` */ hideAnswerEditPanel?: boolean; } /** * APP_INIT data shape for AppEmbed. * @internal */ export interface AppEmbedAppInitData extends DefaultAppInitData { embedParams?: { spotterSidebarConfig?: SpotterSidebarViewConfig; spotterVizConfig?: SpotterVizConfig; spotterShareConversationConfig?: SpotterShareConversationConfig; starterPrompts?: StarterPromptsConfig; }; } /** * Embeds full ThoughtSpot experience in a host application. * @group Embed components */ export class AppEmbed extends V1Embed { protected viewConfig: AppViewConfig; private readonly fullHeightController?: FullHeightController; constructor(domSelector: DOMSelector, viewConfig: AppViewConfig) { viewConfig.embedComponentType = 'AppEmbed'; super(domSelector, viewConfig); if (this.viewConfig.fullHeight === true) { this.fullHeightController = new FullHeightController(this.viewConfig, { getIframe: () => this.iFrame, setFrameHeight: (height) => this.setIFrameHeight(height), on: (eventType, callback) => { this.on(eventType, callback); }, trigger: (hostEvent, data) => { this.trigger(hostEvent, data); }, }); } } /** * Extends the default APP_INIT payload with `embedParams.spotterSidebarConfig` * so the conv-assist app can read sidebar configuration on initialisation. * * Precedence for `enablePastConversationsSidebar`: * `spotterSidebarConfig.enablePastConversationsSidebar` wins over the * deprecated top-level `enablePastConversationsSidebar` flag; if the former * is absent the latter is used as a fallback. * * An invalid `spotterDocumentationUrl` triggers a validation error and is * excluded from the payload rather than forwarded to the app. */ protected async getAppInitData(): Promise { const defaultAppInitData = await super.getAppInitData(); const sidebarInitData = buildSpotterSidebarAppInitData( defaultAppInitData, this.viewConfig, this.handleError.bind(this), ); const vizInitData = buildSpotterVizAppInitData(sidebarInitData, this.viewConfig); const shareInitData = buildSpotterShareConversationAppInitData(vizInitData, this.viewConfig); return buildStarterPromptsAppInitData(shareInitData, this.viewConfig); } /** * Constructs a map of parameters to be passed on to the * embedded Liveboard or visualization. */ protected getEmbedParams() { const params = this.getEmbedParamsObject(); return getQueryParamString(params, true); } protected getEmbedParamsObject() { const { tag, hideTagFilterChips, hideObjects, liveboardV2, showPrimaryNavbar, disableProfileAndHelp, hideHamburger, hideObjectSearch, hideNotification, hideApplicationSwitcher, hideOrgSwitcher, enableSearchAssist, newConnectionsExperience, dataPanelV2 = true, updatedSpotterExperience, hideLiveboardHeader = false, showLiveboardTitle = true, showLiveboardDescription = true, showMaskedFilterChip = false, isLiveboardMasterpiecesEnabled, newChartsLibrary, hideHomepageLeftNav = false, modularHomeExperience, isLiveboardHeaderSticky = true, enableAskSage, collapseSearchBarInitially = false, enable2ColumnLayout, isLiveboardAlwaysOn12ColLayout, enableCustomColumnGroups = false, dataPanelCustomGroupsAccordionInitialState = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL, collapseSearchBar = true, isLiveboardCompactHeaderEnabled, showLiveboardVerifiedBadge = true, showLiveboardReverifyBanner = true, hideIrrelevantChipsInLiveboardTabs, isEnhancedFilterInteractivityEnabled, homePageSearchBarMode, isUnifiedSearchExperienceEnabled = true, enablePendoHelp = true, discoveryExperience, coverAndFilterOptionInPDF, isLiveboardStylingAndGroupingEnabled, liveboardGutter, isPNGInScheduledEmailsEnabled = false, isLiveboardXLSXCSVDownloadEnabled, isGranularXLSXCSVSchedulesEnabled, isCentralizedLiveboardFilterUXEnabled = false, isScopedLiveboardFilteringEnabled, isLinkParametersEnabled, updatedSpotterChatPrompt, showSpotterRadiance, defaultQueryMode, enableStopAnswerGenerationEmbed, spotterChatConfig, spotterDataSources, isThisPeriodInDateFiltersEnabled, enableHomepageAnnouncement = false, isContinuousLiveboardPDFEnabled, enableLiveboardDataCache, hideAnswerEditPanel, } = this.viewConfig; let params: any = {}; params[Param.PrimaryNavHidden] = !showPrimaryNavbar; params[Param.HideProfleAndHelp] = !!disableProfileAndHelp; params[Param.HideApplicationSwitcher] = !!hideApplicationSwitcher; params[Param.HideOrgSwitcher] = !!hideOrgSwitcher; params[Param.HideLiveboardHeader] = hideLiveboardHeader; params[Param.ShowLiveboardTitle] = showLiveboardTitle; params[Param.ShowLiveboardDescription] = !!showLiveboardDescription; params[Param.ShowMaskedFilterChip] = showMaskedFilterChip; if (!isUndefined(isLiveboardMasterpiecesEnabled)) { params[Param.IsLiveboardMasterpiecesEnabled] = isLiveboardMasterpiecesEnabled; } if (newChartsLibrary !== undefined) { params[Param.EnableNewChartLibrary] = newChartsLibrary; } params[Param.LiveboardHeaderSticky] = isLiveboardHeaderSticky; params[Param.IsFullAppEmbed] = true; if (!isUndefined(isLiveboardCompactHeaderEnabled)) { params[Param.LiveboardHeaderV2] = isLiveboardCompactHeaderEnabled; } if (!isUndefined(isEnhancedFilterInteractivityEnabled)) { params[Param.IsEnhancedFilterInteractivityEnabled] = isEnhancedFilterInteractivityEnabled; } params[Param.ShowLiveboardVerifiedBadge] = showLiveboardVerifiedBadge; params[Param.ShowLiveboardReverifyBanner] = showLiveboardReverifyBanner; if (!isUndefined(hideIrrelevantChipsInLiveboardTabs)) { params[Param.HideIrrelevantFiltersInTab] = hideIrrelevantChipsInLiveboardTabs; } if (isUnifiedSearchExperienceEnabled !== undefined) { params[Param.IsUnifiedSearchExperienceEnabled] = isUnifiedSearchExperienceEnabled; } if (!isUndefined(coverAndFilterOptionInPDF)) { params[Param.CoverAndFilterOptionInPDF] = !!coverAndFilterOptionInPDF; } if (!isUndefined(hideAnswerEditPanel)) { params[Param.ShowAnswerEditPanel] = !hideAnswerEditPanel; } params = this.getBaseQueryParams(params); if (!isUndefined(updatedSpotterChatPrompt)) { params[Param.UpdatedSpotterChatPrompt] = !!updatedSpotterChatPrompt; } if (!isUndefined(showSpotterRadiance)) { params[Param.ShowSpotterRadiance] = !!showSpotterRadiance; } if (!isUndefined(defaultQueryMode)) { params[Param.DefaultQueryMode] = defaultQueryMode; } if (!isUndefined(enableStopAnswerGenerationEmbed)) { params[Param.EnableStopAnswerGenerationEmbed] = !!enableStopAnswerGenerationEmbed; } if (spotterDataSources && spotterDataSources.length) { params[Param.SpotterDataSources] = JSON.stringify(spotterDataSources); } // Handle spotterChatConfig params if (spotterChatConfig) { const { hideToolResponseCardBranding, toolResponseCardBrandingLabel, spotterFileUploadEnabled, spotterFileUploadFileTypes, enableStarterPrompts, openSpotterOnLiveboardByDefault, } = spotterChatConfig; setParamIfDefined(params, Param.HideToolResponseCardBranding, hideToolResponseCardBranding, true); setParamIfDefined(params, Param.ToolResponseCardBrandingLabel, toolResponseCardBrandingLabel); if (spotterFileUploadEnabled !== undefined) { params[Param.SpotterFileUploadEnabled] = spotterFileUploadEnabled; } if (enableStarterPrompts !== undefined) { params[Param.IsStarterPromptsEnabled] = enableStarterPrompts; } if (openSpotterOnLiveboardByDefault !== undefined) { params[Param.OpenSpotterOnLiveboardByDefault] = openSpotterOnLiveboardByDefault; } if (spotterFileUploadFileTypes !== undefined) { params[Param.SpotterFileUploadFileTypes] = JSON.stringify(spotterFileUploadFileTypes); } } if (!isUndefined(updatedSpotterExperience)) { params[Param.UpdatedSpotterExperience] = !!updatedSpotterExperience; } if (hideObjectSearch) { params[Param.HideObjectSearch] = !!hideObjectSearch; } if (hideHamburger) { params[Param.HideHamburger] = !!hideHamburger; } if (hideNotification) { params[Param.HideNotification] = !!hideNotification; } this.fullHeightController?.addQueryParams(params); if (tag) { params[Param.Tag] = tag; } if (hideObjects && hideObjects.length) { params[Param.HideObjects] = JSON.stringify(hideObjects); } if (liveboardV2 !== undefined) { params[Param.LiveboardV2Enabled] = liveboardV2; } if (enableSearchAssist !== undefined) { params[Param.EnableSearchAssist] = enableSearchAssist; } if (newConnectionsExperience !== undefined) { params[Param.EnableConnectionNewExperience] = newConnectionsExperience; } if (enable2ColumnLayout !== undefined) { params[Param.Enable2ColumnLayout] = enable2ColumnLayout; } if (isLiveboardAlwaysOn12ColLayout !== undefined) { params[Param.IsLiveboardAlwaysOn12ColLayout] = isLiveboardAlwaysOn12ColLayout; } if (enableAskSage) { params[Param.enableAskSage] = enableAskSage; } if (homePageSearchBarMode) { params[Param.HomePageSearchBarMode] = homePageSearchBarMode; } if (enablePendoHelp !== undefined) { params[Param.EnablePendoHelp] = enablePendoHelp; } if (isLiveboardStylingAndGroupingEnabled !== undefined) { params[ Param.IsLiveboardStylingAndGroupingEnabled ] = isLiveboardStylingAndGroupingEnabled; } if (liveboardGutter !== undefined) { params[Param.LiveboardGutter] = liveboardGutter; } if (isPNGInScheduledEmailsEnabled !== undefined) { params[Param.isPNGInScheduledEmailsEnabled] = isPNGInScheduledEmailsEnabled; } if (isLiveboardXLSXCSVDownloadEnabled !== undefined) { params[Param.isLiveboardXLSXCSVDownloadEnabled] = isLiveboardXLSXCSVDownloadEnabled; } if (isGranularXLSXCSVSchedulesEnabled !== undefined) { params[Param.isGranularXLSXCSVSchedulesEnabled] = isGranularXLSXCSVSchedulesEnabled; } if (hideTagFilterChips !== undefined) { params[Param.HideTagFilterChips] = hideTagFilterChips; } if (isLinkParametersEnabled !== undefined) { params[Param.isLinkParametersEnabled] = isLinkParametersEnabled; } if (isCentralizedLiveboardFilterUXEnabled != undefined) { params[ Param.isCentralizedLiveboardFilterUXEnabled ] = isCentralizedLiveboardFilterUXEnabled; } if (isScopedLiveboardFilteringEnabled !== undefined) { params[ Param.isScopedLiveboardFilteringEnabled ] = isScopedLiveboardFilteringEnabled; } if (isThisPeriodInDateFiltersEnabled !== undefined) { params[Param.IsThisPeriodInDateFiltersEnabled] = isThisPeriodInDateFiltersEnabled; } if (enableHomepageAnnouncement !== undefined) { params[Param.EnableHomepageAnnouncement] = enableHomepageAnnouncement; } if (isContinuousLiveboardPDFEnabled !== undefined) { params[Param.IsWYSIWYGLiveboardPDFEnabled] = isContinuousLiveboardPDFEnabled; } if (enableLiveboardDataCache !== undefined) { params[Param.EnableLiveboardDataCache] = enableLiveboardDataCache; } params[Param.DataPanelV2Enabled] = dataPanelV2; params[Param.HideHomepageLeftNav] = hideHomepageLeftNav; params[Param.CollapseSearchBarInitially] = collapseSearchBarInitially || collapseSearchBar; params[Param.EnableCustomColumnGroups] = enableCustomColumnGroups; if (dataPanelCustomGroupsAccordionInitialState === DataPanelCustomColumnGroupsAccordionState.COLLAPSE_ALL || dataPanelCustomGroupsAccordionInitialState === DataPanelCustomColumnGroupsAccordionState.EXPAND_FIRST ) { params[ Param.DataPanelCustomGroupsAccordionInitialState ] = dataPanelCustomGroupsAccordionInitialState; } else { params[Param.DataPanelCustomGroupsAccordionInitialState] = DataPanelCustomColumnGroupsAccordionState.EXPAND_ALL; } if (modularHomeExperience !== undefined) { params[Param.ModularHomeExperienceEnabled] = modularHomeExperience; } // Navigation V1/V2 and home page V1/V2 are deprecated. V3 is now the // default experience, Need to send navigationVersion=v3 and // homepageVersion=v3 even when the no discoveryExperience / // modularHomeExperience config is set. Without these // params, older TSA versions (< 26.7) fall back to V2 for nav and // home page, so the defaults must be sent explicitly from the SDK. params[Param.NavigationVersion] = PrimaryNavbarVersion.Sliding; params[Param.HomepageVersion] = HomePage.ModularWithStylingChanges; if (discoveryExperience) { // listPageVersion can be changed to v2 or v3 if (discoveryExperience.listPageVersion !== undefined) { params[Param.ListPageVersion] = discoveryExperience.listPageVersion; } if (discoveryExperience.homePage === HomePage.Focused) { params[Param.HomepageVersion] = HomePage.Focused; // The Focused (V4) homepage experience requires the updated // Spotter chat prompt. Enable it automatically unless the // developer has explicitly set updatedSpotterChatPrompt. if (isUndefined(updatedSpotterChatPrompt)) { params[Param.UpdatedSpotterChatPrompt] = true; } } } return params; } /** * Constructs the URL of the ThoughtSpot app page to be rendered. * @param pageId The ID of the page to be embedded. */ public getIFrameSrc(): string { const { pageId, path, modularHomeExperience } = this.viewConfig; const pageRoute = this.formatPath(path) || this.getPageRoute(pageId, modularHomeExperience); let url = `${this.getRootIframeSrc()}/${pageRoute}`; const tsPostHashParams = this.getThoughtSpotPostUrlParams(); url = `${url}${tsPostHashParams}`; return url; } /** * Gets the ThoughtSpot route of the page for a particular page ID. * @param pageId The identifier for a page in the ThoughtSpot app. * @param modularHomeExperience */ private getPageRoute(pageId: Page, modularHomeExperience = false) { switch (pageId) { case Page.Search: return 'answer'; case Page.Answers: return modularHomeExperience ? 'home/answers' : 'answers'; case Page.Liveboards: return modularHomeExperience ? 'home/liveboards' : 'pinboards'; case Page.Pinboards: return modularHomeExperience ? 'home/liveboards' : 'pinboards'; case Page.Data: return 'data/tables'; case Page.SpotIQ: return modularHomeExperience ? 'home/spotiq-analysis' : 'insights/results'; case Page.Monitor: return modularHomeExperience ? 'home/monitor-alerts' : 'insights/monitor-alerts'; case Page.Collections: return 'collections'; case Page.LiveboardSchedules: return 'home/liveboard-schedules'; case Page.Home: default: return 'home'; } } /** * Formats the path provided by the user. * @param path The URL path. * @returns The URL path that the embedded app understands. */ private formatPath(path: string) { if (!path) { return null; } // remove leading slash if (path.indexOf('/') === 0) { return path.substring(1); } return path; } /** * Navigate to particular page for app embed. eg:answers/pinboards/home * This is used for embedding answers, pinboards, visualizations and full application * only. * @param path string | number The string, set to iframe src and navigate to new page * eg: appEmbed.navigateToPage('pinboards') * When used with `noReload` (default: true) this can also be a number * like 1/-1 to go forward/back. * @param noReload boolean Trigger the navigation without reloading the page * @version SDK: 1.12.0 | ThoughtSpot: 8.4.0.cl, 8.4.1-sw */ public navigateToPage(path: string | number, noReload = false): void { if (!this.iFrame) { logger.log('Please call render before invoking this method'); return; } const overrideHistoryState = this.viewConfig?.overrideHistoryState; if (noReload || overrideHistoryState) { this.trigger(HostEvent.Navigate, path); } else { if (typeof path !== 'string') { logger.warn('Path can only by a string when triggered without noReload'); return; } const iframeSrc = this.iFrame.src; const embedPath = '#/embed'; const currentPath = iframeSrc.includes(embedPath) ? embedPath : '#'; this.iFrame.src = `${iframeSrc.split(currentPath)[0]}${currentPath}/${path.replace( /^\/?#?\//, '', )}`; } } /** * Destroys the ThoughtSpot embed, and remove any nodes from the DOM. * @version SDK: 1.39.0 | ThoughtSpot: 10.10.0.cl */ public destroy() { super.destroy(); this.fullHeightController?.destroy(); } private postRender() { this.fullHeightController?.onRender(); } /** * Renders the embedded application pages in the ThoughtSpot app. * @param renderOptions An object containing the page ID * to be embedded. */ public async render(): Promise { await super.render(); const src = this.getIFrameSrc(); await this.renderV1Embed(src); this.postRender(); return this; } }