diff --git a/__visual_tests__/__snapshots__/workflow-tests.spec.ts/104-chromium-darwin.png b/__visual_tests__/__snapshots__/workflow-tests.spec.ts/104-chromium-darwin.png new file mode 100644 index 000000000..d91956789 Binary files /dev/null and b/__visual_tests__/__snapshots__/workflow-tests.spec.ts/104-chromium-darwin.png differ diff --git a/__visual_tests__/__snapshots__/workflow-tests.spec.ts/104-firefox-darwin.png b/__visual_tests__/__snapshots__/workflow-tests.spec.ts/104-firefox-darwin.png new file mode 100644 index 000000000..58a4c9785 Binary files /dev/null and b/__visual_tests__/__snapshots__/workflow-tests.spec.ts/104-firefox-darwin.png differ diff --git a/__visual_tests__/const.ts b/__visual_tests__/const.ts index b8cbde4f5..5ebc1074e 100644 --- a/__visual_tests__/const.ts +++ b/__visual_tests__/const.ts @@ -15,4 +15,5 @@ export const WidgetComponentIds = { INPUT: '.sendbird-input__input', CHIPS_CONTAINER: '.sendbird-form-chip__container', FORM: '#aichatbot-widget-form', + MARKDOWN: '.widget-markdown', }; diff --git a/__visual_tests__/workflow-tests.spec.ts b/__visual_tests__/workflow-tests.spec.ts index 49778c090..e2926b483 100644 --- a/__visual_tests__/workflow-tests.spec.ts +++ b/__visual_tests__/workflow-tests.spec.ts @@ -137,3 +137,20 @@ test('103', async ({ page, browserName }) => { await page.waitForTimeout(2000); await assertScreenshot(page, '103-6', browserName); }); + +/** + * 104 + * Workflow - Markdown response + * Steps: + * 1. Send the trigger message: "give me a markdown message" + */ +test('104', async ({ page, browserName }) => { + await loadWidget(page); + // 1 + await sendTextMessage(page, 'give me a markdown message', 2000); + + // Check if the fallback component is visible + const fallback = page.locator(WidgetComponentIds.MARKDOWN); + await fallback.waitFor({ state: 'visible' }); + await assertScreenshot(page, '104', browserName); +}); diff --git a/src/components/TokensBody.tsx b/src/components/TokensBody.tsx index 059d68e37..fe5c63aa8 100644 --- a/src/components/TokensBody.tsx +++ b/src/components/TokensBody.tsx @@ -1,5 +1,5 @@ import DOMPurify from 'dompurify'; -import Markdown from 'markdown-to-jsx'; +import { lazy, Suspense } from 'react'; import styled from 'styled-components'; import BotMessageBottom from './BotMessageBottom'; @@ -10,6 +10,8 @@ import { Token, TokenType } from '../utils'; import './markdown.scss'; +const Markdown = lazy(() => import('markdown-to-jsx')); + type TokensBodyProps = { tokens: Token[]; sources?: Source[]; @@ -44,66 +46,74 @@ export default function TokensBody({ tokens, sources }: TokensBodyProps) { const { enableSourceMessage } = useConstantState(); return ( - - {tokens.map((token: Token, i) => { - // Normal text part of the message. - if (token.type === TokenType.string) { - return ( -
- { - return DOMPurify.sanitize(value); - }, - overrides: { - // Note that this is to remove text-align: right by the library. - td: { - component: ({ children, ...props }) => ( - - {children} - - ), + + Loading markdown renderer... + + } + > + + {tokens.map((token: Token, i) => { + // Normal text part of the message. + if (token.type === TokenType.string) { + return ( +
+ { + return DOMPurify.sanitize(value); }, - // Note that this is to remove text-align: right by the library. - th: { - component: ({ children, ...props }) => ( - - {children} - - ), + overrides: { + // Note that this is to remove text-align: right by the library. + td: { + component: ({ children, ...props }) => ( + + {children} + + ), + }, + // Note that this is to remove text-align: right by the library. + th: { + component: ({ children, ...props }) => ( + + {children} + + ), + }, + a: { + component: ({ children, ...props }) => ( + + {children} + + ), + }, }, - a: { - component: ({ children, ...props }) => ( - - {children} - - ), - }, - }, - }} - > - {token.value} - -
+ }} + > + {token.value} +
+
+ ); + } + // Code part of the message. + return ( + + + ); - } - // Code part of the message. - return ( - - - - ); - })} - {sources && sources.length > 0 && enableSourceMessage ? ( -
- - -
- ) : null} -
+ })} + {sources && sources.length > 0 && enableSourceMessage ? ( +
+ + +
+ ) : null} + + ); } diff --git a/src/components/markdown.scss b/src/components/markdown.scss index dffbe1b9f..d7a583689 100644 --- a/src/components/markdown.scss +++ b/src/components/markdown.scss @@ -2,8 +2,12 @@ padding: 0 12px; /* apply side padding of the bubble */ } +.widget-markdown * { + color: var(--sb-on-content-inverse-1); +} + .widget-markdown hr { - border-top-width: 1px; + border-top: 1px solid var(--sb-on-bg-4); margin-bottom: 21px; /* 1.5em (3em in ChatGPT) */ margin-top: 21px; /* 1.5em (3em in ChatGPT) */ } @@ -13,7 +17,7 @@ margin-bottom: 12.4px; /* 0.8888889em */ padding: 0; font-size: 22.4px; /* 1.6em (2.25em in ChatGPT, reduced for visual appeal) */ - line-height: 15.6px; /* 1.1em */ + line-height: 27.6px; font-weight: 700; letter-spacing: -0.6px; /* -0.04rem */ } @@ -23,10 +27,13 @@ margin-bottom: 12px; /* 0.75rem (1em in ChatGPT) */ margin-top: 24px; /* 1.5rem (2em in ChatGPT) */ font-size: 18.2px; /* 1.3em (1.5em in ChatGPT) */ - line-height: 18.7px; /* 1.3333333em */ + line-height: 22.3px; /* 1.3333333em */ } -.widget-markdown h3, h4, h5, h6 { +.widget-markdown h3, +h4, +h5, +h6 { font-weight: 600; margin-bottom: 8px; /* 0.5rem */ margin-top: 16px; /* 1rem */ @@ -173,7 +180,8 @@ widget-markdown.widget-markdown is to increase specificity. Refer to: https://ww margin-top: 4px; /* 0.25rem */ *, :after, - :before { /* Need this for table borders to be displayed */ + :before { + /* Need this for table borders to be displayed */ border: 0 solid; box-sizing: border-box; } @@ -271,5 +279,3 @@ widget-markdown.widget-markdown is to increase specificity. Refer to: https://ww color: var(--sb-on-bg-1); font-weight: 700; } - -