Spaces:
Running
Running
| # streamlit_app.py | |
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| from pathlib import Path | |
| import uuid | |
| from cards import ( | |
| chat_card, | |
| inplace_chat_card | |
| ) | |
| # ---------- Global state init ---------- | |
| if "init" not in st.session_state: | |
| st.session_state.init = True | |
| st.session_state.conversations = {} # {chat_id: {"title": str, "messages": [...]}} | |
| st.session_state.current_chat_id = None # which conversation is open | |
| def create_new_conversation(): | |
| chat_id = str(uuid.uuid4()) | |
| st.session_state.conversations[chat_id] = { | |
| "title": "New chat", | |
| "messages": [], | |
| "editable": False, | |
| "prev_text": "", | |
| "edited_text": "", | |
| "original_user_prompt": "", | |
| "edit_history": [] | |
| } | |
| st.session_state.current_chat_id = chat_id | |
| # ensure at least one conversation exists | |
| if not st.session_state.conversations: | |
| create_new_conversation() | |
| # Fix code block font rendering - force monospace | |
| st.markdown(""" | |
| <style> | |
| code, pre, pre code { | |
| font-family: 'Space Mono', monospace !important; | |
| font-size: 0.95rem !important; | |
| line-height: 1.4 !important; | |
| font-weight: 400 !important; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| pages = [ | |
| st.Page( | |
| "inplace_chat.py", | |
| title="In-place Feedback Chat", | |
| icon=":material/edit_document:" | |
| ), | |
| st.Page( | |
| "chat.py", | |
| title="Chat", | |
| icon=":material/chat:" | |
| ) | |
| ] | |
| page = st.navigation(pages) | |
| # ---------- Sidebar: ChatGPT-style history ---------- | |
| with st.sidebar: | |
| # new chat button | |
| st.write("This is a simple demo for CSED499 Inplace feedback.") | |
| st.button( | |
| "+ New chat", | |
| use_container_width=True, | |
| on_click=create_new_conversation, | |
| ) | |
| st.markdown("---") | |
| st.markdown("### Chat History") | |
| # Add custom CSS for hover effect on chat history items | |
| st.markdown(""" | |
| <style> | |
| /* Chat history item buttons */ | |
| .stButton > button[kind="secondary"] { | |
| background-color: transparent; | |
| border: none; | |
| color: inherit; | |
| padding: 8px 12px; | |
| text-align: left; | |
| width: 100%; | |
| border-radius: 8px; | |
| transition: background-color 0.2s; | |
| justify-content: flex-start; | |
| } | |
| .stButton > button[kind="secondary"]:hover { | |
| background-color: rgba(128, 128, 128, 0.2); | |
| } | |
| .stButton > button[kind="secondary"]:focus { | |
| box-shadow: none; | |
| } | |
| /* Delete button styling */ | |
| .stButton > button[kind="secondary"].delete-btn { | |
| width: auto; | |
| padding: 4px 8px; | |
| color: rgba(128, 128, 128, 0.6); | |
| } | |
| .stButton > button[kind="secondary"].delete-btn:hover { | |
| color: rgba(128, 128, 128, 1); | |
| background-color: rgba(128, 128, 128, 0.15); | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # list existing conversations as clickable text | |
| for chat_id, conv in st.session_state.conversations.items(): | |
| # truncate title like ChatGPT | |
| label = conv["title"] | |
| if len(label) > 35: | |
| label = label[:35] + "…" | |
| # Create columns for chat item and delete button | |
| col_chat, col_delete = st.columns([0.85, 0.15]) | |
| with col_chat: | |
| # highlight current chat with a marker | |
| if chat_id == st.session_state.current_chat_id: | |
| st.markdown(f"**▶ {label}**") | |
| else: | |
| # Make entire text clickable with hover effect | |
| if st.button(label, key=f"chat-{chat_id}", type="secondary", use_container_width=True): | |
| st.session_state.current_chat_id = chat_id | |
| st.rerun() | |
| with col_delete: | |
| # Delete button - only show for non-active chats or show for all | |
| if st.button("✕", key=f"delete-{chat_id}", type="secondary", help="Delete chat"): | |
| # If deleting current chat, switch to another one first | |
| if chat_id == st.session_state.current_chat_id: | |
| remaining = [cid for cid in st.session_state.conversations.keys() if cid != chat_id] | |
| if remaining: | |
| st.session_state.current_chat_id = remaining[0] | |
| else: | |
| st.session_state.current_chat_id = None | |
| # Delete the conversation | |
| del st.session_state.conversations[chat_id] | |
| st.rerun() | |
| # run selected page (after sidebar is defined) | |
| page.run() | |
| # === Edit History Panel === | |
| st.sidebar.markdown("### Edit History") | |
| if st.session_state.current_chat_id: | |
| current_conv = st.session_state.conversations.get(st.session_state.current_chat_id, {}) | |
| edit_history = current_conv.get("edit_history", []) | |
| if edit_history: | |
| st.sidebar.markdown(""" | |
| <style> | |
| .diff-container { | |
| font-family: 'Space Mono', monospace; | |
| font-size: 0.85em; | |
| line-height: 1.4; | |
| margin: 8px 0; | |
| padding: 8px; | |
| background-color: rgba(128, 128, 128, 0.05); | |
| border-radius: 4px; | |
| } | |
| .diff-delete { | |
| background-color: rgba(255, 0, 0, 0.15); | |
| color: #d73a49; | |
| text-decoration: line-through; | |
| padding: 2px 4px; | |
| border-radius: 2px; | |
| } | |
| .diff-insert { | |
| background-color: rgba(0, 255, 0, 0.15); | |
| color: #22863a; | |
| padding: 2px 4px; | |
| border-radius: 2px; | |
| } | |
| .diff-equal { | |
| color: inherit; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| for idx, edit in enumerate(reversed(edit_history)): | |
| edit_num = len(edit_history) - idx | |
| # Each edit as a collapsible expander | |
| with st.sidebar.expander(f"Edit #{edit_num} at {edit['timestamp']}", expanded=False): | |
| # Build the diff HTML | |
| diff_html = "<div class='diff-container'>" | |
| for chunk in edit.get('diff_chunks', []): | |
| text = chunk['text'].replace('<', '<').replace('>', '>') | |
| if chunk['tag'] == 'delete': | |
| diff_html += f"<span class='diff-delete'>{text}</span>" | |
| elif chunk['tag'] == 'insert': | |
| diff_html += f"<span class='diff-insert'>{text}</span>" | |
| else: # equal | |
| # Truncate long unchanged sections | |
| if len(text) > 100: | |
| text = text[:50] + "..." + text[-50:] | |
| diff_html += f"<span class='diff-equal'>{text}</span>" | |
| diff_html += "</div>" | |
| st.markdown(diff_html, unsafe_allow_html=True) | |
| else: | |
| st.sidebar.write("No edits yet in this conversation.") | |
| else: | |
| st.sidebar.write("No conversation selected.") | |
| # st.markdown("---") | |
| st.sidebar.caption( | |
| "This app uses [Space Grotesk](https://fonts.google.com/specimen/Space+Grotesk) " | |
| "and [Space Mono](https://fonts.google.com/specimen/Space+Mono) fonts." | |
| ) | |