posit-dev / py-shiny

Shiny for Python
https://shiny.posit.co/py/
MIT License
1.32k stars 82 forks source link

fix(chat): Disable chat input when sending input #1781

Open gadenbuie opened 4 days ago

gadenbuie commented 4 days ago

While reading through the code (and debugging something else I was working on), I noticed that we globally disable the input when setting the input value to an empty string.

This initially struck me as a bug – we'd want to disable the send input button but not the whole input – until I realized that it's part of the input/response lifecycle and we're expecting the input to be re-enabled when the next response happens.

This PR updates the code to explicitly set this.disabled = true in ChatInput.#sendInput() along with a note about the purpose and how it's unset.

While here, I also realized that if the user starts typing in the input while waiting for a response, but the user finishes before the response comes back, the input will be enabled but the send button won't. I added logic to recheck the button state when the input disabled attributes changes.

import asyncio

from shiny import App, ui

app_ui = ui.page_fillable(
    ui.panel_title("Hello Shiny Chat"),
    ui.chat_ui("chat"),
    fillable_mobile=True,
)

# Create a welcome message
welcome = ui.markdown(
    """
    Hi! This is a simple Shiny `Chat` UI. Enter a message below and I will
    simply repeat it back to you. For more examples, see this
    [folder of examples](https://github.com/posit-dev/py-shiny/tree/main/examples/chat).
    """
)

def server(input, output, session):
    chat = ui.Chat(id="chat", messages=[welcome])

    # Define a callback to run when the user submits a message
    @chat.on_user_submit
    async def _():
        # Get the user's input
        user = chat.user_input()
        # Append a response to the chat
        await asyncio.sleep(5)
        await chat.append_message(f"You said: {user}")

app = App(app_ui, server)