diff --git a/app.py b/app.py index 3b36164..4afe407 100644 --- a/app.py +++ b/app.py @@ -111,7 +111,10 @@ def ui_polish(raw_text: str) -> tuple[str, str]: ok, result = polish_text(raw_text) if ok: - return result, f"✅ Gemma4 润色完成,共 {len(result)} 字。" + return ( + result, + f"✅ Gemma4 润色完成,共 {len(result)} 字。请向下滚动到 Step 3 选择音色并合成。", + ) return "", f"❌ {result}" @@ -525,6 +528,63 @@ gradio-app, height: 18px !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 兜底(其他下拉组件深色化) */ .gradio-container .gradio-dropdown input, @@ -988,11 +1048,11 @@ def build_app() -> gr.Blocks: [lock_log, speaker_status], ) - # ---- Tab 2: 分步操作 ---- + # ---- Tab 2: 分步操作(纵向三步,避免三栏挤在一起)---- with gr.Tab("🔧 分步流水线"): gr.HTML(MIC_HINT_HTML) - with gr.Row(elem_classes=["pipeline-steps"]): - with gr.Column(scale=1): + with gr.Column(elem_classes=["pipeline-flow"]): + with gr.Group(elem_classes=["pipeline-step-card"]): gr.Markdown("### Step 1 · 音频极速识别") rec_audio = gr.Audio( label="交易复盘碎碎念录音", @@ -1002,36 +1062,37 @@ def build_app() -> gr.Blocks: transcribe_btn = gr.Button("⚡ Faster-Whisper 识别", variant="primary") 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 纪律审判") raw_text = gr.Textbox( label="转写原文(可编辑)", - lines=10, + lines=8, placeholder="识别结果将显示在此,也可手动粘贴...", ) polish_btn = gr.Button("⚖️ 远程 Gemma4 严厉润色", variant="primary") 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( - "> 全部在 **本机显卡** 运行,无需微软/讯飞 API。" - "可选「我的锁定音色」或预设男/女声;合成前会自动清洗 Markdown。" - ) - tts_voice = gr.Radio( - label="配音音色(本地 ChatTTS)", - choices=voice_choice_labels(), - value=_default_voice_label(), - info="预设音色需先在服务器执行 bash scripts/generate_voice_presets.sh", - elem_classes=["voice-radio"], + "" + "本机显卡合成,无需 API。润色完成后在此选音色并点合成。" ) + with gr.Accordion("🎚️ 选择配音音色", open=True): + tts_voice = gr.Radio( + label="配音音色(本地 ChatTTS)", + choices=voice_choice_labels(), + value=_default_voice_label(), + info="预设音色:bash scripts/generate_voice_presets.sh", + elem_classes=["voice-radio"], + ) polished_text = gr.Textbox( - label="润色配音稿(可编辑,支持含 Markdown,合成时自动清洗)", - lines=10, + label="润色配音稿(可编辑,合成时自动清洗 Markdown)", + lines=12, placeholder="润色结果将显示在此...", ) 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") transcribe_btn.click(ui_transcribe, rec_audio, [raw_text, transcribe_log])