Use vertical pipeline layout after polish to fix cramped UI.

Stack steps in full-width cards, compact voice grid, and guide user to Step 3 after polish.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-12 17:39:13 +08:00
parent 131cbf070a
commit 038e00fbcf
+74 -13
View File
@@ -111,7 +111,10 @@ def ui_polish(raw_text: str) -> tuple[str, str]:
ok, result = polish_text(raw_text) ok, result = polish_text(raw_text)
if ok: if ok:
return result, f"✅ Gemma4 润色完成,共 {len(result)} 字。" return (
result,
f"✅ Gemma4 润色完成,共 {len(result)} 字。请向下滚动到 Step 3 选择音色并合成。",
)
return "", f"{result}" return "", f"{result}"
@@ -525,6 +528,63 @@ gradio-app,
height: 18px !important; height: 18px !important;
flex-shrink: 0 !important; flex-shrink: 0 !important;
} }
@media (min-width: 520px) {
.gradio-container .voice-radio fieldset {
display: grid !important;
grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
gap: 8px !important;
max-height: none !important;
}
.gradio-container .voice-radio label { margin: 0 !important; }
}
@media (max-width: 519px) {
.gradio-container .voice-radio fieldset {
max-height: 200px !important;
overflow-y: auto !important;
}
}
/* 分步流水线:纵向卡片,避免三栏挤扁 */
.pipeline-flow {
width: 100% !important;
gap: 4px !important;
}
.pipeline-step-card {
border: 1px solid #374151 !important;
border-radius: 12px !important;
padding: 16px 18px !important;
background: #111827 !important;
margin-bottom: 18px !important;
width: 100% !important;
box-sizing: border-box !important;
}
.pipeline-step-card h3 {
margin-top: 0 !important;
padding-bottom: 8px !important;
border-bottom: 1px solid #374151 !important;
}
.gradio-container .pipeline-step-card button {
width: 100% !important;
max-width: 100% !important;
white-space: normal !important;
line-height: 1.35 !important;
min-height: 44px !important;
}
.gradio-container .accordion,
.gradio-container .gr-accordion {
border: 1px solid #374151 !important;
border-radius: 10px !important;
background: #1a2332 !important;
}
.gradio-container .accordion > .label-wrap,
.gradio-container .gr-accordion .label-wrap {
background: #1e293b !important;
color: #e5e7eb !important;
}
.gradio-container .accordion .content,
.gradio-container .gr-accordion .content {
background: #111827 !important;
}
/* Dropdown 兜底(其他下拉组件深色化) */ /* Dropdown 兜底(其他下拉组件深色化) */
.gradio-container .gradio-dropdown input, .gradio-container .gradio-dropdown input,
@@ -988,11 +1048,11 @@ def build_app() -> gr.Blocks:
[lock_log, speaker_status], [lock_log, speaker_status],
) )
# ---- Tab 2: 分步操作 ---- # ---- Tab 2: 分步操作(纵向三步,避免三栏挤在一起)----
with gr.Tab("🔧 分步流水线"): with gr.Tab("🔧 分步流水线"):
gr.HTML(MIC_HINT_HTML) gr.HTML(MIC_HINT_HTML)
with gr.Row(elem_classes=["pipeline-steps"]): with gr.Column(elem_classes=["pipeline-flow"]):
with gr.Column(scale=1): with gr.Group(elem_classes=["pipeline-step-card"]):
gr.Markdown("### Step 1 · 音频极速识别") gr.Markdown("### Step 1 · 音频极速识别")
rec_audio = gr.Audio( rec_audio = gr.Audio(
label="交易复盘碎碎念录音", label="交易复盘碎碎念录音",
@@ -1002,36 +1062,37 @@ def build_app() -> gr.Blocks:
transcribe_btn = gr.Button("⚡ Faster-Whisper 识别", variant="primary") transcribe_btn = gr.Button("⚡ Faster-Whisper 识别", variant="primary")
transcribe_log = gr.Textbox(label="识别日志", lines=2, interactive=False) transcribe_log = gr.Textbox(label="识别日志", lines=2, interactive=False)
with gr.Column(scale=1): with gr.Group(elem_classes=["pipeline-step-card"]):
gr.Markdown("### Step 2 · Gemma4 纪律审判") gr.Markdown("### Step 2 · Gemma4 纪律审判")
raw_text = gr.Textbox( raw_text = gr.Textbox(
label="转写原文(可编辑)", label="转写原文(可编辑)",
lines=10, lines=8,
placeholder="识别结果将显示在此,也可手动粘贴...", placeholder="识别结果将显示在此,也可手动粘贴...",
) )
polish_btn = gr.Button("⚖️ 远程 Gemma4 严厉润色", variant="primary") polish_btn = gr.Button("⚖️ 远程 Gemma4 严厉润色", variant="primary")
polish_log = gr.Textbox(label="润色日志", lines=2, interactive=False) polish_log = gr.Textbox(label="润色日志", lines=2, interactive=False)
with gr.Column(scale=1): with gr.Group(elem_classes=["pipeline-step-card"]):
gr.Markdown("### Step 3 · 本地 GPU 配音合成") gr.Markdown("### Step 3 · 本地 GPU 配音合成")
gr.Markdown( gr.Markdown(
"> 全部在 **本机显卡** 运行,无需微软/讯飞 API。" "<span style='color:#94a3b8;font-size:0.9rem'>"
"可选「我的锁定音色」或预设男/女声;合成前会自动清洗 Markdown。" "本机显卡合成,无需 API。润色完成后在此选音色并点合成。</span>"
) )
with gr.Accordion("🎚️ 选择配音音色", open=True):
tts_voice = gr.Radio( tts_voice = gr.Radio(
label="配音音色(本地 ChatTTS", label="配音音色(本地 ChatTTS",
choices=voice_choice_labels(), choices=voice_choice_labels(),
value=_default_voice_label(), value=_default_voice_label(),
info="预设音色需先在服务器执行 bash scripts/generate_voice_presets.sh", info="预设音色bash scripts/generate_voice_presets.sh",
elem_classes=["voice-radio"], elem_classes=["voice-radio"],
) )
polished_text = gr.Textbox( polished_text = gr.Textbox(
label="润色配音稿(可编辑,支持含 Markdown,合成时自动清洗", label="润色配音稿(可编辑,合成时自动清洗 Markdown",
lines=10, lines=12,
placeholder="润色结果将显示在此...", placeholder="润色结果将显示在此...",
) )
synth_btn = gr.Button("🔊 合成配音 WAV", variant="primary") synth_btn = gr.Button("🔊 合成配音 WAV", variant="primary")
synth_log = gr.Textbox(label="合成日志", lines=2, interactive=False) synth_log = gr.Textbox(label="合成日志", lines=3, interactive=False)
output_audio = gr.Audio(label="成品配音", type="filepath") output_audio = gr.Audio(label="成品配音", type="filepath")
transcribe_btn.click(ui_transcribe, rec_audio, [raw_text, transcribe_log]) transcribe_btn.click(ui_transcribe, rec_audio, [raw_text, transcribe_log])