From e0799f74e1c7e1d75ad238ea56bc8931949a0626 Mon Sep 17 00:00:00 2001 From: Delirium Date: Mon, 12 Feb 2024 15:34:43 +0100 Subject: [PATCH] feat(ui): ai summary UI (#1868) * feat(ui): start ai summary UI * feat(ui): add api * feat(ui): rm console log * feat(ui): style fix * feat(ui): some ui changes * feat(ui): some ui changes * feat(ui): some text formatting * fix(ui): method fix * fix(ui): fix icon gen * fix(ui): fix global ts declaration for window env --- frontend/app/api_client.ts | 24 +- .../Player/ReplayPlayer/AiSubheader.tsx | 266 ++++++++++++++++++ .../Player/ReplayPlayer/PlayerBlock.tsx | 42 ++- .../Player/ReplayPlayer/PlayerContent.tsx | 19 +- .../ReplayPlayer/SummaryBlock/index.tsx | 85 ++++++ .../app/components/Session/RightBlock.tsx | 4 +- .../app/components/Session_/Issues/Issues.js | 24 +- .../components/shared/Bookmark/Bookmark.tsx | 4 +- frontend/app/components/ui/Icons/index.ts | 1 + frontend/app/components/ui/Icons/sparkles.tsx | 19 ++ frontend/app/components/ui/SVG.tsx | 8 +- frontend/app/mstore/aiSummaryStore.ts | 59 ++++ frontend/app/mstore/index.tsx | 6 +- frontend/app/services/AiService.ts | 13 + frontend/app/services/index.ts | 4 + frontend/app/svg/icons/sparkles.svg | 7 + frontend/globals.d.ts | 11 + frontend/scripts/icons.js | 4 +- 18 files changed, 531 insertions(+), 69 deletions(-) create mode 100644 frontend/app/components/Session/Player/ReplayPlayer/AiSubheader.tsx create mode 100644 frontend/app/components/Session/Player/ReplayPlayer/SummaryBlock/index.tsx create mode 100644 frontend/app/components/ui/Icons/sparkles.tsx create mode 100644 frontend/app/mstore/aiSummaryStore.ts create mode 100644 frontend/app/services/AiService.ts create mode 100644 frontend/app/svg/icons/sparkles.svg create mode 100644 frontend/globals.d.ts diff --git a/frontend/app/api_client.ts b/frontend/app/api_client.ts index 3d1b7993d..1feda4dfd 100644 --- a/frontend/app/api_client.ts +++ b/frontend/app/api_client.ts @@ -31,7 +31,8 @@ const siteIdRequiredPaths: string[] = [ '/feature-flags', '/check-recording-status', '/usability-tests', - '/tags' + '/tags', + '/intelligent' ]; export const clean = (obj: any, forbiddenValues: any[] = [undefined, '']): any => { @@ -71,7 +72,7 @@ export default class APIClient { this.siteId = siteId; } - private getInit(method: string = 'GET', params?: any): RequestInit { + private getInit(method: string = 'GET', params?: any, reqHeaders?: Record): RequestInit { // Always fetch the latest JWT from the store const jwt = store.getState().getIn(['user', 'jwt']); const headers = new Headers({ @@ -79,6 +80,12 @@ export default class APIClient { 'Content-Type': 'application/json', }); + if (reqHeaders) { + for (const [key, value] of Object.entries(reqHeaders)) { + headers.set(key, value); + } + } + if (jwt) { headers.set('Authorization', `Bearer ${jwt}`); } @@ -122,15 +129,14 @@ export default class APIClient { async fetch(path: string, params?: any, method: string = 'GET', options: { clean?: boolean - } = { clean: true }): Promise { + } = { clean: true }, headers?: Record): Promise { let jwt = store.getState().getIn(['user', 'jwt']); if (!path.includes('/refresh') && jwt && this.isTokenExpired(jwt)) { jwt = await this.handleTokenRefresh(); (this.init.headers as Headers).set('Authorization', `Bearer ${jwt}`); } - const init = this.getInit(method, options.clean && params ? clean(params) : params); - + const init = this.getInit(method, options.clean && params ? clean(params) : params, headers); if (params !== undefined) { const cleanedParams = options.clean ? clean(params) : params; @@ -184,14 +190,14 @@ export default class APIClient { } } - get(path: string, params?: any, options?: any): Promise { + get(path: string, params?: any, options?: any, headers?: Record): Promise { this.init.method = 'GET'; - return this.fetch(queried(path, params), 'GET', options); + return this.fetch(queried(path, params), options, 'GET', undefined, headers); } - post(path: string, params?: any, options?: any): Promise { + post(path: string, params?: any, options?: any, headers?: Record): Promise { this.init.method = 'POST'; - return this.fetch(path, params, 'POST'); + return this.fetch(path, params, 'POST', options, headers); } put(path: string, params?: any, options?: any): Promise { diff --git a/frontend/app/components/Session/Player/ReplayPlayer/AiSubheader.tsx b/frontend/app/components/Session/Player/ReplayPlayer/AiSubheader.tsx new file mode 100644 index 000000000..9d37dc839 --- /dev/null +++ b/frontend/app/components/Session/Player/ReplayPlayer/AiSubheader.tsx @@ -0,0 +1,266 @@ +import { useStore } from 'App/mstore'; +import SummaryBlock from "Components/Session/Player/ReplayPlayer/SummaryBlock"; +import React, { useMemo } from 'react'; +import { Icon, Tooltip } from 'UI'; +import QueueControls from 'App/components/Session_/QueueControls'; +import Bookmark from 'Shared/Bookmark'; +import SharePopup from 'Shared/SharePopup/SharePopup'; +import Issues from 'App/components/Session_/Issues/Issues'; +import NotePopup from 'App/components/Session_/components/NotePopup'; +import ItemMenu from 'App/components/Session_/components/HeaderMenu'; +import { useModal } from 'App/components/Modal'; +import BugReportModal from 'App/components/Session_/BugReport/BugReportModal'; +import { PlayerContext } from 'App/components/Session/playerContext'; +import { observer } from 'mobx-react-lite'; +import AutoplayToggle from 'Shared/AutoplayToggle'; +import { connect } from 'react-redux'; +import SessionTabs from 'Components/Session/Player/SharedComponents/SessionTabs'; +import { IFRAME } from 'App/constants/storageKeys'; +import cn from 'classnames'; +import { Switch } from 'antd'; + +const localhostWarn = (project: string): string => project + '_localhost_warn'; +const disableDevtools = 'or_devtools_uxt_toggle'; + +function SubHeader(props: any) { + const localhostWarnKey = localhostWarn(props.siteId); + const defaultLocalhostWarn = localStorage.getItem(localhostWarnKey) !== '1'; + const [showWarningModal, setWarning] = React.useState(defaultLocalhostWarn); + const { player, store } = React.useContext(PlayerContext); + const { width, height, endTime, location: currentLocation = 'loading...' } = store.get(); + const hasIframe = localStorage.getItem(IFRAME) === 'true'; + const { uxtestingStore } = useStore(); + + const enabledIntegration = useMemo(() => { + const { integrations } = props; + if (!integrations || !integrations.size) { + return false; + } + + return integrations.some((i: Record) => i.token); + }, [props.integrations]); + + const { showModal, hideModal } = useModal(); + + const location = + currentLocation && currentLocation.length > 70 + ? `${currentLocation.slice(0, 25)}...${currentLocation.slice(-40)}` + : currentLocation; + + const showReportModal = () => { + const { tabStates, currentTab } = store.get(); + const resourceList = tabStates[currentTab]?.resourceList || []; + const exceptionsList = tabStates[currentTab]?.exceptionsList || []; + const eventsList = tabStates[currentTab]?.eventList || []; + const graphqlList = tabStates[currentTab]?.graphqlList || []; + const fetchList = tabStates[currentTab]?.fetchList || []; + + const mappedResourceList = resourceList + .filter((r) => r.isRed || r.isYellow) + // @ts-ignore + .concat(fetchList.filter((i) => parseInt(i.status) >= 400)) + // @ts-ignore + .concat(graphqlList.filter((i) => parseInt(i.status) >= 400)); + + player.pause(); + const xrayProps = { + currentLocation: currentLocation, + resourceList: mappedResourceList, + exceptionsList: exceptionsList, + eventsList: eventsList, + endTime: endTime, + }; + showModal( + // @ts-ignore + , + { right: true, width: 620 } + ); + }; + + const showSummary = () => { + player.pause(); + showModal(, { right: true, width: 330 }) + } + + const showWarning = + location && /(localhost)|(127.0.0.1)|(0.0.0.0)/.test(location) && showWarningModal; + const closeWarning = () => { + localStorage.setItem(localhostWarnKey, '1'); + setWarning(false); + }; + + const toggleDevtools = (enabled: boolean): void => { + localStorage.setItem(disableDevtools, enabled ? '0' : '1'); + uxtestingStore.setHideDevtools(!enabled); + }; + + const additionalMenu = [ + { + key: 1, + component: , + }, + { + key: 2, + component: , + }, + { + key: 3, + component: ( +
+ +
Create Bug Report
+
+ ), + }, + { + key: 4, + component: ( + + +
Share
+ + } + /> + ), + }, + ]; + if (enabledIntegration) { + additionalMenu.push({ + key: 5, + component: , + }); + } + + return ( + <> +
+ {showWarning ? ( +
+ Some assets may load incorrectly on localhost. + + Learn More + +
+ +
+
+ ) : null} + +
+ + + + {uxtestingStore.isUxt() ? ( + + ) : ( +
+ {/* @ts-ignore */} + +
+ )} +
+
+ {location && ( +
+ +
+ )} + + ); +} + +function SummaryButton({ onClick }: { onClick?: () => void }) { + const [isHovered, setHovered] = React.useState(false); + + return ( +
+
setHovered(true)} + onMouseLeave={() => setHovered(false)}> + +
AI Summary
+
+
+ ); +} + +const gradientButton = { + border: 'double 1px transparent', + borderRadius: '60px', + background: + 'linear-gradient(#f6f6f6, #f6f6f6), linear-gradient(to right, #394EFF 0%, #3EAAAF 100%)', + backgroundOrigin: 'border-box', + backgroundClip: 'content-box, border-box', + cursor: 'pointer', +}; +const onHoverFillStyle = { + width: '100%', + height: '100%', + display: 'flex', + borderRadius: '60px', + gap: 2, + alignItems: 'center', + padding: '4px 8px', + background: + 'linear-gradient(156deg, #E3E6FF 0%, #E4F3F4 69.48%)', +}; +const fillStyle = { + width: '100%', + height: '100%', + display: 'flex', + borderRadius: '60px', + gap: 2, + alignItems: 'center', + padding: '4px 8px', +} + +export default connect((state: Record) => ({ + siteId: state.getIn(['site', 'siteId']), + integrations: state.getIn(['issues', 'list']), + modules: state.getIn(['user', 'account', 'modules']) || [], +}))(observer(SubHeader)); diff --git a/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlock.tsx b/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlock.tsx index 3aa3f1f8e..5fea4e393 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlock.tsx +++ b/frontend/app/components/Session/Player/ReplayPlayer/PlayerBlock.tsx @@ -3,7 +3,7 @@ import cn from 'classnames'; import { connect } from 'react-redux'; import Player from './PlayerInst'; import SubHeader from 'Components/Session_/Subheader'; - +import AiSubheader from 'Components/Session/Player/ReplayPlayer/AiSubheader'; import styles from 'Components/Session_/playerBlock.module.css'; interface IProps { @@ -11,39 +11,33 @@ interface IProps { sessionId: string; disabled: boolean; activeTab: string; - jiraConfig: Record - fullView?: boolean + jiraConfig: Record; + fullView?: boolean; + setActiveTab: (tab: string) => void; } function PlayerBlock(props: IProps) { - const { - fullscreen, - sessionId, - disabled, - activeTab, - jiraConfig, - fullView = false, - } = props; + const { fullscreen, sessionId, disabled, activeTab, jiraConfig, fullView = false, setActiveTab } = props; - const shouldShowSubHeader = !fullscreen && !fullView + const originStr = window.env.ORIGIN || window.location.origin + const isSaas = /api\.openreplay\.com/.test(originStr) + + const shouldShowSubHeader = !fullscreen && !fullView; return ( -
- {shouldShowSubHeader ? ( - - ) : null} - +
+ {shouldShowSubHeader ? + isSaas + ? + : + : null} +
); } -export default connect((state: any) => ({ +export default connect((state: Record) => ({ fullscreen: state.getIn(['components', 'player', 'fullscreen']), sessionId: state.getIn(['sessions', 'current']).sessionId, disabled: state.getIn(['components', 'targetDefiner', 'inspectorMode']), jiraConfig: state.getIn(['issues', 'list'])[0], -}))(PlayerBlock) \ No newline at end of file +}))(PlayerBlock); diff --git a/frontend/app/components/Session/Player/ReplayPlayer/PlayerContent.tsx b/frontend/app/components/Session/Player/ReplayPlayer/PlayerContent.tsx index d64567d21..ae347eb38 100644 --- a/frontend/app/components/Session/Player/ReplayPlayer/PlayerContent.tsx +++ b/frontend/app/components/Session/Player/ReplayPlayer/PlayerContent.tsx @@ -60,27 +60,16 @@ function PlayerContent({ session, fullscreen, activeTab, setActiveTab }: IProps) style={activeTab && !fullscreen ? { maxWidth: 'calc(100% - 270px)' } : undefined} >
- +
- {activeTab !== '' && ( - - )} + {!fullscreen && activeTab !== '' ? ( + + ) : null} )} ); } -function RightMenu({ tabs, activeTab, setActiveTab, fullscreen }: any) { - return ( - !fullscreen ? : null - ); -} - export default observer(PlayerContent); diff --git a/frontend/app/components/Session/Player/ReplayPlayer/SummaryBlock/index.tsx b/frontend/app/components/Session/Player/ReplayPlayer/SummaryBlock/index.tsx new file mode 100644 index 000000000..e68721356 --- /dev/null +++ b/frontend/app/components/Session/Player/ReplayPlayer/SummaryBlock/index.tsx @@ -0,0 +1,85 @@ +import React from 'react'; +import Icon from 'UI/Icon'; +import { useStore } from 'App/mstore'; +import { observer } from 'mobx-react-lite'; + +const userBehaviorRegex = /User\s+(\w+\s+)?Behavior/i; +const issuesErrorsRegex = /Issues\s+(and\s+|,?\s+)?(\w+\s+)?Errors/i; + +function testLine(line: string): boolean { + return userBehaviorRegex.test(line) || issuesErrorsRegex.test(line); +} + +function SummaryBlock({ sessionId }: { sessionId: string }) { + const { aiSummaryStore } = useStore(); + + React.useEffect(() => { + void aiSummaryStore.getSummary(sessionId); + }, []); + + const formattedText = aiSummaryStore.text.split('\n').map((line) => { + if (testLine(line)) { + return
{line}
; + } + if (line.startsWith('*')) { + return
  • {line.replace('* ', '')}
  • ; + } + return
    {line}
    ; + }); + + return ( +
    +
    + +
    AI Summary
    +
    + + {aiSummaryStore.text ? ( +
    +
    + Here’s the AI breakdown of the session, covering user behavior and technical insights. +
    + <>{formattedText.map((v) => v)} +
    + ) : ( + + )} +
    + ); +} + +function TextPlaceholder() { + return ( +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + ); +} + +const summaryBlockStyle: React.CSSProperties = { + background: 'linear-gradient(156deg, #E3E6FF 0%, #E4F3F4 69.48%)', + width: '100%', + height: '100vh', + overflow: 'auto', + display: 'flex', + flexDirection: 'column', + gap: '0.5rem', + padding: '1rem', +}; + +export default observer(SummaryBlock); diff --git a/frontend/app/components/Session/RightBlock.tsx b/frontend/app/components/Session/RightBlock.tsx index dd082681d..a09003d3c 100644 --- a/frontend/app/components/Session/RightBlock.tsx +++ b/frontend/app/components/Session/RightBlock.tsx @@ -1,7 +1,9 @@ +import SummaryBlock from 'Components/Session/Player/ReplayPlayer/SummaryBlock'; import React from 'react'; +import Session from 'Types/session/session'; import EventsBlock from '../Session_/EventsBlock'; import PageInsightsPanel from '../Session_/PageInsightsPanel/PageInsightsPanel'; -import TagWatch from "Components/Session/Player/TagWatch"; +import TagWatch from 'Components/Session/Player/TagWatch'; import cn from 'classnames'; import stl from './rightblock.module.css'; diff --git a/frontend/app/components/Session_/Issues/Issues.js b/frontend/app/components/Session_/Issues/Issues.js index 7de41b097..dc610334f 100644 --- a/frontend/app/components/Session_/Issues/Issues.js +++ b/frontend/app/components/Session_/Issues/Issues.js @@ -1,6 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; -import { Popover, Button } from 'UI'; +import { Popover, Button, Icon } from 'UI'; import IssuesModal from './IssuesModal'; import { fetchProjects, fetchMeta } from 'Duck/assignments'; @@ -53,10 +53,7 @@ class Issues extends React.Component { }; render() { - const { - sessionId, - issuesIntegration, - } = this.props; + const { sessionId, issuesIntegration, isInline } = this.props; const provider = issuesIntegration.first()?.provider || ''; return ( @@ -68,11 +65,18 @@ class Issues extends React.Component {
    )} > -
    - -
    + {isInline ? ( +
    + +
    Create Issue
    +
    + ) : ( +
    + +
    + )} ); } diff --git a/frontend/app/components/shared/Bookmark/Bookmark.tsx b/frontend/app/components/shared/Bookmark/Bookmark.tsx index d011af5f7..baf3d04cc 100644 --- a/frontend/app/components/shared/Bookmark/Bookmark.tsx +++ b/frontend/app/components/shared/Bookmark/Bookmark.tsx @@ -45,7 +45,7 @@ function Bookmark(props: Props) { color={isFavorite ? 'teal' : undefined} size="16" /> - {isEnterprise ? 'Vault' : 'Bookmark'} + {isEnterprise ? 'Vault' : 'Bookmark'}
    ) : ( )} diff --git a/frontend/app/components/ui/Icons/index.ts b/frontend/app/components/ui/Icons/index.ts index d8aa47651..b07278749 100644 --- a/frontend/app/components/ui/Icons/index.ts +++ b/frontend/app/components/ui/Icons/index.ts @@ -427,6 +427,7 @@ export { default as Sleep } from './sleep'; export { default as Sliders } from './sliders'; export { default as Social_slack } from './social_slack'; export { default as Social_trello } from './social_trello'; +export { default as Sparkles } from './sparkles'; export { default as Speedometer2 } from './speedometer2'; export { default as Spinner } from './spinner'; export { default as Star_solid } from './star_solid'; diff --git a/frontend/app/components/ui/Icons/sparkles.tsx b/frontend/app/components/ui/Icons/sparkles.tsx new file mode 100644 index 000000000..70ac7ef2c --- /dev/null +++ b/frontend/app/components/ui/Icons/sparkles.tsx @@ -0,0 +1,19 @@ + +/* Auto-generated, do not edit */ +import React from 'react'; + +interface Props { + size?: number | string; + width?: number | string; + height?: number | string; + fill?: string; +} + +function Sparkles(props: Props) { + const { size = 14, width = size, height = size, fill = '' } = props; + return ( + + ); +} + +export default Sparkles; diff --git a/frontend/app/components/ui/SVG.tsx b/frontend/app/components/ui/SVG.tsx index 2cb84072e..eaad56b14 100644 --- a/frontend/app/components/ui/SVG.tsx +++ b/frontend/app/components/ui/SVG.tsx @@ -429,6 +429,7 @@ import { Sliders, Social_slack, Social_trello, + Sparkles, Speedometer2, Spinner, Star_solid, @@ -474,9 +475,7 @@ import { Zoom_in } from './Icons' - -// export type NewIconNames = 'activity' | 'alarm_clock' | 'alarm_plus' | 'all_sessions' | 'analytics' | 'anchor' | 'arrow_alt_square_right' | 'arrow_bar_left' | 'arrow_clockwise' | 'arrow_counterclockwise' | 'arrow_down_short' | 'arrow_down_up' | 'arrow_down' | 'arrow_repeat' | 'arrow_right_short' | 'arrow_square_left' | 'arrow_square_right' | 'arrow_up_short' | 'arrow_up' | 'arrows_angle_extend' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar_chart_line' | 'bar_pencil' | 'battery_charging' | 'battery' | 'bell_fill' | 'bell_plus' | 'bell_slash' | 'bell' | 'binoculars' | 'book_doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business_time' | 'calendar_alt' | 'calendar_check' | 'calendar_day' | 'calendar' | 'call' | 'camera_alt' | 'camera_video_off' | 'camera_video' | 'camera' | 'card_checklist' | 'card_list' | 'card_text' | 'caret_down_fill' | 'caret_left_fill' | 'caret_right_fill' | 'caret_up_fill' | 'chat_dots' | 'chat_left_text' | 'chat_right_text' | 'chat_square_quote' | 'check_circle_fill' | 'check_circle' | 'check' | 'chevron_double_left' | 'chevron_double_right' | 'chevron_down' | 'chevron_left' | 'chevron_right' | 'chevron_up' | 'circle_fill' | 'circle' | 'click_hesitation' | 'click_rage' | 'clipboard_check' | 'clipboard_list_check' | 'clock_history' | 'clock' | 'close' | 'cloud_fog2_fill' | 'code' | 'cog' | 'cogs' | 'collection_play' | 'collection' | 'columns_gap_filled' | 'columns_gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit_card_2_back' | 'credit_card_front' | 'cross' | 'cubes' | 'cursor_trash' | 'dash' | 'dashboard_icn' | 'db_icons/icn_card_clickMap' | 'db_icons/icn_card_errors' | 'db_icons/icn_card_funnel' | 'db_icons/icn_card_funnels' | 'db_icons/icn_card_insights' | 'db_icons/icn_card_library' | 'db_icons/icn_card_mapchart' | 'db_icons/icn_card_pathAnalysis' | 'db_icons/icn_card_performance' | 'db_icons/icn_card_resources' | 'db_icons/icn_card_table' | 'db_icons/icn_card_timeseries' | 'db_icons/icn_card_webVitals' | 'desktop' | 'device' | 'diagram_3' | 'dice_3' | 'dizzy' | 'door_closed' | 'doublecheck' | 'download' | 'drag' | 'edit' | 'ellipsis_v' | 'emoji_dizzy' | 'enter' | 'envelope_check' | 'envelope_paper' | 'envelope_x' | 'envelope' | 'errors_icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i_cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation_circle_fill' | 'exclamation_circle' | 'exclamation_triangle' | 'expand_wide' | 'explosion' | 'external_link_alt' | 'eye_slash_fill' | 'eye_slash' | 'eye' | 'fetch' | 'fflag_multi' | 'fflag_single' | 'file_bar_graph' | 'file_code' | 'file_medical_alt' | 'file_pdf' | 'file' | 'files' | 'filetype_js' | 'filetype_pdf' | 'filter' | 'filters/arrow_return_right' | 'filters/browser' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu_load' | 'filters/custom' | 'filters/device' | 'filters/dom_complete' | 'filters/duration' | 'filters/error' | 'filters/fetch_failed' | 'filters/fetch' | 'filters/file_code' | 'filters/graphql' | 'filters/i_cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory_load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance_network_request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev_id' | 'filters/state_action' | 'filters/tag_element' | 'filters/ttfb' | 'filters/user_alt' | 'filters/userid' | 'filters/view' | 'flag_na' | 'folder_plus' | 'folder2' | 'fullscreen' | 'funnel/cpu_fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji_angry_fill' | 'funnel/emoji_angry' | 'funnel/emoji_dizzy_fill' | 'funnel/exclamation_circle_fill' | 'funnel/exclamation_circle' | 'funnel/file_earmark_break_fill' | 'funnel/file_earmark_break' | 'funnel/file_earmark_minus_fill' | 'funnel/file_earmark_minus' | 'funnel/file_medical_alt' | 'funnel/file_x' | 'funnel/hdd_fill' | 'funnel/hourglass_top' | 'funnel/image_fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch_exclamation_fill' | 'funnel/sd_card' | 'funnel_fill' | 'funnel_new' | 'funnel' | 'gear_fill' | 'gear' | 'geo_alt_fill_custom' | 'github' | 'graph_up_arrow' | 'graph_up' | 'grid_1x2' | 'grid_3x3' | 'grid_check' | 'grid_horizontal' | 'grid' | 'grip_horizontal' | 'hash' | 'hdd_stack' | 'headset' | 'heart_rate' | 'high_engagement' | 'history' | 'hourglass_start' | 'ic_errors' | 'ic_network' | 'ic_rage' | 'ic_resources' | 'id_card' | 'image' | 'info_circle_fill' | 'info_circle' | 'info_square' | 'info' | 'input_hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag_text' | 'integrations/bugsnag' | 'integrations/cloudwatch_text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/elasticsearch_text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira_text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic_text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay_text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar_text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry_text' | 'integrations/sentry' | 'integrations/slack_bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic_text' | 'integrations/sumologic' | 'integrations/teams_white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal_code' | 'key' | 'layer_group' | 'layers_half' | 'lightbulb_on' | 'lightbulb' | 'link_45deg' | 'list_alt' | 'list_arrow' | 'list_ul' | 'list' | 'lock_alt' | 'low_disc_space' | 'magic' | 'map_marker_alt' | 'memory_ios' | 'memory' | 'mic_mute' | 'mic' | 'minus' | 'mobile' | 'mouse_alt' | 'mouse_pointer_click' | 'network' | 'next1' | 'no_dashboard' | 'no_metrics_chart' | 'no_metrics' | 'no_recordings' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause_circle_fill' | 'pause_fill' | 'pause' | 'pdf_download' | 'pencil_stop' | 'pencil' | 'people' | 'percent' | 'performance_icon' | 'person_border' | 'person_fill' | 'person' | 'pie_chart_fill' | 'pin_fill' | 'play_circle_bold' | 'play_circle_light' | 'play_circle' | 'play_fill_new' | 'play_fill' | 'play_hover' | 'play' | 'plug' | 'plus_circle' | 'plus_lg' | 'plus' | 'pointer_sessions_search' | 'prev1' | 'pulse' | 'puzzle_piece' | 'puzzle' | 'question_circle' | 'question_lg' | 'quote_left' | 'quote_right' | 'quotes' | 'record_btn' | 'record_circle_fill' | 'record_circle' | 'record2' | 'redo_back' | 'redo' | 'redux' | 'remote_control' | 'replay_10' | 'resources_icon' | 'safe_fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share_alt' | 'shield_lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost_split' | 'signup' | 'skip_forward_fill' | 'skip_forward' | 'slack' | 'slash_circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'speedometer2' | 'spinner' | 'star_solid' | 'star' | 'step_forward' | 'stickies' | 'stop_record_circle' | 'stopwatch' | 'store' | 'sync_alt' | 'table_new' | 'table' | 'tablet_android' | 'tachometer_slow' | 'tachometer_slowest' | 'tags' | 'team_funnel' | 'telephone_fill' | 'telephone' | 'terminal' | 'text_paragraph' | 'thermometer_sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user_alt' | 'user_circle' | 'user_friends' | 'user_switch' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web_vitals' | 'wifi' | 'window_alt' | 'window_restore' | 'window_x' | 'window' | 'zoom_in'; -export type IconNames = 'activity' | 'alarm-clock' | 'alarm-plus' | 'all-sessions' | 'analytics' | 'anchor' | 'arrow-alt-square-right' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-square-left' | 'arrow-square-right' | 'arrow-up-short' | 'arrow-up' | 'arrows-angle-extend' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar-chart-line' | 'bar-pencil' | 'battery-charging' | 'battery' | 'bell-fill' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book-doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business-time' | 'calendar-alt' | 'calendar-check' | 'calendar-day' | 'calendar' | 'call' | 'camera-alt' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-checklist' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-left-fill' | 'caret-right-fill' | 'caret-up-fill' | 'chat-dots' | 'chat-left-text' | 'chat-right-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-double-left' | 'chevron-double-right' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clipboard-list-check' | 'clock-history' | 'clock' | 'close' | 'cloud-fog2-fill' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'columns-gap-filled' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'credit-card-front' | 'cross' | 'cubes' | 'cursor-trash' | 'dash' | 'dashboard-icn' | 'db-icons/icn-card-clickMap' | 'db-icons/icn-card-errors' | 'db-icons/icn-card-funnel' | 'db-icons/icn-card-funnels' | 'db-icons/icn-card-insights' | 'db-icons/icn-card-library' | 'db-icons/icn-card-mapchart' | 'db-icons/icn-card-pathAnalysis' | 'db-icons/icn-card-performance' | 'db-icons/icn-card-resources' | 'db-icons/icn-card-table' | 'db-icons/icn-card-timeseries' | 'db-icons/icn-card-webVitals' | 'desktop' | 'device' | 'diagram-3' | 'dice-3' | 'dizzy' | 'door-closed' | 'doublecheck' | 'download' | 'drag' | 'edit' | 'ellipsis-v' | 'emoji-dizzy' | 'enter' | 'envelope-check' | 'envelope-paper' | 'envelope-x' | 'envelope' | 'errors-icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i-cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation-circle-fill' | 'exclamation-circle' | 'exclamation-triangle' | 'expand-wide' | 'explosion' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | 'fetch' | 'fflag-multi' | 'fflag-single' | 'file-bar-graph' | 'file-code' | 'file-medical-alt' | 'file-pdf' | 'file' | 'files' | 'filetype-js' | 'filetype-pdf' | 'filter' | 'filters/arrow-return-right' | 'filters/browser' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu-load' | 'filters/custom' | 'filters/device' | 'filters/dom-complete' | 'filters/duration' | 'filters/error' | 'filters/fetch-failed' | 'filters/fetch' | 'filters/file-code' | 'filters/graphql' | 'filters/i-cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory-load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance-network-request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev-id' | 'filters/state-action' | 'filters/tag-element' | 'filters/ttfb' | 'filters/user-alt' | 'filters/userid' | 'filters/view' | 'flag-na' | 'folder-plus' | 'folder2' | 'fullscreen' | 'funnel/cpu-fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji-angry-fill' | 'funnel/emoji-angry' | 'funnel/emoji-dizzy-fill' | 'funnel/exclamation-circle-fill' | 'funnel/exclamation-circle' | 'funnel/file-earmark-break-fill' | 'funnel/file-earmark-break' | 'funnel/file-earmark-minus-fill' | 'funnel/file-earmark-minus' | 'funnel/file-medical-alt' | 'funnel/file-x' | 'funnel/hdd-fill' | 'funnel/hourglass-top' | 'funnel/image-fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch-exclamation-fill' | 'funnel/sd-card' | 'funnel-fill' | 'funnel-new' | 'funnel' | 'gear-fill' | 'gear' | 'geo-alt-fill-custom' | 'github' | 'graph-up-arrow' | 'graph-up' | 'grid-1x2' | 'grid-3x3' | 'grid-check' | 'grid-horizontal' | 'grid' | 'grip-horizontal' | 'hash' | 'hdd-stack' | 'headset' | 'heart-rate' | 'high-engagement' | 'history' | 'hourglass-start' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | 'id-card' | 'image' | 'info-circle-fill' | 'info-circle' | 'info-square' | 'info' | 'input-hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag-text' | 'integrations/bugsnag' | 'integrations/cloudwatch-text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/elasticsearch-text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira-text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic-text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay-text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar-text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry-text' | 'integrations/sentry' | 'integrations/slack-bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic-text' | 'integrations/sumologic' | 'integrations/teams-white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal-code' | 'key' | 'layer-group' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-arrow' | 'list-ul' | 'list' | 'lock-alt' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause-circle-fill' | 'pause-fill' | 'pause' | 'pdf-download' | 'pencil-stop' | 'pencil' | 'people' | 'percent' | 'performance-icon' | 'person-border' | 'person-fill' | 'person' | 'pie-chart-fill' | 'pin-fill' | 'play-circle-bold' | 'play-circle-light' | 'play-circle' | 'play-fill-new' | 'play-fill' | 'play-hover' | 'play' | 'plug' | 'plus-circle' | 'plus-lg' | 'plus' | 'pointer-sessions-search' | 'prev1' | 'pulse' | 'puzzle-piece' | 'puzzle' | 'question-circle' | 'question-lg' | 'quote-left' | 'quote-right' | 'quotes' | 'record-btn' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo-back' | 'redo' | 'redux' | 'remote-control' | 'replay-10' | 'resources-icon' | 'safe-fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'skip-forward-fill' | 'skip-forward' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'speedometer2' | 'spinner' | 'star-solid' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table-new' | 'table' | 'tablet-android' | 'tachometer-slow' | 'tachometer-slowest' | 'tags' | 'team-funnel' | 'telephone-fill' | 'telephone' | 'terminal' | 'text-paragraph' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-switch' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web-vitals' | 'wifi' | 'window-alt' | 'window-restore' | 'window-x' | 'window' | 'zoom-in'; +export type IconNames = 'activity' | 'alarm-clock' | 'alarm-plus' | 'all-sessions' | 'analytics' | 'anchor' | 'arrow-alt-square-right' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-square-left' | 'arrow-square-right' | 'arrow-up-short' | 'arrow-up' | 'arrows-angle-extend' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar-chart-line' | 'bar-pencil' | 'battery-charging' | 'battery' | 'bell-fill' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book-doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business-time' | 'calendar-alt' | 'calendar-check' | 'calendar-day' | 'calendar' | 'call' | 'camera-alt' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-checklist' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-left-fill' | 'caret-right-fill' | 'caret-up-fill' | 'chat-dots' | 'chat-left-text' | 'chat-right-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-double-left' | 'chevron-double-right' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clipboard-list-check' | 'clock-history' | 'clock' | 'close' | 'cloud-fog2-fill' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'columns-gap-filled' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'credit-card-front' | 'cross' | 'cubes' | 'cursor-trash' | 'dash' | 'dashboard-icn' | 'db-icons/icn-card-clickMap' | 'db-icons/icn-card-errors' | 'db-icons/icn-card-funnel' | 'db-icons/icn-card-funnels' | 'db-icons/icn-card-insights' | 'db-icons/icn-card-library' | 'db-icons/icn-card-mapchart' | 'db-icons/icn-card-pathAnalysis' | 'db-icons/icn-card-performance' | 'db-icons/icn-card-resources' | 'db-icons/icn-card-table' | 'db-icons/icn-card-timeseries' | 'db-icons/icn-card-webVitals' | 'desktop' | 'device' | 'diagram-3' | 'dice-3' | 'dizzy' | 'door-closed' | 'doublecheck' | 'download' | 'drag' | 'edit' | 'ellipsis-v' | 'emoji-dizzy' | 'enter' | 'envelope-check' | 'envelope-paper' | 'envelope-x' | 'envelope' | 'errors-icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i-cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation-circle-fill' | 'exclamation-circle' | 'exclamation-triangle' | 'expand-wide' | 'explosion' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | 'fetch' | 'fflag-multi' | 'fflag-single' | 'file-bar-graph' | 'file-code' | 'file-medical-alt' | 'file-pdf' | 'file' | 'files' | 'filetype-js' | 'filetype-pdf' | 'filter' | 'filters/arrow-return-right' | 'filters/browser' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu-load' | 'filters/custom' | 'filters/device' | 'filters/dom-complete' | 'filters/duration' | 'filters/error' | 'filters/fetch-failed' | 'filters/fetch' | 'filters/file-code' | 'filters/graphql' | 'filters/i-cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory-load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance-network-request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev-id' | 'filters/state-action' | 'filters/tag-element' | 'filters/ttfb' | 'filters/user-alt' | 'filters/userid' | 'filters/view' | 'flag-na' | 'folder-plus' | 'folder2' | 'fullscreen' | 'funnel/cpu-fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji-angry-fill' | 'funnel/emoji-angry' | 'funnel/emoji-dizzy-fill' | 'funnel/exclamation-circle-fill' | 'funnel/exclamation-circle' | 'funnel/file-earmark-break-fill' | 'funnel/file-earmark-break' | 'funnel/file-earmark-minus-fill' | 'funnel/file-earmark-minus' | 'funnel/file-medical-alt' | 'funnel/file-x' | 'funnel/hdd-fill' | 'funnel/hourglass-top' | 'funnel/image-fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch-exclamation-fill' | 'funnel/sd-card' | 'funnel-fill' | 'funnel-new' | 'funnel' | 'gear-fill' | 'gear' | 'geo-alt-fill-custom' | 'github' | 'graph-up-arrow' | 'graph-up' | 'grid-1x2' | 'grid-3x3' | 'grid-check' | 'grid-horizontal' | 'grid' | 'grip-horizontal' | 'hash' | 'hdd-stack' | 'headset' | 'heart-rate' | 'high-engagement' | 'history' | 'hourglass-start' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | 'id-card' | 'image' | 'info-circle-fill' | 'info-circle' | 'info-square' | 'info' | 'input-hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag-text' | 'integrations/bugsnag' | 'integrations/cloudwatch-text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/elasticsearch-text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira-text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic-text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay-text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar-text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry-text' | 'integrations/sentry' | 'integrations/slack-bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic-text' | 'integrations/sumologic' | 'integrations/teams-white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal-code' | 'key' | 'layer-group' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-arrow' | 'list-ul' | 'list' | 'lock-alt' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause-circle-fill' | 'pause-fill' | 'pause' | 'pdf-download' | 'pencil-stop' | 'pencil' | 'people' | 'percent' | 'performance-icon' | 'person-border' | 'person-fill' | 'person' | 'pie-chart-fill' | 'pin-fill' | 'play-circle-bold' | 'play-circle-light' | 'play-circle' | 'play-fill-new' | 'play-fill' | 'play-hover' | 'play' | 'plug' | 'plus-circle' | 'plus-lg' | 'plus' | 'pointer-sessions-search' | 'prev1' | 'pulse' | 'puzzle-piece' | 'puzzle' | 'question-circle' | 'question-lg' | 'quote-left' | 'quote-right' | 'quotes' | 'record-btn' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo-back' | 'redo' | 'redux' | 'remote-control' | 'replay-10' | 'resources-icon' | 'safe-fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'skip-forward-fill' | 'skip-forward' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'sparkles' | 'speedometer2' | 'spinner' | 'star-solid' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table-new' | 'table' | 'tablet-android' | 'tachometer-slow' | 'tachometer-slowest' | 'tags' | 'team-funnel' | 'telephone-fill' | 'telephone' | 'terminal' | 'text-paragraph' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-switch' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web-vitals' | 'wifi' | 'window-alt' | 'window-restore' | 'window-x' | 'window' | 'zoom-in'; interface Props { name: IconNames; @@ -1773,6 +1772,9 @@ const SVG = (props: Props) => { case 'social/trello': return ; + case 'sparkles': return ; + + case 'speedometer2': return ; diff --git a/frontend/app/mstore/aiSummaryStore.ts b/frontend/app/mstore/aiSummaryStore.ts new file mode 100644 index 000000000..b72d4bf66 --- /dev/null +++ b/frontend/app/mstore/aiSummaryStore.ts @@ -0,0 +1,59 @@ +import { aiService } from 'App/services'; +import { makeAutoObservable } from 'mobx'; + +export default class AiSummaryStore { + text = ''; + + constructor() { + makeAutoObservable(this); + } + + setText(text: string) { + this.text = text; + } + + appendText(text: string) { + this.text += text; + } + + getSummary = async (sessionId: string) => { + this.setText(''); + const respBody = await aiService.getSummary(sessionId); + if (!respBody) return; + + const reader = respBody.getReader(); + + let lastIncompleteWord = ''; + + const processTextChunk = (textChunk: string) => { + textChunk = lastIncompleteWord + textChunk; + const words = textChunk.split(' '); + + lastIncompleteWord = words.pop() || ''; + + words.forEach((word) => { + if(word) this.appendText(word + ' '); + }); + }; + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) { + // Processing any remaining incomplete word at the end of the stream + if (lastIncompleteWord) { + this.appendText(lastIncompleteWord + ' '); + } + break; + } + let textChunk = new TextDecoder().decode(value, { stream: true }); + if (this.text === '') { + textChunk = textChunk.trimStart() + } + processTextChunk(textChunk); + } + } catch (error) { + console.log(error); + } + }; +} diff --git a/frontend/app/mstore/index.tsx b/frontend/app/mstore/index.tsx index 2266f9b91..30ac1f930 100644 --- a/frontend/app/mstore/index.tsx +++ b/frontend/app/mstore/index.tsx @@ -19,8 +19,8 @@ import WeeklyReportStore from './weeklyReportConfigStore'; import AlertStore from './alertsStore'; import FeatureFlagsStore from './featureFlagsStore'; import UxtestingStore from './uxtestingStore'; -import TagWatchStore from './tagWatchStore'; - +import TagWatchStore from './tagWatchStore'; +import AiSummaryStore from "./aiSummaryStore"; export class RootStore { dashboardStore: DashboardStore; metricStore: MetricStore; @@ -41,6 +41,7 @@ export class RootStore { featureFlagsStore: FeatureFlagsStore; uxtestingStore: UxtestingStore; tagWatchStore: TagWatchStore; + aiSummaryStore: AiSummaryStore; constructor() { this.dashboardStore = new DashboardStore(); @@ -62,6 +63,7 @@ export class RootStore { this.featureFlagsStore = new FeatureFlagsStore(); this.uxtestingStore = new UxtestingStore(); this.tagWatchStore = new TagWatchStore(); + this.aiSummaryStore = new AiSummaryStore(); } initClient() { diff --git a/frontend/app/services/AiService.ts b/frontend/app/services/AiService.ts new file mode 100644 index 000000000..b1ed55a1b --- /dev/null +++ b/frontend/app/services/AiService.ts @@ -0,0 +1,13 @@ +import BaseService from 'App/services/BaseService'; + +export default class AiService extends BaseService { + /** + * @returns stream of text symbols + * */ + async getSummary(sessionId: string) { + const r = await this.client.post( + `/sessions/${sessionId}/intelligent/summary`, + ); + return r.body; + } +} diff --git a/frontend/app/services/index.ts b/frontend/app/services/index.ts index 100c81b7b..af022037a 100644 --- a/frontend/app/services/index.ts +++ b/frontend/app/services/index.ts @@ -15,6 +15,7 @@ import FFlagsService from 'App/services/FFlagsService'; import AssistStatsService from './AssistStatsService'; import UxtestingService from './UxtestingService'; import TagWatchService from 'App/services/TagWatchService'; +import AiService from "App/services/AiService"; export const dashboardService = new DashboardService(); export const metricService = new MetricService(); @@ -39,6 +40,8 @@ export const uxtestingService = new UxtestingService(); export const tagWatchService = new TagWatchService(); +export const aiService = new AiService(); + export const services = [ dashboardService, metricService, @@ -57,4 +60,5 @@ export const services = [ assistStatsService, uxtestingService, tagWatchService, + aiService, ]; diff --git a/frontend/app/svg/icons/sparkles.svg b/frontend/app/svg/icons/sparkles.svg new file mode 100644 index 000000000..bfec01a5a --- /dev/null +++ b/frontend/app/svg/icons/sparkles.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/globals.d.ts b/frontend/globals.d.ts new file mode 100644 index 000000000..1c0895573 --- /dev/null +++ b/frontend/globals.d.ts @@ -0,0 +1,11 @@ +interface Window { + env: { + ORIGIN: string; + API_EDP: string; + ASSETS_HOST: string; + VERSION: string; + TRACKER_VERSION: string; + TRACKER_HOST: string; + COMMIT_HASH: string; + } + } diff --git a/frontend/scripts/icons.js b/frontend/scripts/icons.js index 0a91e9eaa..cd1d10480 100644 --- a/frontend/scripts/icons.js +++ b/frontend/scripts/icons.js @@ -115,9 +115,7 @@ import { ${iconPaths.map(icon => ` ${titleCase(icon.fileName)}`).join(',\n')} } from './Icons' - -// export type NewIconNames = ${icons.map((icon) => '\'' + icon.slice(0, -4).replaceAll('-', '_') + '\'').join(' | ')}; -export type IconNames = ${icons.map((icon) => '\'' + icon.slice(0, -4) + '\'').join(' | ')}; +export type IconNames = ${icons.map((icon, i) => `'${icon.slice(0, -4)}'`).join(' | ')}; interface Props { name: IconNames;