Spaces:
Sleeping
Sleeping
Upload css.py
Browse files
css.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import streamlit.components.v1 as components
|
| 3 |
+
import re
|
| 4 |
+
import uuid
|
| 5 |
+
import pandas as pd
|
| 6 |
+
|
| 7 |
+
# ββββββββββββββββββ λ§νμ μμ± ν¨μ
|
| 8 |
+
# μμ μ μ
|
| 9 |
+
PRIMARY_USER = "#e2f6e8"
|
| 10 |
+
PRIMARY_BOT = "#f6f6f6"
|
| 11 |
+
|
| 12 |
+
def render_message(
|
| 13 |
+
message: str,
|
| 14 |
+
sender: str = "bot",
|
| 15 |
+
chips: list[str] | None = None,
|
| 16 |
+
key: str | None = None,
|
| 17 |
+
) -> str | None:
|
| 18 |
+
"""
|
| 19 |
+
- `message` : νμν ν
μ€νΈ (HTML νμ©)
|
| 20 |
+
- `sender` : "user" | "bot"
|
| 21 |
+
- `chips` : λ²νΌ ννλ‘ λ³΄μ¬ μ€ λ¬Έμμ΄ λ¦¬μ€νΈ
|
| 22 |
+
- return : μ¬μ©μκ° ν΄λ¦ν μΉ©(λ¬Έμμ΄) λλ None
|
| 23 |
+
"""
|
| 24 |
+
# 1) λ§νμ κΈ°λ³Έ μμ±
|
| 25 |
+
color = PRIMARY_USER if sender == "user" else PRIMARY_BOT
|
| 26 |
+
align = "right" if sender == "user" else "left"
|
| 27 |
+
|
| 28 |
+
# κ°ν 꼬리ν μ κ±°
|
| 29 |
+
message = str(message).rstrip()
|
| 30 |
+
|
| 31 |
+
# 2) νμ μΆλ ₯
|
| 32 |
+
st.markdown(
|
| 33 |
+
f'''<div style="text-align:{align}; margin:6px 0;">
|
| 34 |
+
<p style = "font-size:13px;"></p>
|
| 35 |
+
<span style="background:{color}; padding:10px 14px; border-radius:12px;
|
| 36 |
+
display:inline-block; max-width:80%; font-size:13px; line-height:1.45;
|
| 37 |
+
word-break:break-word; ">{message}</span>
|
| 38 |
+
</div>''',
|
| 39 |
+
unsafe_allow_html=True,
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
# 3) μΉ© λ²νΌμ΄ μμ κ²½μ°
|
| 43 |
+
if chips:
|
| 44 |
+
prefix = f"{key or 'chips'}_{abs(hash(message))}"
|
| 45 |
+
clicked = render_chip_buttons(chips, key_prefix=prefix)
|
| 46 |
+
return clicked
|
| 47 |
+
return None
|
| 48 |
+
|
| 49 |
+
# ββββββββββββββββββ μΉ©λ²νΌ μμ± ν¨μ
|
| 50 |
+
def render_chip_buttons(options, key_prefix="chip", selected_value=None):
|
| 51 |
+
def slugify(text):
|
| 52 |
+
return re.sub(r"[^a-zA-Z0-9]+", "-", str(text)).strip("-").lower() or "empty"
|
| 53 |
+
session_key = f"{key_prefix}_selected"
|
| 54 |
+
selected_value = st.session_state.get(session_key)
|
| 55 |
+
|
| 56 |
+
# μ€νμΌ μ μ©
|
| 57 |
+
st.markdown(f"""
|
| 58 |
+
<style>
|
| 59 |
+
div[data-testid="stHorizontalBlock"]{{
|
| 60 |
+
display:block !important;
|
| 61 |
+
}}
|
| 62 |
+
button[data-testid="stBaseButton-secondary"] {{
|
| 63 |
+
background-color: white;
|
| 64 |
+
border: 1px solid #e3e8e7;
|
| 65 |
+
border-radius: 20px;
|
| 66 |
+
padding: 6px 14px;
|
| 67 |
+
font-size: 14px;
|
| 68 |
+
cursor: pointer;
|
| 69 |
+
transition: 0.2s ease-in-out;
|
| 70 |
+
margin-bottom: -2px;
|
| 71 |
+
width: 230px;
|
| 72 |
+
text-align:center;
|
| 73 |
+
}}
|
| 74 |
+
|
| 75 |
+
button[data-testid="stBaseButton-secondary"]:hover {{
|
| 76 |
+
background-color: #e8f0ef;
|
| 77 |
+
border-color: #009c75;
|
| 78 |
+
color: #009c75;
|
| 79 |
+
}}
|
| 80 |
+
button[data-testid="baseButton-secondary"][disabled]{{
|
| 81 |
+
background-color: white;
|
| 82 |
+
border-color: #009c75; !important;
|
| 83 |
+
color: #009c75; !important;
|
| 84 |
+
}}
|
| 85 |
+
</style>
|
| 86 |
+
""", unsafe_allow_html=True)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
clicked_val = None
|
| 90 |
+
|
| 91 |
+
#cols = st.columns(len(options))
|
| 92 |
+
for idx, opt in enumerate(options):
|
| 93 |
+
if opt is None or (isinstance(opt, float) and pd.isna(opt)) or str(opt).strip()=="":
|
| 94 |
+
continue
|
| 95 |
+
|
| 96 |
+
is_selected = (opt == selected_value)
|
| 97 |
+
is_refresh_btn = "λ€λ₯Έ μ¬νμ§ λ³΄κΈ°" in str(opt)
|
| 98 |
+
disabled = (opt == selected_value) and not is_refresh_btn
|
| 99 |
+
|
| 100 |
+
label = f"{opt}" if is_selected else opt
|
| 101 |
+
|
| 102 |
+
# stable key
|
| 103 |
+
safe_opt = slugify(opt)
|
| 104 |
+
stable_key = f"{key_prefix}_{idx}_{safe_opt}"
|
| 105 |
+
|
| 106 |
+
if st.button(label, key=stable_key, disabled=disabled):
|
| 107 |
+
clicked_val = opt
|
| 108 |
+
|
| 109 |
+
return clicked_val
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
# ββββββββββββββββββ λ©μμ§ λ¦¬νλ μ΄ ν¨μ
|
| 113 |
+
def replay_log(chat_container=None):
|
| 114 |
+
with chat_container:
|
| 115 |
+
for sender, msg in st.session_state.chat_log:
|
| 116 |
+
render_message(msg, sender=sender)
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
# ββββββββββββββββββ λ©μμ§ λ‘κΉ
&μμ± ν¨μ
|
| 120 |
+
def log_and_render(msg, sender, chat_container=None, key=None, chips=None):
|
| 121 |
+
# μ€λ³΅ λ°©μ§ λ‘μ§
|
| 122 |
+
sent_once = st.session_state.setdefault("sent_once", {})
|
| 123 |
+
if key and sent_once.get(key):
|
| 124 |
+
return
|
| 125 |
+
if key:
|
| 126 |
+
sent_once[key] = True
|
| 127 |
+
if st.session_state.chat_log and st.session_state.chat_log[-1] == (sender, msg):
|
| 128 |
+
return
|
| 129 |
+
|
| 130 |
+
# λ‘κ·Έμ μ μ₯
|
| 131 |
+
st.session_state.chat_log.append((sender, msg))
|
| 132 |
+
|
| 133 |
+
# λ©μμ§ μΆλ ₯
|
| 134 |
+
with chat_container:
|
| 135 |
+
rendered = render_message(msg, sender=sender, chips=chips, key=key)
|
| 136 |
+
|
| 137 |
+
return rendered
|