UI/UX D0: fix Textbox label, hide audio accordion, hide footer, hide ingest result box

- app.py: show_label=False on msg_box to remove "Textbox" label
- app.py: wrap audio input in gr.Accordion(open=False) to hide microphone error on load
- app.py: add CSS to hide Gradio footer branding
- app.py: ingest_status starts visible=False; ingest_files returns gr.update(visible=True)
- ROADMAP.md: add UI/UX design improvement section (D0–D3) and mark D0 complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 14:25:54 +09:00
parent 20f385e2a0
commit e08b43c785
2 changed files with 91 additions and 13 deletions
+73
View File
@@ -278,6 +278,79 @@ youlbot-webui/
---
## UI/UX 디자인 개선
> 실제 UI 스크린샷 분석(2026-06-02) 기반 — 우선순위 순 정렬
### D0 — 즉시 수정 (노출 결함)
| # | 위치 | 문제 | 개선 방안 |
|---|------|------|-----------|
| 1 | 대화 탭 · 입력창 | `"Textbox"` 레이블이 입력창 위에 그대로 노출 | `gr.Textbox(label="", show_label=False, ...)` 또는 `label=None` |
| 2 | 대화 탭 · 음성 영역 | `"마이크를 찾을 수 ..."` 오류 메시지가 항상 노출 | 마이크 미사용 시 해당 텍스트 숨김 처리, 또는 `visible=False` 기본값 |
| 3 | 전체 탭 · 푸터 | `"Gradio로 제작됨 🎉"` Gradio 브랜딩 노출 | `css="footer { display: none; }"` 추가 또는 `gr.Blocks(show_footer=False)` |
| 4 | 문서 등록 탭 · 결과 박스 | 업로드 전에도 빈 `결과` 박스가 항상 노출 | 초기 `visible=False`, 수집 완료 시에만 `visible=True` 반환 |
### D1 — 1주일 내 (레이아웃 개선)
| # | 위치 | 문제 | 개선 방안 |
|---|------|------|-----------|
| 5 | 대화 탭 · 채팅 영역 | 초기 화면이 빈 흰 공간으로 시작 — 어색한 첫 인상 | 웰컴 메시지 또는 예시 질문 버블(`gr.Examples`) 추가 |
| 6 | 대화 탭 · 이미지 첨부 | 이미지 업로드 영역이 항상 전체 크기로 펼쳐짐 | `gr.Accordion("이미지 첨부 (선택)", open=False)` 로 접이식 변경 |
| 7 | 대화 탭 · 하단 컨트롤 | `사고 과정 표시`, `TTS`, `대화 초기화` 배치가 불규칙 | `gr.Row`로 균등 3분할, `대화 초기화`는 오른쪽 정렬 |
| 8 | 대화 탭 · 입력 영역 | 텍스트 입력창과 `전송` 버튼이 시각적으로 분리됨 | 입력창 높이를 `lines=2`로 통일, 버튼 높이 CSS로 맞춤 |
| 9 | 문서 관리 탭 · 삭제 UX | 경로를 테이블에서 복사해 입력 필드에 붙여넣기 해야 함 | 테이블 행 클릭 → 입력 필드 자동 채움 (`select` 이벤트 활용) |
| 10 | 문서 등록 탭 · 버튼 | `문서 수집` 버튼이 전체 너비를 차지해 무게감 과도 | `scale=0` 또는 `min_width=200` 으로 적정 크기 조절 |
### D2 — 2주일 내 (시각 품질)
| # | 항목 | 설명 |
|---|------|------|
| 11 | 헤더 브랜딩 | 텍스트 전용 헤더 → 아이콘/로고 이미지 + 서브타이틀 레이아웃으로 개선 |
| 12 | 커스텀 테마 | Gradio 기본 보라색 → `gr.themes.Soft()` 또는 커스텀 `primary_hue` 색상 지정 |
| 13 | 사용자 선택 위치 | `사용자` 드롭다운이 채팅 위에 있어 흐름 방해 → 헤더 우측 또는 사이드바로 이동 |
| 14 | 채팅 버블 스타일 | Gradio 기본 스타일 → CSS로 사용자/봇 버블 배경색·radius 차별화 |
| 15 | 응답 로딩 표시 | 스트리밍 중 시각적 피드백 없음 → 입력 비활성화 + 스피너 CSS 추가 |
| 16 | 반응형 레이아웃 | 좁은 뷰포트에서 요소 겹침 → `gr.Column`/`gr.Row` 비율 재조정 |
### D3 — 선택 사항 (장기)
| # | 항목 | 설명 |
|---|------|------|
| 17 | 다크 모드 | `gr.themes.Base()` + CSS 변수로 다크/라이트 토글 지원 |
| 18 | 채팅 내보내기 | 대화 내용을 `.txt`/`.md`로 다운로드하는 버튼 추가 |
| 19 | 접근성 | `aria-label` 속성 추가, 키보드 포커스 표시 개선 |
| 20 | 온보딩 투어 | 첫 방문 사용자 대상 단계별 기능 안내 (JS 오버레이) |
### D 체크리스트
#### D0
- [x] `gr.Textbox(show_label=False)` — `"Textbox"` 레이블 제거
- [x] 음성 오류 메시지 기본 숨김 처리 — `gr.Accordion("🎤 음성으로 질문하기", open=False)` 로 기본 접힘
- [x] Gradio 푸터 CSS 숨김 — `footer { display: none !important; }` 추가
- [x] `결과` 박스 초기 `visible=False` — `ingest_files`에서 `gr.update(visible=True)` 반환
#### D1
- [ ] 웰컴 메시지 또는 예시 질문 버블 추가
- [ ] 이미지 첨부 영역 `gr.Accordion`으로 접이식 변경
- [ ] 하단 컨트롤 `gr.Row` 균등 배치
- [ ] 문서 관리 탭 — 테이블 행 클릭 → 삭제 경로 자동 채움
- [ ] `문서 수집` 버튼 크기 적정화
#### D2
- [ ] 헤더 아이콘/로고 추가
- [ ] `gr.themes.Soft()` 또는 커스텀 테마 적용
- [ ] 사용자 드롭다운 위치 이동
- [ ] 채팅 버블 커스텀 CSS
- [ ] 스트리밍 로딩 표시
#### D3
- [ ] 다크 모드 토글
- [ ] 채팅 내보내기 버튼
- [ ] 접근성 개선
---
## 진행 체크리스트
### P0
+18 -13
View File
@@ -168,7 +168,7 @@ async def reset_chat(user_id):
async def ingest_files(files):
if not files:
return "파일을 선택해주세요."
return gr.update(value="파일을 선택해주세요.", visible=True)
paths = [f if isinstance(f, str) else f.name for f in files]
results = []
for path in paths:
@@ -178,7 +178,7 @@ async def ingest_files(files):
results.append(f"{name}{result.get('chunks', '?')}개 청크")
except Exception as e:
results.append(f"{os.path.basename(path)} 오류: {e}")
return "\n".join(results)
return gr.update(value="\n".join(results), visible=True)
async def list_docs():
@@ -262,7 +262,11 @@ def _sources_html(sources: list) -> str:
)
with gr.Blocks(title="율봇") as demo:
_CUSTOM_CSS = """
footer { display: none !important; }
"""
with gr.Blocks(title="율봇", css=_CUSTOM_CSS) as demo:
gr.Markdown("# 율봇\n육아·금융 전문 AI 상담 도우미")
user_state = gr.State(DEFAULT_USER)
@@ -283,7 +287,7 @@ with gr.Blocks(title="율봇") as demo:
with gr.Row():
msg_box = gr.Textbox(
placeholder="질문을 입력하세요... (Enter로 전송)",
label="",
show_label=False,
scale=5,
autofocus=True,
)
@@ -298,14 +302,15 @@ with gr.Blocks(title="율봇") as demo:
)
gr.HTML("<div></div>", visible=False) # spacer
with gr.Row():
audio_input = gr.Audio(
sources=["microphone"],
type="filepath",
label="음성으로 질문하기",
scale=4,
)
transcribe_btn = gr.Button("음성 → 텍스트 변환", scale=1)
with gr.Accordion("🎤 음성으로 질문하기", open=False):
with gr.Row():
audio_input = gr.Audio(
sources=["microphone"],
type="filepath",
show_label=False,
scale=4,
)
transcribe_btn = gr.Button("음성 → 텍스트 변환", scale=1)
with gr.Row():
show_thinking = gr.Checkbox(label="사고 과정 표시", value=True)
@@ -346,7 +351,7 @@ with gr.Blocks(title="율봇") as demo:
label="파일 선택",
)
ingest_btn = gr.Button("문서 수집", variant="primary")
ingest_status = gr.Textbox(label="결과", interactive=False)
ingest_status = gr.Textbox(label="결과", interactive=False, visible=False)
ingest_btn.click(ingest_files, inputs=[file_input], outputs=[ingest_status])
with gr.Tab("문서 관리"):