Hi, I want to make a copy to clipboard button that has this icon .
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
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.
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='β ' )
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})
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.