Spaces:
Running
Running
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +44 -13
src/streamlit_app.py
CHANGED
|
@@ -6,7 +6,7 @@ import json
|
|
| 6 |
from streamlit_cookies_manager import EncryptedCookieManager
|
| 7 |
|
| 8 |
# --- ИНИЦИАЛИЗАЦИЯ ПАМЯТИ ---
|
| 9 |
-
cookies = EncryptedCookieManager(password="
|
| 10 |
if not cookies.ready():
|
| 11 |
st.stop()
|
| 12 |
|
|
@@ -53,14 +53,12 @@ st.markdown("""
|
|
| 53 |
background: radial-gradient(circle at top right, #1a0b2e, #020205);
|
| 54 |
background-attachment: fixed;
|
| 55 |
}
|
| 56 |
-
|
| 57 |
.fixed-header {
|
| 58 |
position: fixed; top: 0; left: 0; width: 100%;
|
| 59 |
z-index: 999; background: rgba(2, 2, 5, 0.85);
|
| 60 |
backdrop-filter: blur(15px); padding: 10px 0;
|
| 61 |
border-bottom: 1px solid rgba(115, 103, 240, 0.2);
|
| 62 |
}
|
| 63 |
-
|
| 64 |
.main-title {
|
| 65 |
font-size: 2.2rem; font-weight: 900; text-align: center;
|
| 66 |
background: linear-gradient(90deg, #00f2fe, #7367f0, #ff00cc, #00f2fe);
|
|
@@ -68,7 +66,6 @@ st.markdown("""
|
|
| 68 |
animation: shine 4s linear infinite; margin: 0;
|
| 69 |
}
|
| 70 |
@keyframes shine { to { background-position: 200% center; } }
|
| 71 |
-
|
| 72 |
[data-testid="stBottom"] {
|
| 73 |
position: fixed !important; bottom: 0px !important;
|
| 74 |
background: rgba(10, 10, 20, 0.98) !important;
|
|
@@ -76,7 +73,6 @@ st.markdown("""
|
|
| 76 |
padding: 10px 5% 40px 5% !important;
|
| 77 |
border-top: 1px solid rgba(115, 103, 240, 0.4);
|
| 78 |
}
|
| 79 |
-
|
| 80 |
.chat-container { margin-top: 100px; }
|
| 81 |
.chat-bubble {
|
| 82 |
padding: 18px; border-radius: 20px; margin-bottom: 15px;
|
|
@@ -84,7 +80,6 @@ st.markdown("""
|
|
| 84 |
color: #f0f0f0; backdrop-filter: blur(10px);
|
| 85 |
}
|
| 86 |
.user-bubble { border-left: 5px solid #ff00cc; background: rgba(255, 0, 204, 0.07); }
|
| 87 |
-
|
| 88 |
footer { visibility: hidden; }
|
| 89 |
</style>
|
| 90 |
""", unsafe_allow_html=True)
|
|
@@ -95,7 +90,7 @@ if not st.session_state.user_email:
|
|
| 95 |
st.markdown(f'<div style="text-align:center; margin-top:35vh;"><a href="{get_google_auth_url()}" target="_self" style="background:#4285F4; color:white; padding:15px 30px; text-decoration:none; border-radius:10px; font-weight:bold;">Войти через Google</a></div>', unsafe_allow_html=True)
|
| 96 |
st.stop()
|
| 97 |
|
| 98 |
-
# --- БД ---
|
| 99 |
u_id = st.session_state.user_email.replace('@','_').replace('.','_')
|
| 100 |
DB_FILE = f"chats_db_{u_id}.json"
|
| 101 |
|
|
@@ -121,32 +116,68 @@ MODELS_CONFIG = {
|
|
| 121 |
|
| 122 |
if "chats" not in st.session_state: st.session_state.chats = load_chats()
|
| 123 |
if "current_chat" not in st.session_state: st.session_state.current_chat = list(st.session_state.chats.keys())[0]
|
|
|
|
| 124 |
|
| 125 |
-
# --- SIDEBAR ---
|
| 126 |
with st.sidebar:
|
| 127 |
st.markdown(f"👤 **{st.session_state.user_name}**")
|
| 128 |
if st.button("🚪 Выйти", use_container_width=True): logout()
|
| 129 |
st.markdown("---")
|
|
|
|
| 130 |
sel_mod = st.selectbox("🤖 Модель:", list(MODELS_CONFIG.keys()))
|
| 131 |
cfg = MODELS_CONFIG[sel_mod]
|
|
|
|
| 132 |
if st.button("➕ Новый чат", use_container_width=True):
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
st.markdown("---")
|
|
|
|
|
|
|
| 135 |
for c_name in list(st.session_state.chats.keys()):
|
| 136 |
-
|
| 137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
|
| 139 |
# --- ЧАТ ---
|
| 140 |
st.markdown('<div class="fixed-header"><p class="main-title">HiperDouble AI</p></div>', unsafe_allow_html=True)
|
| 141 |
|
| 142 |
st.markdown('<div class="chat-container">', unsafe_allow_html=True)
|
| 143 |
if st.session_state.current_chat in st.session_state.chats:
|
| 144 |
-
# Выводим сообщения (новые сверху)
|
| 145 |
for m in reversed(st.session_state.chats[st.session_state.current_chat]):
|
| 146 |
role, style = ("Вы", "user-bubble") if m['role'] == 'user' else ("HiperAi", "")
|
| 147 |
st.markdown(f"<div class='chat-bubble {style}'><b>{role}:</b><br>{m['content']}</div>", unsafe_allow_html=True)
|
| 148 |
|
| 149 |
-
#
|
| 150 |
st.markdown("<div style='height: 180px; width: 100%; pointer-events: none;'></div>", unsafe_allow_html=True)
|
| 151 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 152 |
|
|
|
|
| 6 |
from streamlit_cookies_manager import EncryptedCookieManager
|
| 7 |
|
| 8 |
# --- ИНИЦИАЛИЗАЦИЯ ПАМЯТИ ---
|
| 9 |
+
cookies = EncryptedCookieManager(password="HiperDouble_Full_Control_2026")
|
| 10 |
if not cookies.ready():
|
| 11 |
st.stop()
|
| 12 |
|
|
|
|
| 53 |
background: radial-gradient(circle at top right, #1a0b2e, #020205);
|
| 54 |
background-attachment: fixed;
|
| 55 |
}
|
|
|
|
| 56 |
.fixed-header {
|
| 57 |
position: fixed; top: 0; left: 0; width: 100%;
|
| 58 |
z-index: 999; background: rgba(2, 2, 5, 0.85);
|
| 59 |
backdrop-filter: blur(15px); padding: 10px 0;
|
| 60 |
border-bottom: 1px solid rgba(115, 103, 240, 0.2);
|
| 61 |
}
|
|
|
|
| 62 |
.main-title {
|
| 63 |
font-size: 2.2rem; font-weight: 900; text-align: center;
|
| 64 |
background: linear-gradient(90deg, #00f2fe, #7367f0, #ff00cc, #00f2fe);
|
|
|
|
| 66 |
animation: shine 4s linear infinite; margin: 0;
|
| 67 |
}
|
| 68 |
@keyframes shine { to { background-position: 200% center; } }
|
|
|
|
| 69 |
[data-testid="stBottom"] {
|
| 70 |
position: fixed !important; bottom: 0px !important;
|
| 71 |
background: rgba(10, 10, 20, 0.98) !important;
|
|
|
|
| 73 |
padding: 10px 5% 40px 5% !important;
|
| 74 |
border-top: 1px solid rgba(115, 103, 240, 0.4);
|
| 75 |
}
|
|
|
|
| 76 |
.chat-container { margin-top: 100px; }
|
| 77 |
.chat-bubble {
|
| 78 |
padding: 18px; border-radius: 20px; margin-bottom: 15px;
|
|
|
|
| 80 |
color: #f0f0f0; backdrop-filter: blur(10px);
|
| 81 |
}
|
| 82 |
.user-bubble { border-left: 5px solid #ff00cc; background: rgba(255, 0, 204, 0.07); }
|
|
|
|
| 83 |
footer { visibility: hidden; }
|
| 84 |
</style>
|
| 85 |
""", unsafe_allow_html=True)
|
|
|
|
| 90 |
st.markdown(f'<div style="text-align:center; margin-top:35vh;"><a href="{get_google_auth_url()}" target="_self" style="background:#4285F4; color:white; padding:15px 30px; text-decoration:none; border-radius:10px; font-weight:bold;">Войти через Google</a></div>', unsafe_allow_html=True)
|
| 91 |
st.stop()
|
| 92 |
|
| 93 |
+
# --- ЛОГИКА БД ---
|
| 94 |
u_id = st.session_state.user_email.replace('@','_').replace('.','_')
|
| 95 |
DB_FILE = f"chats_db_{u_id}.json"
|
| 96 |
|
|
|
|
| 116 |
|
| 117 |
if "chats" not in st.session_state: st.session_state.chats = load_chats()
|
| 118 |
if "current_chat" not in st.session_state: st.session_state.current_chat = list(st.session_state.chats.keys())[0]
|
| 119 |
+
if "rename_mode" not in st.session_state: st.session_state.rename_mode = None
|
| 120 |
|
| 121 |
+
# --- SIDEBAR (ПОЛНЫЙ ФУНКЦИОНАЛ) ---
|
| 122 |
with st.sidebar:
|
| 123 |
st.markdown(f"👤 **{st.session_state.user_name}**")
|
| 124 |
if st.button("🚪 Выйти", use_container_width=True): logout()
|
| 125 |
st.markdown("---")
|
| 126 |
+
|
| 127 |
sel_mod = st.selectbox("🤖 Модель:", list(MODELS_CONFIG.keys()))
|
| 128 |
cfg = MODELS_CONFIG[sel_mod]
|
| 129 |
+
|
| 130 |
if st.button("➕ Новый чат", use_container_width=True):
|
| 131 |
+
new_n = f"Чат {len(st.session_state.chats)+1}"
|
| 132 |
+
st.session_state.chats[new_n] = []
|
| 133 |
+
st.session_state.current_chat = new_n
|
| 134 |
+
save_chats(st.session_state.chats); st.rerun()
|
| 135 |
+
|
| 136 |
+
if st.button("🗑️ Очистить текущий", use_container_width=True):
|
| 137 |
+
st.session_state.chats[st.session_state.current_chat] = []
|
| 138 |
+
save_chats(st.session_state.chats); st.rerun()
|
| 139 |
+
|
| 140 |
st.markdown("---")
|
| 141 |
+
st.write("📂 Ваши чаты:")
|
| 142 |
+
|
| 143 |
for c_name in list(st.session_state.chats.keys()):
|
| 144 |
+
col_main, col_edit, col_del = st.columns([0.6, 0.2, 0.2])
|
| 145 |
+
|
| 146 |
+
# Кнопка выбора чата
|
| 147 |
+
if col_main.button(f"💬 {c_name[:12]}", key=f"sel_{c_name}", use_container_width=True):
|
| 148 |
+
st.session_state.current_chat = c_name
|
| 149 |
+
st.rerun()
|
| 150 |
+
|
| 151 |
+
# Кнопка переименования
|
| 152 |
+
if col_edit.button("✏️", key=f"ed_{c_name}"):
|
| 153 |
+
st.session_state.rename_mode = c_name
|
| 154 |
+
|
| 155 |
+
# Кнопка удаления
|
| 156 |
+
if col_del.button("❌", key=f"del_{c_name}"):
|
| 157 |
+
if len(st.session_state.chats) > 1:
|
| 158 |
+
del st.session_state.chats[c_name]
|
| 159 |
+
st.session_state.current_chat = list(st.session_state.chats.keys())[0]
|
| 160 |
+
save_chats(st.session_state.chats); st.rerun()
|
| 161 |
+
|
| 162 |
+
# Поле для переименования, если выбран этот чат
|
| 163 |
+
if st.session_state.rename_mode == c_name:
|
| 164 |
+
new_title = st.text_input("Новое имя:", c_name, key=f"input_{c_name}")
|
| 165 |
+
if st.button("OK", key=f"ok_{c_name}"):
|
| 166 |
+
st.session_state.chats[new_title] = st.session_state.chats.pop(c_name)
|
| 167 |
+
st.session_state.current_chat = new_title
|
| 168 |
+
st.session_state.rename_mode = None
|
| 169 |
+
save_chats(st.session_state.chats); st.rerun()
|
| 170 |
|
| 171 |
# --- ЧАТ ---
|
| 172 |
st.markdown('<div class="fixed-header"><p class="main-title">HiperDouble AI</p></div>', unsafe_allow_html=True)
|
| 173 |
|
| 174 |
st.markdown('<div class="chat-container">', unsafe_allow_html=True)
|
| 175 |
if st.session_state.current_chat in st.session_state.chats:
|
|
|
|
| 176 |
for m in reversed(st.session_state.chats[st.session_state.current_chat]):
|
| 177 |
role, style = ("Вы", "user-bubble") if m['role'] == 'user' else ("HiperAi", "")
|
| 178 |
st.markdown(f"<div class='chat-bubble {style}'><b>{role}:</b><br>{m['content']}</div>", unsafe_allow_html=True)
|
| 179 |
|
| 180 |
+
# Невидимый блок (180px)
|
| 181 |
st.markdown("<div style='height: 180px; width: 100%; pointer-events: none;'></div>", unsafe_allow_html=True)
|
| 182 |
st.markdown('</div>', unsafe_allow_html=True)
|
| 183 |
|