From a4f9f659f38fafda34e4e4ac5a92508a3c773f37 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Thu, 18 Dec 2025 10:46:30 -0600 Subject: [PATCH 01/12] Disclaimer component --- .../src/chat/specs/README.md | 40 +++++++++++++++++- .../specs/spec-images/chat-disclaimer.png | Bin 0 -> 13148 bytes 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 packages/spright-components/src/chat/specs/spec-images/chat-disclaimer.png diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 503e6f260e..d77fa06449 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -7,6 +7,9 @@ This spec describes a set of components that can be used to compose a chat inter - chat input: a text input, button, and related components for users to compose and send new messages - chat message: a single entry in a chat conversation, including some content and metadata about the message - chat conversation: a layout component that allows slotting messages and an input +- chat login: a static message and link to direct the user to an external login experience +- chat welcome: a static initial message that welcomes the user to the chat experience after logging in +- chat disclaimer: a static legal message that can appear within a conversation ### Background @@ -69,7 +72,7 @@ The component also contains the following features: 1. Lays out messages vertically based on their order. 1. Displays a vertical scrollbar if there are more messages than fit in the height allocated to the conversation. -1. Includes a slot to place an input component below the messages. +1. Includes a slot to place an input component below the messages and a slot for the disclaimer below that. 1. Only appearance of its own is to set a background color. #### Chat input @@ -90,6 +93,15 @@ The component also contains the following features: 1. Styling for default, focus, and rollover states 1. Displays errors via the standard red `!` icon and error text +#### Chat login + +#### Chat welcome + +#### Chat disclaimer + +1. A short message ("AI-generated content may be incorrect") and static link to more information ("[View Terms and Conditions](https://www.ni.com/content/dam/web/pdfs/legal/terms_and_conditions_generative_ai.pdf)"). +1. The link `target` will be configurable. Some clients require the link to be configured to open in a new browser context since they are hosted in an embedded pane. Other clients follow best practices which prefer opening links in the same context. + ### Risks and Challenges These components are competing against possible implementations within applications. Depending on who implements these components, the overhead of learning the Nimble repo's tech stack could introduce a small risk. @@ -108,6 +120,10 @@ These components are competing against possible implementations within applicati ![ ](spec-images/chat-pane.png) +**Screenshot of Figma design of chat disclaimer (light mode)** + +![ ](spec-images/chat-disclaimer.png) + --- ## Design @@ -128,6 +144,7 @@ These components are competing against possible implementations within applicati ``` @@ -202,6 +219,7 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; - _Slots_ - chat messages are added to the default slot. The DOM order of the messages controls their screen order within the conversation (earlier DOM order => earlier message => top of the conversation) - a single chat input can optionally be added to the `input` slot. It will be placed below the messages. + - a single chat disclaimer can optionally be added to the `end` slot. It will be placed below the input. #### Input @@ -247,6 +265,18 @@ In the case of the auto-clearing being undesirable, a `resetInput()` method was [Some concern](https://github.com/ni/nimble/pull/2611#discussion_r2110130708) was raised with having the 'Send' button (or pressing \) automatically clearing the text, however there is enough basis to do so both in that this is designed behavior for the control (there is no expectation that the text field should maintain any text after the send event occurs), and we already have a similar UX semantic with the clear button for the `Select`. +#### Disclaimer + +- _Component Name_ `spright-chat-disclaimer` +- _Props/Attrs_ + - `target` - an attribute which is forwarded to the link's `target` attribute. Defaults to `_self`. + - User-visible strings will be be specified via the chat label provider. The link URL will not be configurable. +- _Methods_ +- _Events_ +- _CSS Classes and CSS Custom Properties that affect the component_ +- _How native CSS Properties (height, width, etc.) affect the component_ +- _Slots_ + ### Anatomy #### Message @@ -320,6 +350,12 @@ to implement the ability to grow the height of the text area as the user types. Initially clients will either use modern versions of Chromium-based browsers or will only leverage this component behind a feature flag. If that changes before the feature is available in all supported browsers, we will revisit this decision and consider implementing a JavaScript-based resizing solution. +#### Disclaimer + +The template will simply contain a `span` and a `nimble-anchor` with contents populated by label provider strings. + +Most styling can be achieved with existing tokens and APIs. The visual design calls for some anchor styling which is not available today (grey link text, smaller font size). Since this is the only known use case for this design, we can implement it by overriding anchor token values in the disclaimer component rather than adding new public API to the anchor. + ### Native form integration Native form integration is not needed for these components. @@ -390,7 +426,7 @@ On mobile, typing a newline in the input will be difficult as most on-screen key ### Globalization -Most content is provided by applications so they are responsible for localization. For the input component "Send" and "Stop" button titles and accessible labels we will add label provider strings. These supply default labels which applications can localize or replace with custom labels. These will be added to a new Spright chat label provider. +Most content is provided by applications so they are responsible for localization. For components with user-visible text we will add label provider strings. These supply default labels which applications can localize or replace with custom labels. These will be added to a new Spright chat label provider. Defining the behavior for RTL languages is initially out of scope. But the API can easily be extended to support changing the layout for an RTL language when that is desired. diff --git a/packages/spright-components/src/chat/specs/spec-images/chat-disclaimer.png b/packages/spright-components/src/chat/specs/spec-images/chat-disclaimer.png new file mode 100644 index 0000000000000000000000000000000000000000..178ed0689d70e0ce99e5154666e7de49f9956a0d GIT binary patch literal 13148 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 F{tt4T1pEL1 literal 0 HcmV?d00001 From 99508a2aa11c14f6a86ecf623594bd5c7356cc35 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Thu, 18 Dec 2025 12:46:18 -0600 Subject: [PATCH 02/12] Welcome component --- .../src/chat/specs/README.md | 59 ++++++++++++++++--- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index d77fa06449..22a551d876 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -6,9 +6,8 @@ This spec describes a set of components that can be used to compose a chat inter - chat input: a text input, button, and related components for users to compose and send new messages - chat message: a single entry in a chat conversation, including some content and metadata about the message +- chat message welcome content: one type of message content that welcomes the user to the chat experience and allows them to login - chat conversation: a layout component that allows slotting messages and an input -- chat login: a static message and link to direct the user to an external login experience -- chat welcome: a static initial message that welcomes the user to the chat experience after logging in - chat disclaimer: a static legal message that can appear within a conversation ### Background @@ -45,7 +44,7 @@ The message component will allow slotting arbitrary content, but any efforts to The `spright-chat-message` has the following slot elements. -1. `default` slot displays arbitrary slotted content. For example: text, rich text, buttons, images, or a spinner. +1. `default` slot displays arbitrary slotted content. For example: text, rich text, buttons, images, a spinner, or welcome content. 1. `footer-actions` slot which is used to add action buttons below the main content. 1. `end` slot which is used to add text buttons. They are below any action buttons. @@ -68,6 +67,13 @@ The component also contains the following features: 1. Size based on content size with maximum width (but not height) based on parent's width. 1. Change the styling of the message depending on metadata about who sent the message. For example: render user messages in a bubble with the tail pointing to the right but render system messages with no styling. +#### Chat message welcome content + +1. Contains welcome content that can be slotted into a message +1. Content includes text ("Welcome to Nigel™ AI") and an image to brand the chat experience +1. If the user is not logged in, displays a button to launch the external login process +1. If the user is logged in, displays text ("Chat below to get started") explaining the first step the user should take + #### Chat conversation 1. Lays out messages vertically based on their order. @@ -93,10 +99,6 @@ The component also contains the following features: 1. Styling for default, focus, and rollover states 1. Displays errors via the standard red `!` icon and error text -#### Chat login - -#### Chat welcome - #### Chat disclaimer 1. A short message ("AI-generated content may be incorrect") and static link to more information ("[View Terms and Conditions](https://www.ni.com/content/dam/web/pdfs/legal/terms_and_conditions_generative_ai.pdf)"). @@ -163,6 +165,16 @@ const richText = document.querySelector('#welcome'); richText.markdown = 'Welcome **Homer**, how can I help?'; ``` +#### Welcome message example + +```html + + + + + +``` + #### Prompt buttons message example @@ -173,7 +185,6 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; ``` - #### Input example ```html @@ -202,6 +213,26 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; - `(default)` - arbitrary content can be added to the default slot to be displayed within the message +#### Message welcome content + +- _Component Name_ `spright-chat-message-welcome-content` +- _Props/Attrs_ + - User-visible strings will be be specified via the chat label provider. +- _Methods_ +- _Events_ +- _CSS Classes and CSS Custom Properties that affect the component_ +- _How native CSS Properties (height, width, etc.) affect the component_ +- _Slots_ + - default slot can be used to provide a login button or anchor button. If not provided, the component will show the post-login instructions instead. We will provide usage guidance for the button content ("Login") and appearance (primary block). + +##### Message welcome content API alternatives + +Instead of clients slotting the login button, the component could expose attributes that are forwarded to a button that is managed by the component. This would improve consistency and avoid the need for usage guidance about how to configure the button. However it would require a rather large API surface: + - whether to use a button or anchor button + - `login-disabled` attribute + - if using a button: `loginclick` event + - if using an anchor button: `login-href` attribute + #### Conversation - _Component Name_ `spright-chat-conversation` @@ -298,6 +329,16 @@ A message is simply a `div` which will styled with background / border / rounded ``` +#### Message welcome content + +The template will include an `svg` element to render the image. The image requires different svg contents for dark and light themes (they use different gradient parameters). The gradient content will be specified in a new design token, `messageWelcomeContentGradient`, and the template will read the correct gradient value for the current theme using `messageWelcomeContentGradient.getValueFor()`. + +A `slot` element will be used to host the login button. + +Text content will be placed in `div` elements, conditionally shown depending on whether there is slotted content. We will use our standard pattern to detect whether there is slotted content (see #2579). + +We can use [the existing Blazor implementation](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/Controls/Components/StartPage.razor) and [images](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/NigelLocalService/wwwroot/Images/two-chat-sparkle_green_DarkUI_48x48.svg) for reference. + #### Conversation Other than setting a background, a conversation has no appearance of its own and simply contains the default slot for messages and the input slot for the chat input, along with wrapper `div`s for layout purposes. @@ -356,6 +397,8 @@ The template will simply contain a `span` and a `nimble-anchor` with contents po Most styling can be achieved with existing tokens and APIs. The visual design calls for some anchor styling which is not available today (grey link text, smaller font size). Since this is the only known use case for this design, we can implement it by overriding anchor token values in the disclaimer component rather than adding new public API to the anchor. +We can use [the existing Blazor implementation](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/Controls/Components/ChatbotViewFooter.razor) for reference. + ### Native form integration Native form integration is not needed for these components. From d9d771a3406cd9bdac1a836956a58d0318f57af2 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Thu, 18 Dec 2025 12:46:38 -0600 Subject: [PATCH 03/12] Change files --- ...ht-components-7e39001e-264a-45c9-9b94-6c5510aab6b0.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@ni-spright-components-7e39001e-264a-45c9-9b94-6c5510aab6b0.json diff --git a/change/@ni-spright-components-7e39001e-264a-45c9-9b94-6c5510aab6b0.json b/change/@ni-spright-components-7e39001e-264a-45c9-9b94-6c5510aab6b0.json new file mode 100644 index 0000000000..5d40daeb60 --- /dev/null +++ b/change/@ni-spright-components-7e39001e-264a-45c9-9b94-6c5510aab6b0.json @@ -0,0 +1,7 @@ +{ + "type": "none", + "comment": "Spec updates for new chat components", + "packageName": "@ni/spright-components", + "email": "jattasNI@users.noreply.github.com", + "dependentChangeType": "none" +} From 410463066a1e88b4fd74aec7b80afc15c43a2252 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Thu, 18 Dec 2025 12:57:56 -0600 Subject: [PATCH 04/12] welcome message screenshot --- .../spright-components/src/chat/specs/README.md | 4 ++++ .../specs/spec-images/chat-welcome-login.png | Bin 0 -> 65971 bytes 2 files changed, 4 insertions(+) create mode 100644 packages/spright-components/src/chat/specs/spec-images/chat-welcome-login.png diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 22a551d876..066ddad10b 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -114,6 +114,10 @@ These components are competing against possible implementations within applicati ![ ](spec-images/chat-conversation.png) +**Screenshot of Figma design of chat welcome message when login is visible (light mode)** + +![ ](spec-images/chat-welcome-login.png) + **Screenshot of Figma design of chat input component (light mode)** ![ ](spec-images/chat-input.png) diff --git a/packages/spright-components/src/chat/specs/spec-images/chat-welcome-login.png b/packages/spright-components/src/chat/specs/spec-images/chat-welcome-login.png new file mode 100644 index 0000000000000000000000000000000000000000..61bf24453019b8db9e6dc7a05c01f44ea3550a3a GIT binary patch literal 65971 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 literal 0 HcmV?d00001 From 509df0c5f8041aaefa8bed3f8e2d720ddba4f3f8 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Thu, 18 Dec 2025 13:27:26 -0600 Subject: [PATCH 05/12] Update login example --- packages/spright-components/src/chat/specs/README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 066ddad10b..6b2c86b6b4 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -173,8 +173,12 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; ```html - - + + + + Login + + ``` @@ -189,6 +193,7 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; ``` + #### Input example ```html From 2ca6c8a35913ecf209aa865ccfd1d08d0382e3a5 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Thu, 18 Dec 2025 13:33:09 -0600 Subject: [PATCH 06/12] Rename welcome component --- packages/spright-components/src/chat/specs/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 6b2c86b6b4..57508361f9 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -174,11 +174,11 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; ```html - + Login - + ``` @@ -224,7 +224,7 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; #### Message welcome content -- _Component Name_ `spright-chat-message-welcome-content` +- _Component Name_ `spright-chat-welcome-message-content` - _Props/Attrs_ - User-visible strings will be be specified via the chat label provider. - _Methods_ @@ -404,7 +404,7 @@ that changes before the feature is available in all supported browsers, we will The template will simply contain a `span` and a `nimble-anchor` with contents populated by label provider strings. -Most styling can be achieved with existing tokens and APIs. The visual design calls for some anchor styling which is not available today (grey link text, smaller font size). Since this is the only known use case for this design, we can implement it by overriding anchor token values in the disclaimer component rather than adding new public API to the anchor. +Most styling can be achieved with existing tokens and APIs. The visual design calls for some anchor styling which is not available today (grey text, smaller font size). Since this is the only known use case for this design, we can implement it by overriding anchor token values in the disclaimer component rather than adding new public API to the anchor. We can use [the existing Blazor implementation](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/Controls/Components/ChatbotViewFooter.razor) for reference. From 29a9694b41eeea873cbc4563f3e4a180fce37f53 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Mon, 5 Jan 2026 16:55:01 -0600 Subject: [PATCH 07/12] minor tweaks --- packages/spright-components/src/chat/specs/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 57508361f9..f036aa3f6f 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -71,7 +71,7 @@ The component also contains the following features: 1. Contains welcome content that can be slotted into a message 1. Content includes text ("Welcome to Nigel™ AI") and an image to brand the chat experience -1. If the user is not logged in, displays a button to launch the external login process +1. If the user is not logged in, displays a button or anchor button to launch the external login process 1. If the user is logged in, displays text ("Chat below to get started") explaining the first step the user should take #### Chat conversation @@ -232,7 +232,7 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; - _CSS Classes and CSS Custom Properties that affect the component_ - _How native CSS Properties (height, width, etc.) affect the component_ - _Slots_ - - default slot can be used to provide a login button or anchor button. If not provided, the component will show the post-login instructions instead. We will provide usage guidance for the button content ("Login") and appearance (primary block). + - default slot can be used to provide a login button or anchor button. If not provided, the component will show the post-login instructions instead. We will provide usage guidance suggesting the button content ("Login") and appearance (primary block). ##### Message welcome content API alternatives @@ -344,7 +344,7 @@ The template will include an `svg` element to render the image. The image requir A `slot` element will be used to host the login button. -Text content will be placed in `div` elements, conditionally shown depending on whether there is slotted content. We will use our standard pattern to detect whether there is slotted content (see #2579). +Text content will be placed in `div` elements, conditionally shown if there is no slotted login button. We will use our standard pattern to detect whether there is slotted content (see #2579). We can use [the existing Blazor implementation](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/Controls/Components/StartPage.razor) and [images](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/NigelLocalService/wwwroot/Images/two-chat-sparkle_green_DarkUI_48x48.svg) for reference. From 7c7967eb12ea1576b410c3df3e547f50d6dd314a Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Wed, 4 Feb 2026 18:00:56 +0200 Subject: [PATCH 08/12] Remove disclaimer component, apply style to slot instead --- .../src/chat/specs/README.md | 35 ++++--------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 923957f362..757068e052 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -11,7 +11,6 @@ This spec describes a set of components that can be used to compose a chat inter - system - outbound - chat conversation: a layout component that allows slotting messages and an input -- chat disclaimer: a static legal message that can appear within a conversation ### Background @@ -87,7 +86,7 @@ All end text buttons must meet the following criteria 1. Lays out messages vertically based on their order. 1. Displays a vertical scrollbar if there are more messages than fit in the height allocated to the conversation. -1. Includes a slot to place an input component below the messages and a slot for the disclaimer below that. +1. Includes a slot to place an input component below the messages and a slot for a legal disclaimer below that. 1. Only appearance of its own is to set a background color. #### Chat input @@ -108,11 +107,6 @@ All end text buttons must meet the following criteria 1. Styling for default, focus, and rollover states 1. Displays errors via the standard red `!` icon and error text -#### Chat disclaimer - -1. A short message ("AI-generated content may be incorrect") and static link to more information ("[View Terms and Conditions](https://www.ni.com/content/dam/web/pdfs/legal/terms_and_conditions_generative_ai.pdf)"). -1. The link `target` will be configurable. Some clients require the link to be configured to open in a new browser context since they are hosted in an embedded pane. Other clients follow best practices which prefer opening links in the same context. - ### Risks and Challenges These components are competing against possible implementations within applications. Depending on who implements these components, the overhead of learning the Nimble repo's tech stack could introduce a small risk. @@ -159,7 +153,10 @@ These components are competing against possible implementations within applicati + + AI-generated content may be incorrect. + View terms and conditions + ``` @@ -283,7 +280,7 @@ Instead of clients slotting the login button, the component could expose attribu - _Slots_ - chat messages are added to the default slot. The DOM order of the messages controls their screen order within the conversation (earlier DOM order => earlier message => top of the conversation) - a single chat input can optionally be added to the `input` slot. It will be placed below the messages. - - a single chat disclaimer can optionally be added to the `end` slot. It will be placed below the input. + - chat disclaimer content can optionally be added to the `end` slot. It will be placed below the input. The slot will apply color and font size styles to text and anchor content to match the visual design spec. See [the existing Blazor implementation](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/Controls/Components/ChatbotViewFooter.razor) for reference. #### Input @@ -329,18 +326,6 @@ In the case of the auto-clearing being undesirable, a `resetInput()` method was [Some concern](https://github.com/ni/nimble/pull/2611#discussion_r2110130708) was raised with having the 'Send' button (or pressing \) automatically clearing the text, however there is enough basis to do so both in that this is designed behavior for the control (there is no expectation that the text field should maintain any text after the send event occurs), and we already have a similar UX semantic with the clear button for the `Select`. -#### Disclaimer - -- _Component Name_ `spright-chat-disclaimer` -- _Props/Attrs_ - - `target` - an attribute which is forwarded to the link's `target` attribute. Defaults to `_self`. - - User-visible strings will be be specified via the chat label provider. The link URL will not be configurable. -- _Methods_ -- _Events_ -- _CSS Classes and CSS Custom Properties that affect the component_ -- _How native CSS Properties (height, width, etc.) affect the component_ -- _Slots_ - ### Anatomy #### Message @@ -424,14 +409,6 @@ to implement the ability to grow the height of the text area as the user types. Initially clients will either use modern versions of Chromium-based browsers or will only leverage this component behind a feature flag. If that changes before the feature is available in all supported browsers, we will revisit this decision and consider implementing a JavaScript-based resizing solution. -#### Disclaimer - -The template will simply contain a `span` and a `nimble-anchor` with contents populated by label provider strings. - -Most styling can be achieved with existing tokens and APIs. The visual design calls for some anchor styling which is not available today (grey text, smaller font size). Since this is the only known use case for this design, we can implement it by overriding anchor token values in the disclaimer component rather than adding new public API to the anchor. - -We can use [the existing Blazor implementation](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/Controls/Components/ChatbotViewFooter.razor) for reference. - ### Native form integration Native form integration is not needed for these components. From 06aeeaaf640a8099bc23dfafe8a000227c363580 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Fri, 13 Feb 2026 18:29:14 -0600 Subject: [PATCH 09/12] welcome message updates --- .../src/chat/specs/README.md | 57 +++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 757068e052..ea030f9ddc 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -6,10 +6,10 @@ This spec describes a set of components that can be used to compose a chat inter - chat input: a text input, button, and related components for users to compose and send new messages - chat messages: each a single entry in a chat conversation, including some content and metadata about the message -- chat message welcome content: one type of message content that welcomes the user to the chat experience and allows them to login - inbound - system - outbound + - welcome - chat conversation: a layout component that allows slotting messages and an input ### Background @@ -75,12 +75,14 @@ All end text buttons must meet the following criteria 1. The `apperance` attribute is set to `block` 1. The buttons only have text -#### Chat message welcome content +##### Welcome message features -1. Contains welcome content that can be slotted into a message -1. Content includes text ("Welcome to Nigel™ AI") and an image to brand the chat experience -1. If the user is not logged in, displays a button or anchor button to launch the external login process -1. If the user is logged in, displays text ("Chat below to get started") explaining the first step the user should take +1. Centered horizontally within a conversation, similar to system messages +1. Displays a fixed image to brand the chat experience +1. Displays client-provided title (e.g. "Welcome to Nigel™ AI") and sub-title (e.g. "Chat below to get started", "Log in to get started") text +1. Allows content to be added in the default slot for purposes including: + - If the user is not logged in, slot a button or anchor button to launch the external login process + - If the user is logged in, slot buttons offering suggested outbound messages #### Chat conversation @@ -179,13 +181,11 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; ```html - - + Login - - + ``` @@ -214,6 +214,7 @@ Message components will be created with the following names: - `spright-chat-message-inbound` - `spright-chat-message-outbound` - `spright-chat-message-system` +- `spright-chat-message-welcome` ##### Messages common API @@ -228,40 +229,24 @@ All message types will share the following API: - A message will grow its height to fit its content, with no maximum height. - Clients could override this behavior but we don't anticipate use cases for doing so when the message is used within a conversation - _Slots_ - - `footer-actions` - - Action buttons to display after the main content. - `end` - - Buttons with text that are displayed at the bottom after any action buttons. + - Buttons with text that are displayed at the bottom after any other content. - `(default)` - arbitrary content can be added to the default slot to be displayed within the message -#### Message welcome content - -- _Component Name_ `spright-chat-welcome-message-content` -- _Props/Attrs_ - - User-visible strings will be be specified via the chat label provider. -- _Methods_ -- _Events_ -- _CSS Classes and CSS Custom Properties that affect the component_ -- _How native CSS Properties (height, width, etc.) affect the component_ -- _Slots_ - - default slot can be used to provide a login button or anchor button. If not provided, the component will show the post-login instructions instead. We will provide usage guidance suggesting the button content ("Login") and appearance (primary block). - -##### Message welcome content API alternatives - -Instead of clients slotting the login button, the component could expose attributes that are forwarded to a button that is managed by the component. This would improve consistency and avoid the need for usage guidance about how to configure the button. However it would require a rather large API surface: - - whether to use a button or anchor button - - `login-disabled` attribute - - if using a button: `loginclick` event - - if using an anchor button: `login-href` attribute - ##### Inbound message additional API - _Slots_ - `footer-actions` - Action buttons to display after the main content. - - `end` - - Buttons with text that are displayed at the bottom after any action buttons. + +##### Welcome message additional API + +- _Props/Attrs_ + - `title` - string attribute for the primary welcome message + - `subtitle` - string attribute for a secondary welcome message. Naming is aligned with `nimble-dialog` +- _Slots_ + - default slot can be used to provide content below the icon, title, and subtitle. For example, a login button or suggested outbound messages. #### Conversation @@ -347,7 +332,7 @@ A message is simply a `div` which will styled with background / border / rounded ``` -#### Message welcome content +##### Welcome message The template will include an `svg` element to render the image. The image requires different svg contents for dark and light themes (they use different gradient parameters). The gradient content will be specified in a new design token, `messageWelcomeContentGradient`, and the template will read the correct gradient value for the current theme using `messageWelcomeContentGradient.getValueFor()`. From 6cc1b2b69adcb7c88b5b4b8d7844ff62db2395a5 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Fri, 13 Feb 2026 18:33:26 -0600 Subject: [PATCH 10/12] welcome message structure --- packages/spright-components/src/chat/specs/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index ea030f9ddc..186b76119c 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -334,11 +334,7 @@ A message is simply a `div` which will styled with background / border / rounded ##### Welcome message -The template will include an `svg` element to render the image. The image requires different svg contents for dark and light themes (they use different gradient parameters). The gradient content will be specified in a new design token, `messageWelcomeContentGradient`, and the template will read the correct gradient value for the current theme using `messageWelcomeContentGradient.getValueFor()`. - -A `slot` element will be used to host the login button. - -Text content will be placed in `div` elements, conditionally shown if there is no slotted login button. We will use our standard pattern to detect whether there is slotted content (see #2579). +The template will include an `svg` element to render the image. The image requires different svg contents for dark and light themes (they use different gradient parameters). The gradient content will be specified in a new design token. We can use [the existing Blazor implementation](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/Controls/Components/StartPage.razor) and [images](https://dev.azure.com/ni/DevCentral/_git/ASW?path=/Source/MeasurementServices/AiAssistants/NigelLocalService/wwwroot/Images/two-chat-sparkle_green_DarkUI_48x48.svg) for reference. From 20eb5b69c416b83d2ab15cfc18da7eac00fca824 Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Fri, 13 Feb 2026 18:37:40 -0600 Subject: [PATCH 11/12] consistent . --- packages/spright-components/src/chat/specs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 3a9c19cd14..8e67e41dff 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -89,8 +89,8 @@ All end text buttons must meet the following criteria 1. Lays out messages vertically based on their order. 1. Displays a vertical scrollbar if there are more messages than fit in the height allocated to the conversation. 1. Includes a slot to place a toolbar (and its content such as buttons or menu buttons) on top of the conversation. -1. Includes a slot to place an input component below the messages -1. Includes a slot for a legal disclaimer below that +1. Includes a slot to place an input component below the messages. +1. Includes a slot for a legal disclaimer below that. 1. Only appearance of its own is to set a background color. #### Chat input From 71214b923c6cc30e16987007db15796de6a85c9c Mon Sep 17 00:00:00 2001 From: Jesse Attas Date: Fri, 13 Feb 2026 18:38:19 -0600 Subject: [PATCH 12/12] subtitle --- packages/spright-components/src/chat/specs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/spright-components/src/chat/specs/README.md b/packages/spright-components/src/chat/specs/README.md index 8e67e41dff..34527774d1 100644 --- a/packages/spright-components/src/chat/specs/README.md +++ b/packages/spright-components/src/chat/specs/README.md @@ -79,7 +79,7 @@ All end text buttons must meet the following criteria 1. Centered horizontally within a conversation, similar to system messages 1. Displays a fixed image to brand the chat experience -1. Displays client-provided title (e.g. "Welcome to Nigel™ AI") and sub-title (e.g. "Chat below to get started", "Log in to get started") text +1. Displays client-provided title (e.g. "Welcome to Nigel™ AI") and subtitle (e.g. "Chat below to get started", "Log in to get started") text 1. Allows content to be added in the default slot for purposes including: - If the user is not logged in, slot a button or anchor button to launch the external login process - If the user is logged in, slot buttons offering suggested outbound messages @@ -184,7 +184,7 @@ richText.markdown = 'Welcome **Homer**, how can I help?'; ```html - + Login