fix: replace example chip gr.HTML+onclick with gr.Button for reliable input fill

gr.HTML inline onclick cannot update Gradio 6 Svelte component state.
Replace _example_chips_html() with gr.Button per chip wired to msg_box
via Gradio event system. CSS updated from .example-chip to .example-chip-btn.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 17:13:36 +09:00
co-authored by Claude Sonnet 4.6
parent f987df5da5
commit a456650bc8
+31 -33
View File
@@ -318,13 +318,6 @@ _EXAMPLE_QUESTIONS = [
] ]
def _example_chips_html() -> str:
"""D4-4: 예시 질문 chip 카드"""
chips = "".join(
f'<button class="example-chip" onclick="fillInput({repr(q)})">{_html.escape(q)}</button>'
for q in _EXAMPLE_QUESTIONS
)
return f'<div id="example-chips" class="example-chips-wrap">{chips}</div>'
# ── JavaScript (D3 + D4) ────────────────────────────────────────── # ── JavaScript (D3 + D4) ──────────────────────────────────────────
@@ -364,14 +357,10 @@ _JS = """
if (el) el.classList.add('nav-active'); if (el) el.classList.add('nav-active');
}; };
// D4-3/4: 예시 질문 chip 클릭 → 입력창 채우기 // D4-3: 입력창 포커스 유틸
window.fillInput = function(text) { window.focusInput = function() {
const ta = document.querySelector('.pill-input textarea'); const ta = document.querySelector('.pill-input textarea');
if (!ta) return; if (ta) ta.focus();
const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
setter.call(ta, text);
ta.dispatchEvent(new Event('input', { bubbles: true }));
ta.focus();
}; };
// D4-5: 설정 패널 토글 // D4-5: 설정 패널 토글
@@ -575,22 +564,23 @@ footer { display: none !important; }
} }
/* ── 예시 질문 Chip (D4-4) ── */ /* ── 예시 질문 Chip (D4-4) ── */
.example-chips-wrap { display: flex; flex-wrap: wrap; gap: 8px; padding: 8px 0 4px; } .example-chips-row { padding: 6px 0 4px !important; flex-wrap: wrap !important; gap: 8px !important; }
.example-chip { .example-chip-btn button {
background: #fff; border-radius: 20px !important;
border: 1.5px solid #e2e8f0; border: 1.5px solid #e2e8f0 !important;
border-radius: 20px; background: #fff !important;
padding: 8px 16px; color: #374151 !important;
font-size: .875rem; padding: 8px 18px !important;
color: #374151; font-size: .875rem !important;
cursor: pointer; box-shadow: none !important;
transition: all .15s; white-space: nowrap !important;
white-space: nowrap; transition: all .15s !important;
font-weight: 400 !important;
} }
.example-chip:hover { .example-chip-btn button:hover {
background: #eff6ff; background: #eff6ff !important;
border-color: #93c5fd; border-color: #93c5fd !important;
color: #1d4ed8; color: #1d4ed8 !important;
} }
/* ── 컨트롤 패널 (D4-5) ── */ /* ── 컨트롤 패널 (D4-5) ── */
@@ -655,8 +645,8 @@ footer { display: none !important; }
.dark .message-wrap .bot { background: #1e293b !important; border-color: #334155 !important; } .dark .message-wrap .bot { background: #1e293b !important; border-color: #334155 !important; }
.dark .nav-btn button:hover { background: #1e293b !important; } .dark .nav-btn button:hover { background: #1e293b !important; }
.dark .nav-btn button.nav-active { background: #1e3a5f !important; color: #93c5fd !important; } .dark .nav-btn button.nav-active { background: #1e3a5f !important; color: #93c5fd !important; }
.dark .example-chip { background: #1e293b !important; border-color: #334155 !important; color: #cbd5e1 !important; } .dark .example-chip-btn button { background: #1e293b !important; border-color: #334155 !important; color: #cbd5e1 !important; }
.dark .example-chip:hover { background: #1e3a5f !important; border-color: #3b82f6 !important; } .dark .example-chip-btn button:hover { background: #1e3a5f !important; border-color: #3b82f6 !important; }
.dark #control-panel { background: #1e293b !important; border-color: #334155 !important; } .dark #control-panel { background: #1e293b !important; border-color: #334155 !important; }
""" """
@@ -756,8 +746,12 @@ with gr.Blocks(title="율봇", css=_CUSTOM_CSS, theme=_THEME, js=_JS) as demo:
) )
send_btn = gr.Button("", variant="primary", scale=0, min_width=48, elem_classes=["pill-send-btn"]) send_btn = gr.Button("", variant="primary", scale=0, min_width=48, elem_classes=["pill-send-btn"])
# D4-4: 예시 질문 Chips # D4-4: 예시 질문 Chips (gr.Button — Gradio 이벤트로 msg_box 직접 업데이트)
gr.HTML(_example_chips_html()) with gr.Row(elem_classes=["example-chips-row"]):
chip_btns = [
gr.Button(q, elem_classes=["example-chip-btn"], size="sm")
for q in _EXAMPLE_QUESTIONS
]
# 첨부 Accordions # 첨부 Accordions
with gr.Accordion("📷 이미지 첨부 (선택)", open=False): with gr.Accordion("📷 이미지 첨부 (선택)", open=False):
@@ -816,6 +810,10 @@ with gr.Blocks(title="율봇", css=_CUSTOM_CSS, theme=_THEME, js=_JS) as demo:
# ── 이벤트 바인딩 ───────────────────────────────────────────── # ── 이벤트 바인딩 ─────────────────────────────────────────────
# 예시 질문 chips → msg_box 채우기
for _q, _btn in zip(_EXAMPLE_QUESTIONS, chip_btns):
_btn.click(fn=lambda x=_q: x, outputs=[msg_box])
# 사이드바 네비게이션 # 사이드바 네비게이션
_panels = [panel_chat, panel_doc_register, panel_doc_manage] _panels = [panel_chat, panel_doc_register, panel_doc_manage]
chat_nav_btn.click(show_chat_panel, outputs=_panels, chat_nav_btn.click(show_chat_panel, outputs=_panels,