Ghosting of container in streamlit

Below is the most contrived example I could come up with of a streamlit app that will cause “ghosting” of a screen. It came from our internal app we have been developing for a few weeks now and is a bit of a show stopper. Run the app locally and click on any link. A new tab will open. If you go back to streamlit app’s tab, you will notice it has changed positions. Type anything inside the chat box and observe a double-screen.

I am running streamlit-nightly and it makes no difference. At first, we thought that this was the issue (filed in Github - there is a fix for it on Mar 8) - but it is not…

Any ideas of how to work around this are welcome!

import time

import streamlit as st
from annotated_text import annotated_text
from streamlit.components.v1 import html

if "messages" not in st.session_state:
    st.session_state["messages"] = [
        {
            "role": "assistant",
            "content": "Hello! Choose one search engine today!",
        }
    ]


def open_page(url):
    open_script = """
        <script type="text/javascript">
            window.open('%s', '_blank').focus();
        </script>
    """ % (
        url
    )
    # print(open_script)
    html(open_script)


def display_output(message_container):
    with message_container:
        cnt = 0
        for src, name, url in [
            ("Google", "google search", "https://google.com"),
            ("Yahoo", "yahoo search", "https://yahoo.com"),
            ("Bing", "bing search", "https://bing.com"),
            ("DuckDuckGo", "duck search", "https://duckduckgo.com"),
        ]:
            with st.container():
                col1, col2 = st.columns([9, 1])
                with col1:
                    st.button(
                        f"search engine: {src}, url={url}",
                        on_click=open_page,
                        args=(url,),
                        key=f"btn_{cnt}",
                    )
            cnt += 1


col1, col2, col3 = st.columns([0.2, 0.6, 0.2])

with col2:
    message_container = st.container(height=600)
    messages = st.session_state.messages

    display_output(message_container)

    if prompt := st.chat_input("Your message.."):
        messages.append({"role": "user", "content": prompt})
        message_container.chat_message("user").markdown(f"You wrote: {prompt}")

        with message_container.chat_message("assistant"):
            time.sleep(10)
1 Like

Filed a bug report here: Screen ghosting with chat_input inside container plus multiple columns · Issue #8480 · streamlit/streamlit · GitHub

1 Like

Did you try to use link_button ?

import time
import streamlit as st

if "messages" not in st.session_state:
    st.session_state["messages"] = [
        {
            "role": "assistant",
            "content": "Hello! Choose one search engine today!",
        }
    ]

def display_output():
    for src, name, url in [
        ("Google", "google search", "https://google.com"),
        ("Yahoo", "yahoo search", "https://yahoo.com"),
        ("Bing", "bing search", "https://bing.com"),
        ("DuckDuckGo", "duck search", "https://duckduckgo.com"),
    ]:
        st.link_button(src, url)


col1, col2, col3 = st.columns([0.2, 0.6, 0.2])

with col2:
    message_container = st.container(height=600)
    messages = st.session_state.messages

    with message_container:
        st.chat_message("assistant").markdown("Hello! Choose one search engine today!")
        display_output()

    if prompt := st.chat_input("Your message.."):
        messages.append({"role": "user", "content": prompt})
        message_container.chat_message("user").markdown(f"You wrote: {prompt}")

        with message_container.chat_message("assistant"):
            time.sleep(10)
2 Likes

Yes but I need an actual button because I need to know when someone clicked on the link. The contrived example was for bug reporting purposes, the actual code I need, needs to keep track of users who click on the links.

That worked for me:

import time
import streamlit as st
import webbrowser

if "messages" not in st.session_state:
    st.session_state["messages"] = [
        {
            "role": "assistant",
            "content": "Hello! Choose one search engine today!",
        }
    ]

def display_output():
    for src, name, url in [
        ("Google", "google search", "https://google.com"),
        ("Yahoo", "yahoo search", "https://yahoo.com"),
        ("Bing", "bing search", "https://bing.com"),
        ("DuckDuckGo", "duck search", "https://duckduckgo.com"),
    ]:
        if st.button(src):
            print("I register the click to open", url)
            webbrowser.open(url, new=2)

col1, col2, col3 = st.columns([0.2, 0.6, 0.2])

with col2:
    message_container = st.container(height=600)
    messages = st.session_state.messages

    with message_container:
        st.chat_message("assistant").markdown("Hello! Choose one search engine today!")
        display_output()

    if prompt := st.chat_input("Your message.."):
        messages.append({"role": "user", "content": prompt})
        message_container.chat_message("user").markdown(f"You wrote: {prompt}")

        with message_container.chat_message("assistant"):
            time.sleep(10)
1 Like