Spaces:
Running
Running
| # edit_history.py | |
| import difflib | |
| from datetime import datetime | |
| import streamlit as st | |
| def init_edit_history(): | |
| """Ensure edit history list exists in session_state.""" | |
| if "edit_history" not in st.session_state: | |
| st.session_state.edit_history = [] | |
| def add_edit(old_text: str, new_text: str, source: str | None = None): | |
| """ | |
| Append an edit event to history. | |
| - old_text: text before edit | |
| - new_text: text after edit | |
| - source: where this edit came from (e.g., "In-place Chat", "System") | |
| """ | |
| init_edit_history() | |
| st.session_state.edit_history.append( | |
| { | |
| "old": old_text, | |
| "new": new_text, | |
| "source": source, | |
| "ts": datetime.now().strftime("%H:%M:%S"), | |
| } | |
| ) | |
| def _render_diff_html(old: str, new: str) -> str: | |
| """ | |
| Return HTML for a git-style diff: | |
| - removed tokens: red, strikethrough | |
| - added tokens: green | |
| - unchanged: normal | |
| """ | |
| # word-level diff | |
| diff = difflib.ndiff(old.split(), new.split()) | |
| parts: list[str] = [] | |
| for token in diff: | |
| code = token[:2] # "+ ", "- ", or " " | |
| word = token[2:] | |
| if code == "+ ": | |
| # Added text in green | |
| parts.append( | |
| f"<span style='color:#22c55e;'>{word}</span>" | |
| ) | |
| elif code == "- ": | |
| # Removed text in red with strikethrough | |
| parts.append( | |
| f"<span style='color:#ef4444;text-decoration:line-through;'>{word}</span>" | |
| ) | |
| else: | |
| parts.append(word) | |
| return " ".join(parts) | |
| def render_edit_history_sidebar(max_items: int = 20): | |
| """ | |
| Render the edit history inside the sidebar. | |
| Call this inside a sidebar container. | |
| """ | |
| init_edit_history() | |
| with st.expander("Edit history", expanded=True): | |
| history = st.session_state.edit_history | |
| if not history: | |
| st.caption("No edits yet. Edits will appear here.") | |
| return | |
| # Show most recent first | |
| for idx, item in enumerate(reversed(history[-max_items:]), start=1): | |
| header_html = "<div style='margin-bottom:0.15rem;'>" | |
| # Title + meta info | |
| meta_bits = [] | |
| if "ts" in item: | |
| meta_bits.append(item["ts"]) | |
| if item.get("source"): | |
| meta_bits.append(item["source"]) | |
| meta_html = " · ".join(meta_bits) | |
| header_html += f"<strong>Edit {len(history) - idx + 1}</strong>" | |
| if meta_html: | |
| header_html += ( | |
| f" <span style='font-size:0.75rem;color:#9ca3af;'>" | |
| f"{meta_html}</span>" | |
| ) | |
| header_html += "</div>" | |
| st.markdown(header_html, unsafe_allow_html=True) | |
| diff_html = _render_diff_html(item["old"], item["new"]) | |
| st.markdown( | |
| f"<div style='font-size:0.85rem;'>{diff_html}</div>", | |
| unsafe_allow_html=True, | |
| ) | |
| st.markdown( | |
| "<hr style='margin:0.35rem 0;border:none;border-top:1px solid #e5e7eb;'/>", | |
| unsafe_allow_html=True, | |
| ) | |