@genexus/mercury 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/assets/MERCURY_ASSETS.d.ts +964 -850
  2. package/dist/assets/MERCURY_ASSETS.js +1 -1
  3. package/dist/assets/MERCURY_ASSETS.ts +6585 -6495
  4. package/dist/assets/icons/internal/dark/assistant-fill.svg +1 -0
  5. package/dist/assets/icons/internal/light/assistant-fill.svg +1 -0
  6. package/dist/assets-manager.js +1 -1
  7. package/dist/bundles/css/all.css +1 -1
  8. package/dist/bundles/css/base/icons.css +1 -1
  9. package/dist/bundles/css/components/accordion.css +1 -1
  10. package/dist/bundles/css/components/button.css +1 -1
  11. package/dist/bundles/css/components/chat.css +1 -1
  12. package/dist/bundles/css/components/checkbox.css +1 -1
  13. package/dist/bundles/css/components/combo-box.css +1 -1
  14. package/dist/bundles/css/components/dialog.css +1 -1
  15. package/dist/bundles/css/components/dropdown.css +1 -1
  16. package/dist/bundles/css/components/edit.css +1 -1
  17. package/dist/bundles/css/components/flexible-layout.css +1 -1
  18. package/dist/bundles/css/components/icon.css +1 -1
  19. package/dist/bundles/css/components/layout-splitter.css +1 -1
  20. package/dist/bundles/css/components/list-box.css +1 -1
  21. package/dist/bundles/css/components/markdown-viewer.css +1 -1
  22. package/dist/bundles/css/components/navigation-list.css +1 -1
  23. package/dist/bundles/css/components/paginator.css +1 -1
  24. package/dist/bundles/css/components/pills.css +1 -1
  25. package/dist/bundles/css/components/segmented-control.css +1 -1
  26. package/dist/bundles/css/components/tab.css +1 -1
  27. package/dist/bundles/css/components/tree-view.css +1 -1
  28. package/dist/bundles/css/utils/form--full.css +1 -1
  29. package/dist/bundles/css/utils/form.css +1 -1
  30. package/dist/bundles/css/utils/layout.css +1 -1
  31. package/dist/components/chat/actions.lit.d.ts +2 -0
  32. package/dist/components/chat/actions.lit.js +18 -0
  33. package/dist/components/chat/code-block.lit.d.ts +2 -0
  34. package/dist/components/chat/code-block.lit.js +9 -0
  35. package/dist/components/chat/file.lit.d.ts +9 -0
  36. package/dist/components/chat/file.lit.js +71 -0
  37. package/dist/components/chat/mer-animated-dots.d.ts +10 -0
  38. package/dist/components/chat/mer-animated-dots.js +60 -0
  39. package/dist/components/chat/mer-spinner.lit.d.ts +10 -0
  40. package/dist/components/chat/mer-spinner.lit.js +10 -0
  41. package/dist/components/chat/render.lit.d.ts +4 -0
  42. package/dist/components/chat/render.lit.js +104 -0
  43. package/dist/components/chat/types.d.ts +4 -0
  44. package/dist/components/chat/types.js +1 -0
  45. package/dist/components/chat/utils.d.ts +23 -0
  46. package/dist/components/chat/utils.js +54 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +1 -1
  49. package/package.json +16 -5
@@ -1 +1 @@
1
- .layout{--spacing-body-block-start: var(--mer-spacing--md);--spacing-body-block-end: var(--mer-spacing--md);--spacing-body-inline-start: var(--mer-spacing--lg);--spacing-body-inline-end: var(--mer-spacing--lg);display:grid;gap:var(--mer-spacing--md)}.layout__panel{display:grid}.layout--cols-1{grid-template-rows:1fr;grid-template-columns:1fr}.layout--cols-2{grid-template-rows:1fr;grid-template-columns:repeat(2, 1fr)}.layout--cols-3{grid-template-rows:1fr;grid-template-columns:repeat(3, 1fr)}.layout--cols-4{grid-template-rows:1fr;grid-template-columns:repeat(4, 1fr)}.layout--cols-1-2{grid-template-rows:1fr;grid-template-columns:1fr 2fr}.layout--cols-2-1{grid-template-rows:1fr;grid-template-columns:2fr 1fr}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.control-footer,.control-footer-with-border{--control-footer-justify-content: end;padding-block-start:var(--mer-spacing--xs);display:flex;justify-content:var(--control-footer-justify-content);align-items:center}.control-footer-start{--control-footer-justify-content: start}.control-footer-center{--control-footer-justify-content: center}.control-footer-space-between{--control-footer-justify-content: space-between}.control-footer-with-border{border-block-start:var(--mer-border__width--sm) solid var(--mer-border-color__on-elevation--01)}.control-header,.control-header-with-border{padding-block-end:var(--mer-spacing--md)}.control-header-with-border{border-block-end:var(--mer-border__width--sm) solid var(--mer-border-color__on-elevation--01)}
1
+ @keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.layout{--spacing-body-block-start: var(--mer-spacing--md);--spacing-body-block-end: var(--mer-spacing--md);--spacing-body-inline-start: var(--mer-spacing--lg);--spacing-body-inline-end: var(--mer-spacing--lg);display:grid;gap:var(--mer-spacing--md)}.layout__panel{display:grid}.layout--cols-1{grid-template-rows:1fr;grid-template-columns:1fr}.layout--cols-2{grid-template-rows:1fr;grid-template-columns:repeat(2, 1fr)}.layout--cols-3{grid-template-rows:1fr;grid-template-columns:repeat(3, 1fr)}.layout--cols-4{grid-template-rows:1fr;grid-template-columns:repeat(4, 1fr)}.layout--cols-1-2{grid-template-rows:1fr;grid-template-columns:1fr 2fr}.layout--cols-2-1{grid-template-rows:1fr;grid-template-columns:2fr 1fr}.control-footer,.control-footer-with-border{--control-footer-justify-content: end;padding-block-start:var(--mer-spacing--xs);display:flex;justify-content:var(--control-footer-justify-content);align-items:center}.control-footer-start{--control-footer-justify-content: start}.control-footer-center{--control-footer-justify-content: center}.control-footer-space-between{--control-footer-justify-content: space-between}.control-footer-with-border{border-block-start:var(--mer-border__width--sm) solid var(--mer-border-color__on-elevation--01)}.control-header,.control-header-with-border{padding-block-end:var(--mer-spacing--md)}.control-header-with-border{border-block-end:var(--mer-border__width--sm) solid var(--mer-border-color__on-elevation--01)}
@@ -0,0 +1,2 @@
1
+ import type { ChatActionsRender } from "@genexus/chameleon-controls-library/dist/types/components/chat/types";
2
+ export declare const customActionsRender: ChatActionsRender;
@@ -0,0 +1,18 @@
1
+ import { html } from "lit";
2
+ import { when } from "lit/directives/when.js";
3
+ import { copyButtonClickHandler, getMessageSerializedContentAll, tokenMap } from "./utils.js";
4
+ const copyClickHandler = (event, message) => {
5
+ // We use the whole message to support copying the files and/or sources
6
+ const messageSerializedContentAll = getMessageSerializedContentAll(message);
7
+ copyButtonClickHandler(event, messageSerializedContentAll);
8
+ };
9
+ export const customActionsRender = (message, chatRef) => {
10
+ const { accessibleName } = chatRef.translations;
11
+ const conditionToRender1 = message.role === "assistant" &&
12
+ (message.status === "complete" || !message.status);
13
+ const conditionToRender2 = message.role === "error";
14
+ return when(conditionToRender1 || conditionToRender2, () => html `<div part="actions-container"><button aria-label="${accessibleName.copyMessageContent}" part="${tokenMap({
15
+ [`action-button action-copy-message ${message.id}`]: true,
16
+ ...(message.parts ? { [message.parts]: true } : {})
17
+ })}" type="button" @click="${(e) => copyClickHandler(e, message)}"></button></div>`);
18
+ };
@@ -0,0 +1,2 @@
1
+ import type { ChatCodeBlockRender } from "@genexus/chameleon-controls-library/dist/types/components/chat/types";
2
+ export declare const customCodeBlockRender: ChatCodeBlockRender;
@@ -0,0 +1,9 @@
1
+ import { html } from "lit";
2
+ import { when } from "lit/directives/when.js";
3
+ import { copyButtonClickHandler } from "./utils.js";
4
+ const downloadCodeBlockCallback = (plainText, language, downloadCodeBlock) => () => downloadCodeBlock(plainText, language);
5
+ export const customCodeBlockRender = (chatRef) => (options) => {
6
+ const { accessibleName, text } = chatRef.translations;
7
+ const downloadCb = chatRef.callbacks?.downloadCodeBlock;
8
+ return html `<div part="code-block"><div part="code-block__header"><span part="code-block__header-caption">${options.language}</span><div part="code-block__header-actions"><button class="button-copy-code" aria-label="${text.copyCodeButton}" part="code-block__button--icon-only code-block__copy-code-button" type="button" @click="${(e) => copyButtonClickHandler(e, options.plainText)}"></button> ${when(downloadCb, () => html `<button aria-label="${accessibleName.downloadCodeButton}" part="code-block__button--icon-only code-block__download-code-button" @click="${downloadCodeBlockCallback(options.plainText, options.language, downloadCb)}"></button>`)}</div></div><ch-code .language="${options.language}" .lastNestedChildClass="${options.lastNestedChildClass}" part="code-block__content" .showIndicator="${options.showIndicator}" .value="${options.plainText}"></ch-code></div>`;
9
+ };
@@ -0,0 +1,9 @@
1
+ import type { ChatMessageFile } from "@genexus/chameleon-controls-library/dist/types/components/chat/types";
2
+ import { type TemplateResult } from "lit";
3
+ import "./mer-spinner.lit.js";
4
+ export declare const customFileRenders: {
5
+ readonly audio: (file: ChatMessageFile) => TemplateResult;
6
+ readonly video: (file: ChatMessageFile) => TemplateResult;
7
+ readonly image: (file: ChatMessageFile) => TemplateResult;
8
+ readonly file: (file: ChatMessageFile) => TemplateResult;
9
+ };
@@ -0,0 +1,71 @@
1
+ import { html, nothing } from "lit";
2
+ import { ifDefined } from "lit/directives/if-defined.js";
3
+ import "./mer-spinner.lit.js";
4
+ import { tokenMap } from "./utils.js";
5
+ const DEFAULT_FILE_UPLOAD_STATE = "uploaded";
6
+ // /**
7
+ // * Returns a skeleton element shown while a file is being uploaded.
8
+ // *
9
+ // * In Mercury, the loading state is visually represented in the footer,
10
+ // * so this skeleton is currently unused. However, it is recommended to keep
11
+ // * this function in the file in case the design changes and a visual loader
12
+ // * inside the file container becomes necessary again.
13
+ // *
14
+ // * @param file - The file object containing metadata and upload state.
15
+ // * @param fileFormat - The format type of the file (e.g., text, image, etc.).
16
+ // * @returns A TemplateResult with the skeleton element or `nothing` if not loading.
17
+ // */
18
+ // const fileSkeleton = (
19
+ // file: ChatMessageFile,
20
+ // fileFormat: keyof ChMimeTypeFormatMap
21
+ // ): TemplateResult | typeof nothing => {
22
+ // return file.uploadState === "in-progress"
23
+ // ? html`<div
24
+ // part=${tokenMap({
25
+ // [`file-skeleton format-${fileFormat} ${file.mimeType} ${
26
+ // file.uploadState ?? DEFAULT_FILE_UPLOAD_STATE
27
+ // }`]: true,
28
+ // [file.extension!]: !!file.extension,
29
+ // [file.parts!]: !!file.parts
30
+ // })}
31
+ // ></div>`
32
+ // : nothing;
33
+ // };
34
+ const fileFooter = (file, fileFormat) => {
35
+ return html `<div class="elevation-1" part="${tokenMap({
36
+ [`file-footer format-${fileFormat} ${file.mimeType} ${file.uploadState ?? DEFAULT_FILE_UPLOAD_STATE}`]: true,
37
+ [file.extension]: !!file.extension,
38
+ [file.parts]: !!file.parts
39
+ })}"><span class="body-regular-xs" part="file-caption">${file.caption}${file.extension && "." + file.extension.toLowerCase()} </span>${file.uploadState === "in-progress"
40
+ ? html `<mer-spinner></mer-spinner>`
41
+ : nothing}</div>`;
42
+ };
43
+ const getFileContainerParts = (file, fileFormat) => {
44
+ return tokenMap({
45
+ [`file-container format-${fileFormat} ${file.mimeType} ${file.uploadState ?? DEFAULT_FILE_UPLOAD_STATE}`]: true,
46
+ [file.extension]: !!file.extension,
47
+ [file.parts]: !!file.parts
48
+ });
49
+ };
50
+ const getFileParts = (file, fileFormat) => tokenMap({
51
+ [`file format-${fileFormat} ${file.mimeType} ${file.uploadState ?? DEFAULT_FILE_UPLOAD_STATE}`]: true,
52
+ [file.extension]: !!file.extension,
53
+ [file.parts]: !!file.parts
54
+ });
55
+ // TODO: Improve accessibility by exposing progress or spin states while
56
+ // uploading
57
+ export const customFileRenders = {
58
+ audio: (file) => {
59
+ return html `<li class="file-container" part="${getFileContainerParts(file, "audio")}"><audio aria-label="${ifDefined(file.accessibleName)}" part="${getFileParts(file, "audio")}" src="${ifDefined(file.uploadState === "in-progress" ? undefined : file.url)}" controls></audio>${fileFooter(file, "audio")}</li>`;
60
+ },
61
+ video: (file) => {
62
+ return html `<li class="file-container" part="${getFileContainerParts(file, "video")}"><video aria-label="${ifDefined(file.accessibleName)}" part="${getFileParts(file, "video")}" src="${ifDefined(file.uploadState === "in-progress" ? undefined : file.url)}" controls></video>${fileFooter(file, "video")}</li>`;
63
+ },
64
+ image: (file) => {
65
+ return html `<li class="file-container" part="${getFileContainerParts(file, "image")}"><img aria-label="${ifDefined(file.accessibleName)}" part="${getFileParts(file, "image")}" src="${file.url}" alt="${file.alternativeText ?? file.accessibleName ?? ""}" loading="lazy"> ${fileFooter(file, "image")}</li>`;
66
+ },
67
+ file: (file) => {
68
+ const disabledWhileUploading = file.uploadState === "in-progress";
69
+ return html `<li class="file-container" part="${getFileContainerParts(file, "file")}"><a aria-label="${ifDefined(file.accessibleName)}" role="${disabledWhileUploading ? "link" : nothing}" aria-disabled="${disabledWhileUploading ? "true" : nothing}" part="${getFileParts(file, "file")}" href="${disabledWhileUploading ? nothing : file.url}" target="_blank"></a> ${fileFooter(file, "file")}</li>`;
70
+ }
71
+ };
@@ -0,0 +1,10 @@
1
+ import { LitElement } from "lit";
2
+ export declare class MerAnimatedDots extends LitElement {
3
+ static styles: import("lit").CSSResult;
4
+ render(): import("lit-html").TemplateResult<1>;
5
+ }
6
+ declare global {
7
+ interface HTMLElementTagNameMap {
8
+ "mer-animated-dots": MerAnimatedDots;
9
+ }
10
+ }
@@ -0,0 +1,60 @@
1
+ import { LitElement, css, html } from "lit";
2
+ const DOT_SELECTOR = "dot";
3
+ export class MerAnimatedDots extends LitElement {
4
+ static styles = css `
5
+ :host {
6
+ --dot-size: 5px;
7
+ --dot-initial-opacity: 0;
8
+ --dot-initial-scale: 0.5;
9
+ --animation-duration: 0.4s;
10
+ display: inline-flex;
11
+ gap: calc(var(--dot-size) / 1.15);
12
+ }
13
+
14
+ .dot {
15
+ display: inline-block;
16
+ inline-size: var(--dot-size);
17
+ block-size: var(--dot-size);
18
+ border-radius: calc(var(--dot-size) / 2);
19
+ opacity: var(--dot-initial-opacity);
20
+ transform: scale(var(--dot-initial-scale));
21
+ animation: animateDot var(--animation-duration) ease-in-out infinite alternate;
22
+ }
23
+ .dot:first-child {
24
+ background-color: var(
25
+ --chat-avatar-waiting-dot-1-color,
26
+ var(--mer-color__chat-awaiting-dot-1)
27
+ );
28
+ }
29
+ .dot:nth-child(2) {
30
+ animation-delay: calc(var(--animation-duration) / 3);
31
+ background-color: var(
32
+ --chat-avatar-waiting-dot-2-color,
33
+ var(--mer-color__chat-awaiting-dot-2)
34
+ );
35
+ }
36
+ .dot:nth-child(3) {
37
+ animation-delay: calc(var(--animation-duration) / 1.5);
38
+ background-color: var(
39
+ --chat-avatar-waiting-dot-3-color,
40
+ var(--mer-color__chat-awaiting-dot-3)
41
+ );
42
+ }
43
+
44
+ @keyframes animateDot {
45
+ to {
46
+ opacity: 1;
47
+ transform: scale(1);
48
+ }
49
+ }
50
+ `;
51
+ // Render the UI as a function of component state
52
+ render() {
53
+ return html `<div class=${DOT_SELECTOR} part="dot dot-1"></div>
54
+ <div class=${DOT_SELECTOR} part="dot dot-2"></div>
55
+ <div class=${DOT_SELECTOR} part="dot dot-3"></div>`;
56
+ }
57
+ }
58
+ if (!customElements.get("mer-animated-dots")) {
59
+ customElements.define("mer-animated-dots", MerAnimatedDots);
60
+ }
@@ -0,0 +1,10 @@
1
+ import { LitElement } from "lit";
2
+ export declare class MerSpinner extends LitElement {
3
+ static styles: import("lit").CSSResult;
4
+ render(): import("lit-html").TemplateResult<1>;
5
+ }
6
+ declare global {
7
+ interface HTMLElementTagNameMap {
8
+ "mer-spinner": MerSpinner;
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ import { LitElement, css, html } from "lit";
2
+ export class MerSpinner extends LitElement {
3
+ static styles = css `:host{--spinner-size:var(--mer-spacing--md);--spinner-color:var(--mer-color__primary--300);--track-color:var(--mer-color__tinted-primary--50);--border-thinness:7;inline-size:var(--spinner-size);block-size:var(--spinner-size);border:calc(var(--spinner-size)/ var(--border-thinness)) solid var(--track-color);border-block-start:calc(var(--spinner-size)/ var(--border-thinness)) solid var(--spinner-color);border-radius:50%;animation:spin var(--mer-timing--regular,1s) linear infinite}@keyframes spin{100%{transform:rotate(360deg)}}`;
4
+ render() {
5
+ return html ``;
6
+ }
7
+ }
8
+ if (!customElements.get("mer-spinner")) {
9
+ customElements.define("mer-spinner", MerSpinner);
10
+ }
@@ -0,0 +1,4 @@
1
+ import type { ChatMessageRenderBySections, ChatSendContainerLayout } from "@genexus/chameleon-controls-library/dist/types/components/chat/types";
2
+ import "./mer-animated-dots.js";
3
+ export declare const renderItem: ChatMessageRenderBySections;
4
+ export declare const sendContainerLayout: ChatSendContainerLayout;
@@ -0,0 +1,104 @@
1
+ /*
2
+ README:
3
+ This file contains two utilities:
4
+
5
+ 1) [Sections for customitizing the ch-chat renderItem]
6
+ The following sections are being customized at the time of writting:
7
+ - actions: customActionsRender,
8
+ - codeBlock: customCodeBlockRender,
9
+ - content: customContentRender
10
+
11
+ What for?
12
+
13
+ - actions: In order to be able to customize the copy button, by adding a part
14
+ "copying" when the user copies the message. This additional part allows changing
15
+ the current button icon of "copy" to a "checked" icon.
16
+ - codeBlock: Same reason as "actions" section.
17
+ - content: Mercury includes a "header" section for the assistant message. This
18
+ header includes an avatar, the assistant name, and optionally the message time.
19
+ This header requires custom markup, and for this reason this section has to be
20
+ customized.
21
+
22
+ 2) [Configuration for the ch-chat sendContainerLayout property]
23
+ The chat styles are designed for a specific layout, so users must use the sendContainerLayout provided by Mercury. Customizing it is not supported.
24
+ */
25
+ import { html, nothing } from "lit";
26
+ // Side effect to define the animation for "waiting" messages.
27
+ import { customActionsRender } from "./actions.lit.js";
28
+ import { customCodeBlockRender } from "./code-block.lit.js";
29
+ import { customFileRenders } from "./file.lit.js";
30
+ import { DEFAULT_ASSISTANT_STATUS, getMessageContent, tokenMap } from "./utils.js";
31
+ import "./mer-animated-dots.js";
32
+ // 1) [Sections for customitizing the ch-chat renderItem.]
33
+ /**
34
+ * Mercury section that includes the assistant the assistant name, and an optional message time.
35
+ *
36
+ * @param messageMetaData - Metadata containing agentName and time
37
+ * @returns TemplateResult rendering the assistant message header
38
+ */
39
+ const messageHeader = (messageMetaData, waiting = false) => {
40
+ const agentName = messageMetaData?.agentName;
41
+ const assistantId = agentName?.toLowerCase().replace(/ /g, "-");
42
+ const messageTime = messageMetaData?.time;
43
+ return html `<div part="header assistant${assistantId ? ` header-${assistantId}` : ""}"><div part="header__start assistant"><div part="header__avatar-container assistant"><div part="header__avatar-border assistant"></div><div part="header__avatar-image assistant"></div></div><div part="header__role assistant">${agentName}</div>${waiting ? html `<mer-animated-dots></mer-animated-dots>` : nothing}</div>${messageTime
44
+ ? html `<time datetime="${messageTime}" part="header__time assistant">${messageTime}</time>`
45
+ : nothing}</div>`;
46
+ };
47
+ // customzied in order to add the custom "messageHeader"
48
+ const customAssistantContentRender = (message, chatRef, codeBlockRender) => {
49
+ const messageContent = getMessageContent(message);
50
+ return message.status === "waiting"
51
+ ? html `${messageHeader(message.metadata, true)}<div class="assistant-loading" part="${tokenMap({
52
+ [`assistant content waiting ${message.id}`]: true,
53
+ ...(message.parts ? { [message.parts]: true } : {})
54
+ })}">${messageContent}</div>`
55
+ : html `${messageHeader(message.metadata)}<ch-markdown-viewer part="${tokenMap({
56
+ [`assistant content ${message.id} ${message.status ?? DEFAULT_ASSISTANT_STATUS}`]: true,
57
+ ...(message.parts ? { [message.parts]: true } : {})
58
+ })}" .renderCode="${
59
+ // WA: Define codeBlockRender as "never" to avoid types incompatibility:
60
+ // codeBlockRender returns TemplateResult and renderCode expects TemplateResult | undefined
61
+ // In Chameleon this type mismatch does not happens. TODO: Evaluate a solution in Chameleon.
62
+ codeBlockRender(chatRef)}" .showIndicator="${message.status === "streaming"}" .theme="${chatRef.markdownTheme ?? undefined}" .value="${messageContent}"></ch-markdown-viewer>`;
63
+ };
64
+ // customzied in order to add the custom "messageHeader"
65
+ const customErrorContentRender = (message, chatRef, codeBlockRender) => {
66
+ const errorContent = getMessageContent(message);
67
+ return html `${messageHeader(message.metadata)}<ch-markdown-viewer part="${tokenMap({
68
+ [`error content ${message.id}`]: true,
69
+ ...(message.parts ? { [message.parts]: true } : {})
70
+ })}" .renderCode="${
71
+ // WA: Define codeBlockRender as "never" to avoid types incompatibility:
72
+ // codeBlockRender returns TemplateResult and renderCode expects TemplateResult | undefined
73
+ // In Chameleon this type mismatch does not happens. TODO: Evaluate a solution in Chameleon.
74
+ codeBlockRender(chatRef)}" .theme="${chatRef.markdownTheme ?? undefined}" .value="${errorContent}"></ch-markdown-viewer>`;
75
+ };
76
+ // No modification. It is required to be defined.
77
+ const defaultSystemContentRender = () => "string | TemplateResult";
78
+ // No modification. It is required to be defined.
79
+ const defaultUserContentRender = (messageModel) => getMessageContent(messageModel) ?? "";
80
+ const contentRenderByRole = {
81
+ // TODO: Check if each contentRender can be optional, since it may be not me required to implement
82
+ // a custom render for each type of content. ie.: Mercury does not requres to customize "system" and "user"
83
+ assistant: customAssistantContentRender,
84
+ error: customErrorContentRender,
85
+ system: defaultSystemContentRender, // No modification
86
+ user: defaultUserContentRender // No modification
87
+ };
88
+ const customContentRender = (message, chatRef, codeBlockRender) => contentRenderByRole[message.role](message, chatRef, codeBlockRender);
89
+ export const renderItem = {
90
+ actions: customActionsRender,
91
+ codeBlock: customCodeBlockRender,
92
+ content: customContentRender,
93
+ file: customFileRenders
94
+ };
95
+ // 2) [Configuration for the ch-chat sendContainerLayout property]
96
+ export const sendContainerLayout = {
97
+ sendInputAfter: [
98
+ "attach-files-button",
99
+ "live-audio-button",
100
+ "send-button",
101
+ "chat-attached-files-viewer"
102
+ ],
103
+ sendContainerAfter: ["live-audio-container"]
104
+ };
@@ -0,0 +1,4 @@
1
+ export type MercuryChatMessageMetadata = {
2
+ agentName: string;
3
+ time?: string;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { ChatMessageNoId } from "@genexus/chameleon-controls-library/dist/types/components/chat/types";
2
+ export declare const getMessageSerializedContentAll: (message: ChatMessageNoId) => string;
3
+ export declare const getMessageContent: (message: ChatMessageNoId) => string | undefined;
4
+ /**
5
+ * Converts an object mapping token keys to booleans into a space-separated
6
+ * string containing the token keys that map to truthy values.
7
+ * @example
8
+ * part={tokenMap({
9
+ * header: true,
10
+ * disabled: this.disabled,
11
+ * selected: this.selected,
12
+ * [levelPart]: canShowLines,
13
+ * "expand-button":
14
+ * canShowLines && !this.leaf && this.expandableButton !== "no"
15
+ * })}
16
+ */
17
+ export declare const tokenMap: (tokens: {
18
+ [key: string]: boolean;
19
+ }) => string;
20
+ export declare const copyToTheClipboard: (text: string) => Promise<void>;
21
+ export declare const copy: (text: string) => () => void;
22
+ export declare const copyButtonClickHandler: (event: Event, text: string) => void;
23
+ export declare const DEFAULT_ASSISTANT_STATUS = "complete";
@@ -0,0 +1,54 @@
1
+ // TODO: The functions on this file are a WA until Chameleon 7 is released and we can reuse
2
+ // the existing functions from Chameleon. At the moment, we have to copy paste the functions
3
+ // because in Chameleon 6 we can import them.
4
+ // The following utils were copied from Chamelmeon (src/components/chat/utils.ts or src/common/utils.ts)
5
+ export const getMessageSerializedContentAll = (message) => typeof message.content === "string"
6
+ ? message.content
7
+ : JSON.stringify(message.content, undefined, 2);
8
+ export const getMessageContent = (message) => typeof message.content === "string"
9
+ ? message.content
10
+ : message.content.message;
11
+ /**
12
+ * Converts an object mapping token keys to booleans into a space-separated
13
+ * string containing the token keys that map to truthy values.
14
+ * @example
15
+ * part={tokenMap({
16
+ * header: true,
17
+ * disabled: this.disabled,
18
+ * selected: this.selected,
19
+ * [levelPart]: canShowLines,
20
+ * "expand-button":
21
+ * canShowLines && !this.leaf && this.expandableButton !== "no"
22
+ * })}
23
+ */
24
+ export const tokenMap = (tokens) => {
25
+ const keys = Object.keys(tokens);
26
+ let result = "";
27
+ for (let index = 0; index < keys.length; index++) {
28
+ const tokenKey = keys[index];
29
+ const tokenValue = tokens[tokenKey];
30
+ if (tokenValue) {
31
+ result += result === "" ? tokenKey : ` ${tokenKey}`;
32
+ }
33
+ }
34
+ return result;
35
+ };
36
+ export const copyToTheClipboard = (text) => navigator.clipboard.writeText(text);
37
+ export const copy = (text) => () => {
38
+ copyToTheClipboard(text);
39
+ };
40
+ export const copyButtonClickHandler = (event, text) => {
41
+ copyToTheClipboard(text);
42
+ const button = event.currentTarget;
43
+ if (button) {
44
+ const buttonOriginalParts = button.getAttribute("part");
45
+ const buttonWithCopyingParts = `${buttonOriginalParts} copying`;
46
+ button.setAttribute("disabled", "disabled");
47
+ button.setAttribute("part", buttonWithCopyingParts);
48
+ setTimeout(() => {
49
+ button.removeAttribute("disabled");
50
+ button.setAttribute("part", buttonOriginalParts || "");
51
+ }, 1500);
52
+ }
53
+ };
54
+ export const DEFAULT_ASSISTANT_STATUS = "complete";
package/dist/index.d.ts CHANGED
@@ -2,3 +2,5 @@ export * from "./assets-manager.js";
2
2
  export * from "./bundles.js";
3
3
  export * from "./register-mercury.js";
4
4
  export * from "./types.js";
5
+ export * from "./components/chat/render.lit.js";
6
+ export type * from "./components/chat/types";
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";export*from"./assets-manager.js";export*from"./bundles.js";export*from"./register-mercury.js";export*from"./types.js";
1
+ "use strict";export*from"./assets-manager.js";export*from"./bundles.js";export*from"./register-mercury.js";export*from"./types.js";export*from"./components/chat/render.lit.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@genexus/mercury",
3
- "version": "0.29.0",
3
+ "version": "0.31.0",
4
4
  "description": "Mercury Design System is a robust and scalable solution designed to improve product development.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -43,11 +43,19 @@
43
43
  "default": "./dist/register-mercury.js"
44
44
  }
45
45
  },
46
+ "./components/chat/render.js": {
47
+ "types": "./dist/components/chat/render.lit.d.ts",
48
+ "default": "./dist/components/chat/render.lit.js"
49
+ },
50
+ "./components/chat/message-metadata": {
51
+ "types": "./dist/components/chat/types.d.ts",
52
+ "import": "./dist/components/chat/types.js"
53
+ },
46
54
  "./package.json": "./package.json"
47
55
  },
48
56
  "scripts": {
49
57
  "build": "yarn build.js && yarn icons-svg && yarn icons-sass && yarn build.scss && yarn copy-tasks",
50
- "build.js": "tsc && esbuild dist/*.js --allow-overwrite --minify --outdir=dist --log-level=silent && esbuild dist/assets/*.js --allow-overwrite --minify --outdir=dist/assets --log-level=silent",
58
+ "build.js": "tsc && node minify-literals.js && esbuild dist/*.js --allow-overwrite --minify --outdir=dist --log-level=silent && esbuild dist/assets/*.js --allow-overwrite --minify --outdir=dist/assets --log-level=silent",
51
59
  "build-no-svg": "yarn build.js && yarn build.scss && yarn copy-tasks",
52
60
  "build.scss": "yarn build.scss.base && yarn build.scss.bundles",
53
61
  "build.scss.base": "scss-bundle -e ./src/icons/_generated/categories.scss -o src/icons/_generated/categories-bundled.scss --logLevel=silent",
@@ -69,9 +77,11 @@
69
77
  "test.watch": "yarn playwright install && vitest watch"
70
78
  },
71
79
  "license": "Apache-2.0",
80
+ "peerDependencies": {
81
+ "@genexus/chameleon-controls-library": ">= 6.22.1"
82
+ },
72
83
  "devDependencies": {
73
- "@eslint/js": "~9.26.0",
74
- "@genexus/chameleon-controls-library": "~6.7.0",
84
+ "@genexus/chameleon-controls-library": "6.22.1",
75
85
  "@genexus/svg-sass-generator": "1.1.24",
76
86
  "@jackolope/ts-lit-plugin": "^3.1.4",
77
87
  "@types/node": "~22.10.5",
@@ -82,9 +92,10 @@
82
92
  "@vitest/ui": "~3.1.1",
83
93
  "chokidar": "^3.6.0",
84
94
  "chokidar-cli": "^3.0.0",
85
- "esbuild": "0.25.0",
95
+ "esbuild": "~0.25.8",
86
96
  "eslint": "~9.26.0",
87
97
  "globals": "~16.1.0",
98
+ "minify-html-literals": "~1.3.5",
88
99
  "playwright": "*",
89
100
  "prettier": "~3.5.3",
90
101
  "sass": "~1.86.3",