diff --git a/app.py b/app.py index 1d7dec9..a1f895f 100644 --- a/app.py +++ b/app.py @@ -64,8 +64,8 @@ async def respond(message, history, show_thinking, user_id, use_tts, run_ids, im display_msg = f"πŸ–ΌοΈ [이미지 첨뢀]\n{message}" if message.strip() else "πŸ–ΌοΈ [이미지 첨뢀]" history.append({"role": "user", "content": display_msg}) history.append({"role": "assistant", "content": ""}) - # 첫 λ©”μ‹œμ§€ β†’ μ›°μ»΄ λ·° 숨기고 챗봇 컬럼 ν‘œμ‹œ (D4-2) - yield history, "", None, run_ids, "", "", None, gr.update(visible=False), gr.update(visible=True) + # 첫 λ©”μ‹œμ§€ β†’ μ›°μ»΄ λ·° 숨기고 챗봇 컬럼 ν‘œμ‹œ + λ‘œλ”© 인디케이터 + yield history, "", None, run_ids, _loading_html(), "", None, gr.update(visible=False), gr.update(visible=True) collected_run_id = None tts_text = "" @@ -248,11 +248,29 @@ def _last_line(text: str) -> str: return lines[-1] if lines else text.strip() +def _loading_html(label: str = "μœ¨λ΄‡μ΄ μƒκ°ν•˜κ³  μžˆμ–΄μš”") -> str: + """Gemini/ChatGPT μŠ€νƒ€μΌ 3-dot λ°”μš΄μŠ€ λ‘œλ”© 인디케이터""" + return ( + f'
' + f'
' + f'' + f'
' + f'{_html.escape(label)}' + f'
' + ) + + def _live_html(text: str) -> str: return ( f'
' - f'⏳ 뢄석 쀑...' - f'
{_html.escape(text)} β–Œ
' + f'
' + f'
' + f'' + f'
' + f'뢄석 쀑...' + f'
' + f'
{_html.escape(text)}
' f'
' ) @@ -267,11 +285,7 @@ def _thinking_html(text: str) -> str: def _status_html(status: str) -> str: - return ( - f'
' - f'πŸ€” {_html.escape(status)}' - f'
' - ) + return _loading_html(status) def _sources_html(sources: list) -> str: @@ -463,6 +477,20 @@ _JS = """ _CUSTOM_CSS = """ footer { display: none !important; } +/* ── λ‘œλ”© 인디케이터 (Gemini/ChatGPT 3-dot λ°”μš΄μŠ€) ── */ +.loading-dots { display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0; } +.loading-dots .dot { + width: 8px; height: 8px; border-radius: 50%; + background: linear-gradient(135deg, #3b82f6 0%, #818cf8 100%); + animation: dot-bounce 1.4s ease-in-out infinite; +} +.loading-dots .dot:nth-child(2) { animation-delay: .18s; } +.loading-dots .dot:nth-child(3) { animation-delay: .36s; } +@keyframes dot-bounce { + 0%, 60%, 100% { transform: translateY(0) scale(0.65); opacity: 0.35; } + 30% { transform: translateY(-7px) scale(1.05); opacity: 1; } +} + /* 슀트리밍 쀑 νŒŒλž€ ν…Œλ‘λ¦¬ 제거 (.generating ν΄λž˜μŠ€κ°€ 원인) */ .wrap.generating { border-color: var(--border-color-primary) !important; @@ -726,7 +754,7 @@ _THEME = gr.themes.Soft( # ── UI ꡬ성 ─────────────────────────────────────────────────────── -with gr.Blocks(title="μœ¨λ΄‡", css=_CUSTOM_CSS, theme=_THEME, js=_JS) as demo: +with gr.Blocks(title="μœ¨λ΄‡") as demo: user_state = gr.State(DEFAULT_USER) run_ids_state = gr.State([]) @@ -925,4 +953,7 @@ if __name__ == "__main__": demo.launch( server_name=container.config().server_host, server_port=container.config().server_port, + theme=_THEME, + css=_CUSTOM_CSS, + js=_JS, )