diff --git a/dev/messages-ai-chat.html b/dev/messages-ai-chat.html new file mode 100644 index 00000000000..a8e92a54b29 --- /dev/null +++ b/dev/messages-ai-chat.html @@ -0,0 +1,162 @@ + + + + + + + Messages AI Chat + + + + + + + + +
+ + + + +
+ + diff --git a/packages/message-list/package.json b/packages/message-list/package.json index cb180cf64c1..34ecf01b550 100644 --- a/packages/message-list/package.json +++ b/packages/message-list/package.json @@ -42,6 +42,7 @@ "@vaadin/a11y-base": "24.8.0-alpha15", "@vaadin/avatar": "24.8.0-alpha15", "@vaadin/component-base": "24.8.0-alpha15", + "@vaadin/markdown": "24.8.0-alpha15", "@vaadin/vaadin-lumo-styles": "24.8.0-alpha15", "@vaadin/vaadin-material-styles": "24.8.0-alpha15", "@vaadin/vaadin-themable-mixin": "24.8.0-alpha15", diff --git a/packages/message-list/src/vaadin-message-list-mixin.d.ts b/packages/message-list/src/vaadin-message-list-mixin.d.ts index 7c95dd0396a..03b8ea86e36 100644 --- a/packages/message-list/src/vaadin-message-list-mixin.d.ts +++ b/packages/message-list/src/vaadin-message-list-mixin.d.ts @@ -39,4 +39,9 @@ export declare class MessageListMixinClass { * ``` */ items: MessageListItem[] | null | undefined; + + /** + * When set to `true`, the message text is parsed as Markdown. + */ + markdown: boolean | undefined; } diff --git a/packages/message-list/src/vaadin-message-list-mixin.js b/packages/message-list/src/vaadin-message-list-mixin.js index c4d075ac97f..c24061ee2ef 100644 --- a/packages/message-list/src/vaadin-message-list-mixin.js +++ b/packages/message-list/src/vaadin-message-list-mixin.js @@ -3,6 +3,7 @@ * Copyright (c) 2021 - 2025 Vaadin Ltd. * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ +import '@vaadin/markdown/src/vaadin-markdown.js'; import { html, render } from 'lit'; import { ifDefined } from 'lit/directives/if-defined.js'; import { KeyboardDirectionMixin } from '@vaadin/a11y-base/src/keyboard-direction-mixin.js'; @@ -37,6 +38,16 @@ export const MessageListMixin = (superClass) => observer: '_itemsChanged', sync: true, }, + + /** + * When set to `true`, the message text is parsed as Markdown. + * @type {boolean} + */ + markdown: { + type: Boolean, + observer: '__markdownChanged', + reflectToAttribute: true, + }, }; } @@ -86,6 +97,11 @@ export const MessageListMixin = (superClass) => } } + /** @private */ + __markdownChanged(_markdown) { + this._renderMessages(this.items); + } + /** @private */ _renderMessages(items) { render( @@ -102,7 +118,9 @@ export const MessageListMixin = (superClass) => theme="${ifDefined(item.theme)}" class="${ifDefined(item.className)}" @focusin="${this._onMessageFocusIn}" - >${item.text}${this.markdown + ? html`` + : item.text} `, )} diff --git a/packages/message-list/src/vaadin-message-styles.js b/packages/message-list/src/vaadin-message-styles.js index fee40b88bae..36be2029d4c 100644 --- a/packages/message-list/src/vaadin-message-styles.js +++ b/packages/message-list/src/vaadin-message-styles.js @@ -40,4 +40,8 @@ export const messageStyles = css` --vaadin-avatar-outline-width: 0; flex-shrink: 0; } + + ::slotted(vaadin-markdown) { + white-space: normal; + } `; diff --git a/packages/message-list/test/message-list-markdown.test.js b/packages/message-list/test/message-list-markdown.test.js new file mode 100644 index 00000000000..b1587541c0c --- /dev/null +++ b/packages/message-list/test/message-list-markdown.test.js @@ -0,0 +1,53 @@ +import { expect } from '@vaadin/chai-plugins'; +import { fixtureSync, nextUpdate } from '@vaadin/testing-helpers'; +import '../src/vaadin-message-list.js'; + +describe('message-list-markdown', () => { + let messageList; + const messages = [ + { + text: 'This is a **bold text** in Markdown', + time: '10:00 AM', + userName: 'Markdown User', + userAbbr: 'MU', + }, + ]; + + beforeEach(async () => { + messageList = fixtureSync(''); + messageList.items = messages; + await nextUpdate(messageList); + }); + + it('should render the message items as markdown', () => { + const strongElement = messageList.querySelector('vaadin-message strong'); + expect(strongElement).to.exist; + expect(strongElement.textContent).to.equal('bold text'); + }); + + it('should toggle markdown rendering when property changes', async () => { + // First check with markdown enabled + expect(messageList.querySelector('vaadin-message strong')).to.exist; + + // Disable markdown + messageList.markdown = false; + await nextUpdate(messageList); + + // Verify markdown is disabled on messages + expect(messageList.querySelector('vaadin-message strong')).to.not.exist; + + // Re-enable markdown + messageList.markdown = true; + await nextUpdate(messageList); + + // Verify markdown is re-enabled + expect(messageList.querySelector('vaadin-message strong')).to.exist; + }); + + it('should toggle markdown attribute', async () => { + expect(messageList.hasAttribute('markdown')).to.be.true; + messageList.markdown = false; + await nextUpdate(messageList); + expect(messageList.hasAttribute('markdown')).to.be.false; + }); +}); diff --git a/packages/message-list/test/typings/message-list.types.ts b/packages/message-list/test/typings/message-list.types.ts index d5338ef1aca..9f329923fe0 100644 --- a/packages/message-list/test/typings/message-list.types.ts +++ b/packages/message-list/test/typings/message-list.types.ts @@ -12,6 +12,7 @@ const list = document.createElement('vaadin-message-list'); // Properties assertType(list.items); +assertType(list.markdown); // Item properties const item: MessageListItem = list.items ? list.items[0] : {}; diff --git a/packages/message-list/test/visual/lumo/message-list.test.js b/packages/message-list/test/visual/lumo/message-list.test.js index a7f5fa350aa..46b5fa605a5 100644 --- a/packages/message-list/test/visual/lumo/message-list.test.js +++ b/packages/message-list/test/visual/lumo/message-list.test.js @@ -59,6 +59,13 @@ describe('message-list', () => { await sendKeys({ press: 'ArrowDown' }); await visualDiff(div, `${dir}-focused`); }); + + it('markdown', async () => { + element.items[0].text = 'This is a **bold text** in Markdown'; + element.items = [...element.items]; + element.markdown = true; + await visualDiff(div, `${dir}-markdown`); + }); }); }); }); diff --git a/packages/message-list/test/visual/lumo/screenshots/message-list/baseline/ltr-markdown.png b/packages/message-list/test/visual/lumo/screenshots/message-list/baseline/ltr-markdown.png new file mode 100644 index 00000000000..2ca61ff1fb5 Binary files /dev/null and b/packages/message-list/test/visual/lumo/screenshots/message-list/baseline/ltr-markdown.png differ diff --git a/packages/message-list/test/visual/lumo/screenshots/message-list/baseline/rtl-markdown.png b/packages/message-list/test/visual/lumo/screenshots/message-list/baseline/rtl-markdown.png new file mode 100644 index 00000000000..6dff6396446 Binary files /dev/null and b/packages/message-list/test/visual/lumo/screenshots/message-list/baseline/rtl-markdown.png differ diff --git a/packages/message-list/test/visual/material/message-list.test.js b/packages/message-list/test/visual/material/message-list.test.js index c6ab3201d9b..5789b5ad2e6 100644 --- a/packages/message-list/test/visual/material/message-list.test.js +++ b/packages/message-list/test/visual/material/message-list.test.js @@ -59,6 +59,13 @@ describe('message-list', () => { await sendKeys({ press: 'ArrowDown' }); await visualDiff(div, `${dir}-focused`); }); + + it('markdown', async () => { + element.items[0].text = 'This is a **bold text** in Markdown'; + element.items = [...element.items]; + element.markdown = true; + await visualDiff(div, `${dir}-markdown`); + }); }); }); }); diff --git a/packages/message-list/test/visual/material/screenshots/message-list/baseline/ltr-markdown.png b/packages/message-list/test/visual/material/screenshots/message-list/baseline/ltr-markdown.png new file mode 100644 index 00000000000..f191b613823 Binary files /dev/null and b/packages/message-list/test/visual/material/screenshots/message-list/baseline/ltr-markdown.png differ diff --git a/packages/message-list/test/visual/material/screenshots/message-list/baseline/rtl-markdown.png b/packages/message-list/test/visual/material/screenshots/message-list/baseline/rtl-markdown.png new file mode 100644 index 00000000000..5442e638031 Binary files /dev/null and b/packages/message-list/test/visual/material/screenshots/message-list/baseline/rtl-markdown.png differ diff --git a/packages/message-list/theme/lumo/vaadin-message-list.js b/packages/message-list/theme/lumo/vaadin-message-list.js index 8b8d5115866..42b10a9050c 100644 --- a/packages/message-list/theme/lumo/vaadin-message-list.js +++ b/packages/message-list/theme/lumo/vaadin-message-list.js @@ -1,2 +1,3 @@ +import '@vaadin/markdown/theme/lumo/vaadin-markdown.js'; import './vaadin-message-list-styles.js'; import '../../src/vaadin-message-list.js'; diff --git a/packages/message-list/theme/material/vaadin-message-list.js b/packages/message-list/theme/material/vaadin-message-list.js index 8b8d5115866..d2f65635b0a 100644 --- a/packages/message-list/theme/material/vaadin-message-list.js +++ b/packages/message-list/theme/material/vaadin-message-list.js @@ -1,2 +1,3 @@ +import '@vaadin/markdown/theme/material/vaadin-markdown.js'; import './vaadin-message-list-styles.js'; import '../../src/vaadin-message-list.js';