#2832·taipy

[Refactor] Chat component improvements

Author: arcanaxionCreated Mar 18, 2026Updated Jul 6, 2026
Labels📈 ImprovementGUI: Front-End

Description

This issue is a collection of improvements that can be made to the chat component.

In the following application, the chat component initially has its input box directly below the button. As the user sends more messages, the input box expands downwards, and the page is scrolled down further.

main.py

python
import time

import requests
import taipy as tp
import taipy.gui.builder as tgb
from taipy.gui import Gui, State, invoke_long_callback, navigate, notify

message_list = []
user_list = ["Human", "Assistant"]
sender_id = user_list[0]

is_chat_active = True
is_show_chat = True


def busy_wait(seconds: int):
    start_time = time.time()
    while time.time() - start_time < seconds:
        pass


def get_assistant_message(user_message: str) -> list[str]:
    url = "https://baconipsum.com/api/"
    params = {"type": "meat-and-filler", "sentences": 3, "format": "json"}
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        return response.json()
    except Exception as e:
        return [f"Error fetching bacon ipsum: {e}"]


def stream_assistant_message(user_message: str):
    accumulated_message = ""
    for sentence in get_assistant_message(user_message):
        for word in sentence.split():
            accumulated_message += word + " "
            busy_wait(0.2)
            # time.sleep(0.2)
            print("Yielding:", accumulated_message)
            yield accumulated_message


def update_message_list(state: State, assistant_message_id: str, assistant_message: str):
    message_list = state.message_list
    for i in reversed(range(len(message_list))):
        if message_list[i][0] == assistant_message_id:
            message_list[i][1] = assistant_message
            break
    state.message_list = message_list


def send_chat_message(state, var_name: str, payload: dict):
    state.is_chat_active = False
    try:
        (_, _, message, sender_id) = payload.get("args", [])
        assistant_message_id = str(len(state.message_list) + 1)
        message_list = state.message_list + [
            [str(len(state.message_list)), message, sender_id],
            [assistant_message_id, "", state.user_list[1]],
        ]
        state.message_list = message_list

        for message in stream_assistant_message(message):
            update_message_list(state, assistant_message_id, message)
    finally:
        state.is_chat_active = True


with tgb.Page() as root_page:
    tgb.toggle(theme=True)
    with tgb.part(class_name="container chat-page-layout"):
        tgb.text("# Talk to **Bacon Ipsum**", mode="md")
        tgb.toggle("{is_show_chat}", label="Show Chat Component")
        tgb.button("Notify", on_action=lambda state: notify(state, "S", "Button clicked"))
        with tgb.part(class_name="chat-page", render="{is_show_chat}"):
            tgb.chat(
                "{message_list}",
                users="{user_list}",
                sender_id="{sender_id}",
                active="{is_chat_active}",
                on_action=send_chat_message,
            )

pages = {"/": root_page}

if __name__ == "__main__":
    gui = Gui(pages=pages)
    run_properties = {
        # "server_config": {"socketio": {"ping_interval": 0.1}},
    }
    gui.run(run_browser=False, **run_properties)

Image

Improvements

1, Allow option for input box to be in a fixed location (bottom of the screen?). I.e. same predictable place when chat is empty or when very full 2. Make chat component look more modern. The grey background looks dated 3. Pretty way to inform the user that there is some incoming message in the works (e.g. animation of "Jane is typing...", "Assistant is thinking...") 4. Allow chart/image/html components to be displayed to support LLM multi modal outputs

The following css file kind of addresses points 1 and 2 but requires further study:

main.css

css
strong,
b {
    font-weight: bold;
    color: var(--color-primary);
}

/* Height anchor: fills the viewport inside #root's margins.
   Uses a second unique class so other pages using .container are unaffected. */
.chat-page-layout {
    height: calc(100dvh - 2 * var(--root-margin));
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* .chat-page grows to fill remaining space after heading/toggles/button */
.chat-page-layout .chat-page {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* Taipy inserts one wrapper div between the part and the component */
.chat-page-layout .chat-page > div {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.taipy-chat {
    display: flex !important;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

/* Messages area scrolls; input stays at bottom */
.taipy-chat > .MuiGrid2-root.MuiGrid2-container {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
}

/* --- Chat theme --- */

/* Replace the hardcoded MUI gray with the theme background.
   --sent-bg is defined here so bubble and tip arrow always use the exact same value. */
.taipy-chat {
    background-color: var(--color-background) !important;
    box-shadow: none !important;
    --sent-bg: color-mix(in srgb, var(--color-primary) 12%, var(--color-paper));
}

/* Sent bubbles: soft primary tint so they stand out from the page background */
.taipy-chat .taipy-chatmarkdown {
    background-color: var(--sent-bg) !important;
    box-shadow: none !important;
}

/* Received bubbles: clean paper surface with a subtle shadow for depth */
.taipy-chat .taipy-chat-markdown {
    background-color: var(--color-paper) !important;
    box-shadow: var(--box-shadow) !important;
}

/* Sender name label above received messages */
.taipy-chat .taipy-chat-received .MuiBox-root:first-child {
    color: color-mix(in srgb, var(--color-contrast) 60%, transparent);
    font-size: var(--font-size-small);
}

/* Input area: transparent so the floating label is not obscured */
.taipy-chat .taipy-chat-input .MuiOutlinedInput-root {
    background-color: transparent;
}

/* Sent bubble tip: references the same token as the bubble so they always match */
.taipy-chat .taipy-chat-sent > div::before {
    border-top-color: var(--sent-bg) !important;
}

Code of Conduct

  • I have checked the existing issues to avoid duplicates.
  • I am willing to work on this issue (optional)

✅ Acceptance Criteria

  • The refactored code maintains existing functionality without breaking changes.
  • Any new code is covered by unit tests.
  • Code coverage remains at least 90%.
  • Performance improvements are documented, if applicable.