Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Configurable chat icon #1853

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open

feat: Configurable chat icon #1853

wants to merge 27 commits into from

Conversation

gadenbuie
Copy link
Collaborator

@gadenbuie gadenbuie commented Feb 12, 2025

Adds icon_assistant to chat.ui() and ui.chat_ui() that can take a tag, typically an <svg> (e.g. faicons.icon_svg() or wrapped in ui.HTML()) or an <img>.

image

For example, using faicons:

from shiny import ui

chat_otter = ui.Chat(
    id="chat_otter",
    messages=[
        {
            "content": "Hello! I'm Otter Bot. How can I help you today?",
            "role": "assistant",
        },
    ],
)

with ui.div():
    ui.h2("Otter Bot")
    chat_otter.ui(icon_assistant=faicons.icon_svg("otter"))

Or directly using an image:

chat_shiny = ui.Chat(
    id="chat_shiny",
    messages=[
        {
            "content": "Hello! I'm Shiny Bot. How can I help you today?",
            "role": "assistant",
        },
    ],
)

with ui.div():
    ui.h2("Shiny Bot")
    chat_shiny.ui(
        icon_assistant=ui.img(
            src="https://github.com/rstudio/hex-stickers/raw/refs/heads/main/SVG/shiny.svg"
        )
    )

@gadenbuie gadenbuie marked this pull request as ready for review February 12, 2025 21:29
@cpsievert
Copy link
Collaborator

cpsievert commented Feb 12, 2025

Let's make sure to make the relevant server-side changes to shinychat (before or while merging this)

@cpsievert
Copy link
Collaborator

@gadenbuie since it kinda felt like we'd eventually want to ability to change icons during a chat session, I took a quick stab in fbce8d2. Lmk what you think.

@gadenbuie gadenbuie added this to the Next Release milestone Feb 19, 2025
* `<shiny-chat-container icon-assistant="...html for icon...">`
* `<shiny-chat-message icon="...html for icon...">`
@@ -262,6 +265,7 @@ class ChatInput extends LightElement {
}

class ChatContainer extends LightElement {
@property({ attribute: "icon-assistant" }) iconAssistant = "";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly, but have been using snake_case for other attributes

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was a little confused about this actually because it's icon_assistant in the Tag() call. Are you explicitly forcing snake case for content_type somewhere?

I'd say my preference for kebab case attributes trends toward strong (I didn't care enough to figure out why it's content_type and not content-type, but if I definitely wanted to change it when I saw it).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the question and the proposal here. Btw, since it's probably important context, shiny this behavior when writing tags:

>>> from shiny import ui
>>> ui.div(foo_bar = "baz")
<div foo-bar="baz"></div>
>>> ui.div(fooBar = "baz")
<div fooBar="baz"></div>

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is why I was surprised you have underscores in the attributes. So I used Tag(..., icon_assistant=str(icon_assistant)) in Python, which creates icon-assistant="...html...".

But you're saying you used snake_case for attributes like content_type, which means you had to do something special on the Python side, right?

As far as proposals, I'd prefer we pick one of these choices in order from first to last preference:

  1. kebab-case for attributes
  2. camelCase for attributes if we want to signal that they're reflected attributes
  3. snake_case for attributes if we want to signal that they came from Python

(I could be convinced into 2 or 3 if we have some guidelines for consistency, otherwise I'd much rather all attributes in kebab-case.)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still a bit confused, can we be a bit more specific? AFAIK, I'm using Tag(content_type="") in Python which serializes to <tag content-type=""> in HTML. In the TS, the property name is content_type, but the attribute name is content-type. Are you proposing kebab-case everywhere (even for the TS property names)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the chat messages you pass JSON data (which has content_type) directly from the event to create the message element, so the <shiny-chat-message> (and similar) elements have content_type attributes, although when creating the <shiny-markdown-stream> you then go from content_type to content-type.

Anyway, I've always personally preferred:

  1. Snake case in R/Python, i.e. Tag("shiny-chat-message", content_type="html")
  2. Kebab-case in HTML, i.e. <shiny-chat-message content-type="html">
  3. camelCase in TS/JS, i.e. this.contentType.

But the event-data-direct-to-custom-element approach is easy and it would take a little more work to serialize those elements to JSON with kebab-case names.

Copy link
Collaborator

@cpsievert cpsievert Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I overlooked the message handling aspect 🤦 .

Would it make sense to you if createElement() inside of js/utils/_utils.ts replaced _ with - (similar to what htmltools in Python does)?

Anyways, I don't think I have any strong opinions here and align with your preferences, so feel free to change as you see fit

Copy link
Collaborator

@cpsievert cpsievert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good once suggestions are addressed, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants