diff --git a/CHANGELOG.md b/CHANGELOG.md index 25adec2d30..3e4fa57799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,7 +121,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/ - `@msinternal/botframework-webchat-react-hooks` for helpers for React hooks - Added link sanitization and ESLint rules, in PR [#5564](https://github.com/microsoft/BotFramework-WebChat/pull/5564), by [@compulim](https://github.com/compulim) - Added blob URL sanitization and ESLint rules, in PR [#5568](https://github.com/microsoft/BotFramework-WebChat/pull/5568), by [@compulim](https://github.com/compulim) -- Added visual message grouping following the `isPartOf` property of the `Message` entity, in PR [#5553](https://github.com/microsoft/BotFramework-WebChat/pull/5553), in PR [#5585](https://github.com/microsoft/BotFramework-WebChat/pull/5585), by [@OEvgeny](https://github.com/OEvgeny) +- Added visual message grouping following the `isPartOf` property of the `Message` entity, in PR [#5553](https://github.com/microsoft/BotFramework-WebChat/pull/5553), in PR [#5585](https://github.com/microsoft/BotFramework-WebChat/pull/5585), in PR [#5590](https://github.com/microsoft/BotFramework-WebChat/pull/5590), by [@OEvgeny](https://github.com/OEvgeny) - The mode is suitable for providing chain-of-thought reasoning - Added visual indication of `creativeWorkStatus` property in `Message` entity: - `undefined` - no indicator is shown diff --git a/__tests__/html/assets/custom-element/custom-element.js b/__tests__/html/assets/custom-element/custom-element.js new file mode 100644 index 0000000000..f3ffb99f14 --- /dev/null +++ b/__tests__/html/assets/custom-element/custom-element.js @@ -0,0 +1,127 @@ +/* eslint-env browser */ + +// #region TODO: Remove me after we bump Chrome to v117+ +const customElementNames = customElements.getName instanceof Function ? null : new WeakMap(); + +export function getCustomElementName(customElementConstructor) { + if (customElementNames) { + return customElementNames.get(customElementConstructor); + } + return customElements.getName(customElementConstructor); +} + +function setCustomElementName(customElementConstructor, name) { + if (customElementNames) { + customElementNames.set(customElementConstructor, name); + } + // No need to set for browsers that support customElements.getName() +} +// #endregion + +export function customElement(elementKey, createElementClass) { + const elementRegistration = document.querySelector(`element-registration[element-key="${elementKey}"]`); + elementRegistration.elementConstructor = createElementClass(elementRegistration); +} + +function addSourceMapToExtractedScript(scriptContent, originalFileUrl) { + const sourceMap = { + version: 3, + sources: [originalFileUrl], + names: [], + mappings: 'AAAA', // Simple mapping - entire script maps to original file + file: originalFileUrl.split('/').pop(), + sourceRoot: '', + sourcesContent: [scriptContent] + }; + + const base64Map = btoa(JSON.stringify(sourceMap)); + const dataUrl = `data:application/json;charset=utf-8;base64,${base64Map}`; + + // TODO: Figure out how to make setting breakpoints work + return scriptContent + `\n//# sourceMappingURL=${dataUrl}`; +} + +function fixScript(script, url) { + const newScript = document.createElement('script'); + + Array.from(script.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value)); + newScript.text = addSourceMapToExtractedScript(script.text, url); + + return newScript; +} + +function initDocument(elementRegistration, currentDocument) { + const moduleUrl = new URL(`./${elementRegistration.getAttribute('element-key')}.ce.js`, import.meta.url).toString(); + const allowedElementNames = ['link', 'style', 'script']; + + if (!currentDocument) { + throw new Error('Custom element must be registered within a element.'); + } + + const result = Promise.withResolvers(); + + Object.defineProperty(elementRegistration, 'elementConstructor', { + set(constructor) { + if (!constructor) { + throw new Error('Custom element constructor is required.'); + } + + const elementName = elementRegistration.getAttribute('element-name'); + + if (!elementName) { + throw new Error('Custom element must have a name.'); + } + + customElements.define(elementName, constructor, constructor.options); + setCustomElementName(constructor, elementName); + + result.resolve(constructor); + }, + get() { + return customElement.get(elementRegistration.getAttribute('element-name')); + } + }); + + document.head.append( + ...Array.from(currentDocument.head.children) + .filter(element => allowedElementNames.includes(element.localName)) + .map(element => (element.localName === 'script' ? fixScript(element, moduleUrl) : element)) + ); + + elementRegistration.append( + ...Array.from(currentDocument.body.children).map(element => + element.localName === 'script' ? fixScript(element, moduleUrl) : element + ) + ); + document.body.appendChild(elementRegistration); + + return result.promise; +} + +export function registerElements(...elementNames) { + const parser = new DOMParser(); + const entries = elementNames.map(entry => (typeof entry === 'string' ? [entry, entry] : Object.entries(entry).at(0))); + + const raceInit = (key, initPromise) => + Promise.race([ + new Promise((_resolve, reject) => { + setTimeout( + () => reject(new Error(`Could not initialize custom element "${key}". Did you call customElement()?`)), + 5000 + ); + }), + initPromise + ]); + + return Promise.all( + entries.map(async ([key, elementName]) => { + const content = await fetch(new URL(`./${key}.ce`, import.meta.url)).then(response => response.text()); + + const elementRegistration = document.createElement('element-registration'); + elementRegistration.setAttribute('element-key', key); + elementRegistration.setAttribute('element-name', elementName); + + return raceInit(key, initDocument(elementRegistration, parser.parseFromString(content, 'text/html'))); + }) + ); +} diff --git a/__tests__/html/assets/custom-element/event-stream.ce.html b/__tests__/html/assets/custom-element/event-stream.ce.html new file mode 100644 index 0000000000..836df83944 --- /dev/null +++ b/__tests__/html/assets/custom-element/event-stream.ce.html @@ -0,0 +1,72 @@ + + + + + + Event Stream Custom Element + + + + + + \ No newline at end of file diff --git a/__tests__/html/assets/custom-element/keyboard-event.ce.html b/__tests__/html/assets/custom-element/keyboard-event.ce.html new file mode 100644 index 0000000000..8396000302 --- /dev/null +++ b/__tests__/html/assets/custom-element/keyboard-event.ce.html @@ -0,0 +1,152 @@ + + + + + Keyboard Event Custom Element + + + + + + \ No newline at end of file diff --git a/__tests__/html2/activity/message-status.copilot.html b/__tests__/html2/activity/message-status.copilot.html new file mode 100644 index 0000000000..dbb6b17fa7 --- /dev/null +++ b/__tests__/html2/activity/message-status.copilot.html @@ -0,0 +1,10 @@ + + + + Message status (copilot) + + + + diff --git a/__tests__/html2/activity/message-status.copilot.html.snap-1.png b/__tests__/html2/activity/message-status.copilot.html.snap-1.png new file mode 100644 index 0000000000..bd90d72bc8 Binary files /dev/null and b/__tests__/html2/activity/message-status.copilot.html.snap-1.png differ diff --git a/__tests__/html2/activity/message-status.copilot.html.snap-2.png b/__tests__/html2/activity/message-status.copilot.html.snap-2.png new file mode 100644 index 0000000000..71482d295f Binary files /dev/null and b/__tests__/html2/activity/message-status.copilot.html.snap-2.png differ diff --git a/__tests__/html2/activity/message-status.copilot.html.snap-3.png b/__tests__/html2/activity/message-status.copilot.html.snap-3.png new file mode 100644 index 0000000000..26fd25c9ec Binary files /dev/null and b/__tests__/html2/activity/message-status.copilot.html.snap-3.png differ diff --git a/__tests__/html2/activity/message-status.copilot.html.snap-4.png b/__tests__/html2/activity/message-status.copilot.html.snap-4.png new file mode 100644 index 0000000000..1b992429eb Binary files /dev/null and b/__tests__/html2/activity/message-status.copilot.html.snap-4.png differ diff --git a/__tests__/html2/activity/message-status.copilot.html.snap-5.png b/__tests__/html2/activity/message-status.copilot.html.snap-5.png new file mode 100644 index 0000000000..7eaf2ac39f Binary files /dev/null and b/__tests__/html2/activity/message-status.copilot.html.snap-5.png differ diff --git a/__tests__/html2/activity/message-status.copilot.html.snap-6.png b/__tests__/html2/activity/message-status.copilot.html.snap-6.png new file mode 100644 index 0000000000..fa76652a3d Binary files /dev/null and b/__tests__/html2/activity/message-status.copilot.html.snap-6.png differ diff --git a/__tests__/html2/activity/message-status.copilot.html.snap-7.png b/__tests__/html2/activity/message-status.copilot.html.snap-7.png new file mode 100644 index 0000000000..060f173e33 Binary files /dev/null and b/__tests__/html2/activity/message-status.copilot.html.snap-7.png differ diff --git a/__tests__/html2/activity/message-status.fluent.html b/__tests__/html2/activity/message-status.fluent.html new file mode 100644 index 0000000000..a53b00db3f --- /dev/null +++ b/__tests__/html2/activity/message-status.fluent.html @@ -0,0 +1,10 @@ + + + + Message status (fluent) + + + + diff --git a/__tests__/html2/activity/message-status.fluent.html.snap-1.png b/__tests__/html2/activity/message-status.fluent.html.snap-1.png new file mode 100644 index 0000000000..1a2f133bec Binary files /dev/null and b/__tests__/html2/activity/message-status.fluent.html.snap-1.png differ diff --git a/__tests__/html2/activity/message-status.fluent.html.snap-2.png b/__tests__/html2/activity/message-status.fluent.html.snap-2.png new file mode 100644 index 0000000000..0048562d03 Binary files /dev/null and b/__tests__/html2/activity/message-status.fluent.html.snap-2.png differ diff --git a/__tests__/html2/activity/message-status.fluent.html.snap-3.png b/__tests__/html2/activity/message-status.fluent.html.snap-3.png new file mode 100644 index 0000000000..80db1cd42d Binary files /dev/null and b/__tests__/html2/activity/message-status.fluent.html.snap-3.png differ diff --git a/__tests__/html2/activity/message-status.fluent.html.snap-4.png b/__tests__/html2/activity/message-status.fluent.html.snap-4.png new file mode 100644 index 0000000000..4aac4563cd Binary files /dev/null and b/__tests__/html2/activity/message-status.fluent.html.snap-4.png differ diff --git a/__tests__/html2/activity/message-status.fluent.html.snap-5.png b/__tests__/html2/activity/message-status.fluent.html.snap-5.png new file mode 100644 index 0000000000..fe9cb42701 Binary files /dev/null and b/__tests__/html2/activity/message-status.fluent.html.snap-5.png differ diff --git a/__tests__/html2/activity/message-status.fluent.html.snap-6.png b/__tests__/html2/activity/message-status.fluent.html.snap-6.png new file mode 100644 index 0000000000..16ae91e6ed Binary files /dev/null and b/__tests__/html2/activity/message-status.fluent.html.snap-6.png differ diff --git a/__tests__/html2/activity/message-status.fluent.html.snap-7.png b/__tests__/html2/activity/message-status.fluent.html.snap-7.png new file mode 100644 index 0000000000..7bb434b0ab Binary files /dev/null and b/__tests__/html2/activity/message-status.fluent.html.snap-7.png differ diff --git a/__tests__/html2/activity/message-status.html b/__tests__/html2/activity/message-status.html new file mode 100644 index 0000000000..e07cb05554 --- /dev/null +++ b/__tests__/html2/activity/message-status.html @@ -0,0 +1,365 @@ + + + + + Message status + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/__tests__/html2/activity/message-status.html.snap-1.png b/__tests__/html2/activity/message-status.html.snap-1.png new file mode 100644 index 0000000000..ab18346df0 Binary files /dev/null and b/__tests__/html2/activity/message-status.html.snap-1.png differ diff --git a/__tests__/html2/activity/message-status.html.snap-2.png b/__tests__/html2/activity/message-status.html.snap-2.png new file mode 100644 index 0000000000..8b347a90c3 Binary files /dev/null and b/__tests__/html2/activity/message-status.html.snap-2.png differ diff --git a/__tests__/html2/activity/message-status.html.snap-3.png b/__tests__/html2/activity/message-status.html.snap-3.png new file mode 100644 index 0000000000..a8e1d00d11 Binary files /dev/null and b/__tests__/html2/activity/message-status.html.snap-3.png differ diff --git a/__tests__/html2/activity/message-status.html.snap-4.png b/__tests__/html2/activity/message-status.html.snap-4.png new file mode 100644 index 0000000000..8164410486 Binary files /dev/null and b/__tests__/html2/activity/message-status.html.snap-4.png differ diff --git a/__tests__/html2/activity/message-status.html.snap-5.png b/__tests__/html2/activity/message-status.html.snap-5.png new file mode 100644 index 0000000000..8ccb674898 Binary files /dev/null and b/__tests__/html2/activity/message-status.html.snap-5.png differ diff --git a/__tests__/html2/activity/message-status.html.snap-6.png b/__tests__/html2/activity/message-status.html.snap-6.png new file mode 100644 index 0000000000..9e7ab7cf91 Binary files /dev/null and b/__tests__/html2/activity/message-status.html.snap-6.png differ diff --git a/__tests__/html2/activity/message-status.html.snap-7.png b/__tests__/html2/activity/message-status.html.snap-7.png new file mode 100644 index 0000000000..9a8d68adca Binary files /dev/null and b/__tests__/html2/activity/message-status.html.snap-7.png differ diff --git a/__tests__/html2/part-grouping/avatar.html.snap-1.png b/__tests__/html2/part-grouping/avatar.html.snap-1.png index 5d199b9290..a044d4647d 100644 Binary files a/__tests__/html2/part-grouping/avatar.html.snap-1.png and b/__tests__/html2/part-grouping/avatar.html.snap-1.png differ diff --git a/__tests__/html2/part-grouping/avatar.html.snap-2.png b/__tests__/html2/part-grouping/avatar.html.snap-2.png index e83cf74dd9..2b03d2024f 100644 Binary files a/__tests__/html2/part-grouping/avatar.html.snap-2.png and b/__tests__/html2/part-grouping/avatar.html.snap-2.png differ diff --git a/__tests__/html2/part-grouping/avatar.html.snap-3.png b/__tests__/html2/part-grouping/avatar.html.snap-3.png index 6c365a7f6d..85f0e13938 100644 Binary files a/__tests__/html2/part-grouping/avatar.html.snap-3.png and b/__tests__/html2/part-grouping/avatar.html.snap-3.png differ diff --git a/__tests__/html2/part-grouping/avatar.html.snap-4.png b/__tests__/html2/part-grouping/avatar.html.snap-4.png index 289ed59a35..3d825104d7 100644 Binary files a/__tests__/html2/part-grouping/avatar.html.snap-4.png and b/__tests__/html2/part-grouping/avatar.html.snap-4.png differ diff --git a/__tests__/html2/part-grouping/avatar.html.snap-5.png b/__tests__/html2/part-grouping/avatar.html.snap-5.png index d9a1e6b3f7..1759973593 100644 Binary files a/__tests__/html2/part-grouping/avatar.html.snap-5.png and b/__tests__/html2/part-grouping/avatar.html.snap-5.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html b/__tests__/html2/part-grouping/keyboard.html new file mode 100644 index 0000000000..07c43c011c --- /dev/null +++ b/__tests__/html2/part-grouping/keyboard.html @@ -0,0 +1,259 @@ + + + + Part grouping: keyboard expand/collapse + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-1.png b/__tests__/html2/part-grouping/keyboard.html.snap-1.png new file mode 100644 index 0000000000..021cb5604f Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-1.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-10.png b/__tests__/html2/part-grouping/keyboard.html.snap-10.png new file mode 100644 index 0000000000..ce60e55dfe Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-10.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-11.png b/__tests__/html2/part-grouping/keyboard.html.snap-11.png new file mode 100644 index 0000000000..064a11d4f2 Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-11.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-12.png b/__tests__/html2/part-grouping/keyboard.html.snap-12.png new file mode 100644 index 0000000000..00178a901b Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-12.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-13.png b/__tests__/html2/part-grouping/keyboard.html.snap-13.png new file mode 100644 index 0000000000..6d3d46f47b Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-13.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-14.png b/__tests__/html2/part-grouping/keyboard.html.snap-14.png new file mode 100644 index 0000000000..ee32c5ef48 Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-14.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-15.png b/__tests__/html2/part-grouping/keyboard.html.snap-15.png new file mode 100644 index 0000000000..8fcd43741b Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-15.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-16.png b/__tests__/html2/part-grouping/keyboard.html.snap-16.png new file mode 100644 index 0000000000..19c59b666d Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-16.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-2.png b/__tests__/html2/part-grouping/keyboard.html.snap-2.png new file mode 100644 index 0000000000..290d1c1a9b Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-2.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-3.png b/__tests__/html2/part-grouping/keyboard.html.snap-3.png new file mode 100644 index 0000000000..9f5f11386b Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-3.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-4.png b/__tests__/html2/part-grouping/keyboard.html.snap-4.png new file mode 100644 index 0000000000..dc426f68f1 Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-4.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-5.png b/__tests__/html2/part-grouping/keyboard.html.snap-5.png new file mode 100644 index 0000000000..da97af1202 Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-5.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-6.png b/__tests__/html2/part-grouping/keyboard.html.snap-6.png new file mode 100644 index 0000000000..239fe40e9d Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-6.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-7.png b/__tests__/html2/part-grouping/keyboard.html.snap-7.png new file mode 100644 index 0000000000..c02ceb9171 Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-7.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-8.png b/__tests__/html2/part-grouping/keyboard.html.snap-8.png new file mode 100644 index 0000000000..b9aaf2416b Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-8.png differ diff --git a/__tests__/html2/part-grouping/keyboard.html.snap-9.png b/__tests__/html2/part-grouping/keyboard.html.snap-9.png new file mode 100644 index 0000000000..d976f6bdf3 Binary files /dev/null and b/__tests__/html2/part-grouping/keyboard.html.snap-9.png differ diff --git a/__tests__/html2/part-grouping/navigation.html b/__tests__/html2/part-grouping/navigation.html index 7a37478b49..b1f094c237 100644 --- a/__tests__/html2/part-grouping/navigation.html +++ b/__tests__/html2/part-grouping/navigation.html @@ -34,74 +34,25 @@ .theme.variant-copilot { --webchat__color--surface: var(--colorGrey98); } - - #key-history { - align-items: center; - display: flex; - gap: 0.5em; - left: 0.5em; - padding: 0.5em; - position: fixed; - top: 0.5em; - z-index: 1; - font-size: 3rem; - } - - #key-history kbd { - background-color: #f7f7f7; - border: 1px solid #ccc; - border-radius: 6px; - box-shadow: 0 1px 0 #aaaaaa44, 0 2px 0 #ffffff44 inset; - color: #222; - font-weight: bold; - padding: 0.2em 0.6em; - text-align: center; - } - #key-history kbd:last-of-type { - background-color: #e4e8ec; - } - +
+