From d63cb318b26d81e34179a1205fd365e9506cd169 Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 12 Jun 2026 19:22:52 +0800 Subject: [PATCH] Redesign one-click tab as single compact page Wrap workflow in one panel with side-by-side input columns, inline options, embedded history accordion, and fixed tab bar distribution. Co-authored-by: Cursor --- app.py | 224 ++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 136 insertions(+), 88 deletions(-) diff --git a/app.py b/app.py index a2bcd09..3e054ba 100644 --- a/app.py +++ b/app.py @@ -654,8 +654,10 @@ gradio-app, .pipeline-steps > div, .pipeline-output-row, .pipeline-output-row > div, - .pipeline-input-row, - .pipeline-input-row > div { + .oneclick-input-grid, + .oneclick-input-grid > div, + .oneclick-result-grid, + .oneclick-result-grid > div { flex-direction: column !important; width: 100% !important; } @@ -1019,56 +1021,23 @@ gradio-app, border-color: #374151 !important; } -.gradio-container .tab-nav button { - color: #9ca3af !important; - font-weight: 600 !important; - font-size: 0.95rem !important; - padding: 10px 18px !important; -} -/* 主导航 Tab 均匀分布 */ -.gradio-container .main-nav-tabs > .tab-nav, -.gradio-container .tabs.main-nav-tabs > .tab-nav { +/* 主导航 Tab 均匀分布(兼容 Gradio 4/5) */ +.gradio-container .tab-nav { display: flex !important; width: 100% !important; gap: 0 !important; + flex-wrap: nowrap !important; } -.gradio-container .main-nav-tabs > .tab-nav button, -.gradio-container .tabs.main-nav-tabs > .tab-nav button { - flex: 1 1 25% !important; +.gradio-container .tab-nav button { + flex: 1 1 0 !important; min-width: 0 !important; text-align: center !important; justify-content: center !important; border-radius: 0 !important; -} -/* 一键生成:上传区与转写区等大 */ -.pipeline-input-row { - align-items: stretch !important; - gap: 12px !important; -} -.pipeline-input-row > .gr-column, -.pipeline-input-row > div { - flex: 1 1 50% !important; - min-width: 0 !important; - width: 50% !important; - display: flex !important; - flex-direction: column !important; -} -.pipeline-input-row .block, -.pipeline-input-row .form { - flex: 1 1 auto !important; - height: 100% !important; -} -.pipeline-input-row .audio-container, -.pipeline-input-row .upload-container { - flex: 1 1 auto !important; - min-height: 220px !important; - display: flex !important; - flex-direction: column !important; - justify-content: center !important; -} -.pipeline-input-row textarea { - min-height: 220px !important; - height: 100% !important; + color: #9ca3af !important; + font-weight: 600 !important; + font-size: 0.95rem !important; + padding: 10px 12px !important; } .gradio-container .tab-nav button.selected { color: #ffffff !important; @@ -1076,6 +1045,74 @@ gradio-app, border-bottom: 3px solid #3b82f6 !important; } +/* 一键生成:单页工作区 */ +.oneclick-panel { + border: 1px solid #374151 !important; + border-radius: 12px !important; + padding: 16px 18px 18px !important; + background: #111827 !important; + width: 100% !important; + box-sizing: border-box !important; +} +.oneclick-hint { + color: #94a3b8 !important; + font-size: 0.88rem !important; + margin: 0 0 12px 0 !important; + padding: 0 !important; +} +.oneclick-hint p { margin: 0 !important; color: #94a3b8 !important; } +.oneclick-input-grid { + display: flex !important; + flex-direction: row !important; + flex-wrap: nowrap !important; + align-items: stretch !important; + gap: 12px !important; + width: 100% !important; + margin-bottom: 10px !important; +} +.oneclick-input-grid > .column, +.oneclick-input-grid > .gr-column { + flex: 1 1 0 !important; + min-width: 0 !important; +} +.oneclick-input-grid .audio-container, +.oneclick-input-grid .upload-container { + min-height: 200px !important; +} +.oneclick-input-grid textarea { + min-height: 200px !important; +} +.oneclick-options-row { + align-items: center !important; + gap: 12px !important; + margin: 4px 0 10px !important; +} +.oneclick-options-row .accordion, +.oneclick-options-row .gr-accordion { + margin: 0 !important; + width: 100% !important; +} +.oneclick-result-grid { + display: flex !important; + flex-direction: row !important; + gap: 12px !important; + width: 100% !important; +} +.oneclick-result-grid > .column, +.oneclick-result-grid > .gr-column { + flex: 1 1 0 !important; + min-width: 0 !important; +} +.oneclick-result-grid textarea { + min-height: 140px !important; +} +@media (max-width: 768px) { + .oneclick-input-grid, + .oneclick-result-grid { + flex-direction: column !important; + } +} + .gradio-container button.primary, .gradio-container .primary { background: #2563eb !important; @@ -1355,41 +1392,64 @@ def build_app() -> gr.Blocks: with gr.Tabs(elem_classes=["main-nav-tabs"]): # ---- Tab 1: 一键生成(默认首页)---- with gr.Tab("一键生成"): - gr.HTML(MIC_HINT_HTML) - gr.Markdown( - "上传碎碎念录音,系统自动完成 **识别 → 润色 → 合成** 全流程。" - ) - with gr.Row(equal_height=True, elem_classes=["pipeline-input-row"]): - pipe_audio = gr.Audio( - label="复盘录音", - type="filepath", - sources=["upload", "microphone"], - scale=1, + with gr.Group(elem_classes=["oneclick-panel"]): + gr.Markdown( + "上传录音或粘贴转写 → 自动 **识别 · 润色 · 合成**", + elem_classes=["oneclick-hint"], ) - pipe_manual = gr.Textbox( - label="或手动输入转写(跳过识别)", - lines=8, - placeholder="若已有转写文本,可直接粘贴,留空则走 Whisper 识别", - scale=1, - elem_classes=["bright-input"], + with gr.Row(elem_classes=["oneclick-input-grid"]): + with gr.Column(scale=1, min_width=200): + pipe_audio = gr.Audio( + label="复盘录音", + type="filepath", + sources=["upload", "microphone"], + ) + with gr.Column(scale=1, min_width=200): + pipe_manual = gr.Textbox( + label="或手动输入转写(跳过识别)", + lines=6, + placeholder="已有转写可直接粘贴,留空则 Whisper 识别", + elem_classes=["bright-input"], + ) + with gr.Row(elem_classes=["oneclick-options-row"]): + skip_polish_cb = gr.Checkbox( + label="跳过 Gemma4 润色(仅测试 TTS)", + value=False, + scale=0, + ) + with gr.Column(scale=1): + with gr.Accordion("🎚️ 配音音色", open=False): + pipe_voice = gr.Radio( + label="配音音色(本地 ChatTTS)", + choices=voice_choice_labels(), + value=default_voice_label(), + elem_classes=["voice-radio"], + ) + pipeline_btn = gr.Button( + "▶ 启动全流程", variant="primary", size="lg" ) - skip_polish_cb = gr.Checkbox( - label="跳过 Gemma4 润色(仅测试 TTS)", - value=False, - ) - with gr.Accordion("🎚️ 配音音色", open=False): - pipe_voice = gr.Radio( - label="配音音色(本地 ChatTTS)", - choices=voice_choice_labels(), - value=default_voice_label(), - elem_classes=["voice-radio"], + pipeline_log = gr.Textbox( + label="流水线日志", lines=3, interactive=False ) - pipeline_btn = gr.Button("▶ 启动全流程", variant="primary", size="lg") - pipeline_log = gr.Textbox(label="流水线日志", lines=6, interactive=False) - with gr.Row(elem_classes=["pipeline-output-row"]): - pipe_raw = gr.Textbox(label="转写原文", lines=6) - pipe_polished = gr.Textbox(label="润色稿", lines=6) - pipe_player = _voice_player_block() + with gr.Row(elem_classes=["oneclick-result-grid"]): + with gr.Column(scale=1): + pipe_raw = gr.Textbox(label="转写原文", lines=5) + with gr.Column(scale=1): + pipe_polished = gr.Textbox(label="润色稿", lines=5) + pipe_player = _voice_player_block() + with gr.Accordion("📂 配音历史", open=False): + with gr.Row(): + history_select = gr.Dropdown( + label="历史配音", + choices=list_voice_history(), + value=None, + interactive=True, + scale=4, + ) + history_refresh_btn = gr.Button( + "🔄 刷新", scale=0, min_width=100 + ) + history_player = _voice_player_block() # ---- Tab 2: 分步流水线 ---- with gr.Tab("分步流水线"): @@ -1517,18 +1577,6 @@ def build_app() -> gr.Blocks: [lock_log, speaker_status], ) - with gr.Accordion("📂 配音历史(本地保留,可随时试听下载)", open=True): - with gr.Row(): - history_select = gr.Dropdown( - label="历史配音", - choices=list_voice_history(), - value=None, - interactive=True, - scale=4, - ) - history_refresh_btn = gr.Button("🔄 刷新", scale=0, min_width=100) - history_player = _voice_player_block() - history_refresh_btn.click(ui_history_dropdown, outputs=[history_select]) history_select.change(ui_history_play, history_select, history_player) demo.load(ui_initial_history, outputs=[history_select, history_player])