Copy to clipboard using st.markdown

Summary

Hi, I want to make a copy to clipboard button that has this icon :clipboard:.

Steps to reproduce

Code snippet:

import streamlit as st
import random
import time
import clipboard

st.title("Simple chat")

# Initialize chat history
if "messages" not in st.session_state:
    st.session_state.messages = []

# Display chat messages from history on app rerun
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# Accept user input
if prompt := st.chat_input("What is up?"):
    # Add user message to chat history
    st.session_state.messages.append({"role": "user", "content": prompt})
    # Display user message in chat message container
    with st.chat_message("user"):
        st.markdown(prompt)

    # Display assistant response in chat message container
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        full_response = ""
        assistant_response = random.choice(
            [
                "Hello there! How can I assist you today?",
                "Hi, human! Is there anything I can help you with?",
                "Do you need help?",
            ]
        )
        # Simulate stream of response with milliseconds delay
        for chunk in assistant_response.split():
            full_response += chunk + " "
            time.sleep(0.05)
            # Add a blinking cursor to simulate typing
            message_placeholder.markdown(full_response + "β–Œ")
        message_placeholder.markdown(full_response)
    # Add assistant response to chat history
    with st.button("πŸ“‹", key="copy"):
        clipboard.copy(full_response)
    st.session_state.messages.append({"role": "assistant", "content": full_response})

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

Expected behavior:

For every message shown of the assistant, a copy to clipboard button with the specificed icon should be displayed. When i press it, no matter if an answer is being generated meanwhile or not, I should be able to copy the content of the corresponding answer.

Actual behavior:

If I press on the first message copy button, everything disappears. I also get this error:
AttributeError: enter

Hi @Erica,

Thank you for posting!

  1. Here’s a modified version of your code that works but with the following limitation:
  • The clipboard disappears once you copy the message and when you generate a new response

I have also added st.toast to display a notification when a user copies a response.

Screen Recording 2023-08-30 at 1_scaling-0.5_fps-20_speed-5.0_duration-0-13

Code:

import streamlit as st
import random
import clipboard

st.title("Simple Chat")

def on_copy_click(text):
    st.session_state.copied.append(text)
    clipboard.copy(text)

if "messages" not in st.session_state:
    st.session_state.messages = []

if "copied" not in st.session_state: 
    st.session_state.copied = []
    
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])
        
if prompt := st.chat_input("What is up?"):

    st.session_state.messages.append({"role": "user", "content": prompt})
    
    with st.chat_message("user"):
        st.markdown(prompt)

    assistant_response = random.choice([
        "Hello there!",
        "Hi human!", 
        "Need help?",
        """st.title("Simple chat")"""
    ])
    
    full_response = ""
    for chunk in assistant_response.split():
        full_response += chunk + " "
        
    with st.chat_message("assistant"):
        st.markdown(full_response)
        
        if len(st.session_state.copied) > 5:
            st.session_state.copied.pop(0)
            
        st.button("πŸ“‹", on_click=on_copy_click, args=(full_response,))

    st.session_state.messages.append({"role": "assistant", "content": full_response})
    
for text in st.session_state.copied:
    st.toast(f"Copied to clipboard: {text}", icon='βœ…' )
  1. Another alternative - not the cleanest - is to use the st.code on the bot responses like in the code below:
import streamlit as st
import random
import time
import clipboard

st.title("Simple chat")

# Initialize chat history
if "messages" not in st.session_state:
    st.session_state.messages = []

# Display chat messages from history on app rerun
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.code(message["content"])

# Accept user input
if prompt := st.chat_input("What is up?"):
    # Add user message to chat history
    st.session_state.messages.append({"role": "user", "content": prompt})
    # Display user message in chat message container
    with st.chat_message("user"):
        st.markdown(prompt)

    # Display assistant response in chat message container
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        full_response = ""
        assistant_response = random.choice(
            [
                "Hello there! How can I assist you today?",
                "Hi, human! Is there anything I can help you with?",
                "Do you need help?",
            ]
        )
        # Simulate stream of response with milliseconds delay
        for chunk in assistant_response.split():
            full_response += chunk + " "
            time.sleep(0.05)
            # Add a blinking cursor to simulate typing
            message_placeholder.code(full_response + "β–Œ")
        message_placeholder.code(full_response)
    # Add assistant response to chat history
    st.session_state.messages.append({"role": "assistant", "content": full_response})

I hope this helps!

2 Likes

Hello @tonykip, thank you for the reply. I have tried to modify a bit the code and i reached to this. It adds the copy to clipboard button, which also works but there is only one problem. If i press a previous copy button when the prompt is being generated, my current prompt will be printed twice:

import streamlit as st
import random
import time
import clipboard

st.title("Simple chat")
def on_copy_click(text):
    # st.session_state.copied.append(text)
    clipboard.copy(text)
# Initialize chat history
if "messages" not in st.session_state:
    st.session_state.messages = []
count = 0
# Display chat messages from history on app rerun
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])
        if message["role"] == "assistant":
            st.button("πŸ“‹", on_click=on_copy_click, args=(message["content"],), key=count)
            count += 1

# Accept user input
if prompt := st.chat_input("What is up?"):
    # Add user message to chat history
    st.session_state.messages.append({"role": "user", "content": prompt})
    # Display user message in chat message container
    with st.chat_message("user"):
        st.markdown(prompt)

    # Display assistant response in chat message container
    with st.chat_message("assistant"):
        message_placeholder = st.empty()
        full_response = ""
        assistant_response = random.choice(
            [
                "Hello there! How can I assist you today?",
                "Hi, human! Is there anything I can help you with?",
                "Do you need help?",
            ]
        )
        # Simulate stream of response with milliseconds delay
        for chunk in assistant_response.split():
            full_response += chunk + " "
            time.sleep(0.05)
            # Add a blinking cursor to simulate typing
            message_placeholder.markdown(full_response + "β–Œ")
        message_placeholder.markdown(full_response)
        st.session_state.messages.append({"role": "assistant", "content": full_response})
        st.button("πŸ“‹", on_click=on_copy_click, args=(full_response,))
    # Add assistant response to chat history

Can you do a screen recording of this behavior? Aalso, if you click on a button on a Streamlit app while some other process is running, it will be cancelled since the entire page gets rerun upon interacting with the copy button.

The solution to my problem was provided here: St.code on multiple lines - #11 by Erica

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.