diff --git a/app.py b/app.py index f10ba2d..18f893b 100644 --- a/app.py +++ b/app.py @@ -193,14 +193,99 @@ PWA_HEAD = """ """ +INSTALL_APP_BUTTON_HTML = """ + +""" + CUSTOM_CSS = """ /* ========== 居中布局 + 响应式 + 高对比度 ========== */ html, body { @@ -348,10 +433,168 @@ gradio-app, line-height: 1.6 !important; } +/* placeholder 高对比度 */ .gradio-container textarea::placeholder, -.gradio-container input::placeholder { - color: #9ca3af !important; +.gradio-container input::placeholder, +.gradio-container input[type="text"]::placeholder { + color: #d1d5db !important; opacity: 1 !important; + -webkit-text-fill-color: #d1d5db !important; +} +.gradio-container .bright-input textarea, +.gradio-container .bright-input input { + background: #1e293b !important; + border: 1px solid #64748b !important; +} + +/* 输入框下方 info 提示文字 */ +.gradio-container .hint, +.gradio-container .info, +.gradio-container .form .secondary-wrap, +.gradio-container span[data-testid="block-info"] { + color: #94a3b8 !important; + font-size: 0.88rem !important; +} + +/* Markdown 内联代码 — 修复白底看不见 */ +.gradio-container code, +.gradio-container .prose code, +.gradio-container .markdown-text code, +.gradio-container pre code { + background: #1e3a5f !important; + color: #bfdbfe !important; + border: 1px solid #3b82f6 !important; + padding: 2px 10px !important; + border-radius: 6px !important; + font-size: 0.9em !important; +} +.gradio-container pre { + background: #111827 !important; + border: 1px solid #374151 !important; + padding: 12px !important; + border-radius: 8px !important; +} + +/* 组件标签 — 修复白底蓝字 */ +.gradio-container .block-label, +.gradio-container .label-wrap, +.gradio-container .label-wrap span, +.gradio-container span.label { + background: #1e293b !important; + background-color: #1e293b !important; + color: #93c5fd !important; + font-weight: 600 !important; + font-size: 0.95rem !important; + border: 1px solid #475569 !important; + border-radius: 6px !important; + padding: 4px 10px !important; +} + +/* 说明文字块 */ +.hint-box { + background: #1e293b !important; + border: 1px solid #475569 !important; + border-radius: 10px !important; + padding: 14px 18px !important; + color: #e2e8f0 !important; + font-size: 0.95rem !important; + line-height: 1.7 !important; + margin-bottom: 12px !important; +} +.hint-box strong { color: #ffffff !important; } +.file-tag { + display: inline-block; + background: #1e3a5f !important; + color: #bfdbfe !important; + border: 1px solid #3b82f6 !important; + padding: 3px 12px !important; + border-radius: 6px !important; + font-family: "JetBrains Mono", Consolas, monospace !important; + font-weight: 700 !important; + font-size: 0.92em !important; +} + +/* 页头 + 安装 App 按钮 */ +.header-row { + align-items: flex-start !important; + width: 100% !important; +} +.header-row > div { flex: 1 1 auto !important; } +.pwa-install-btn { + display: none; + align-items: center; + gap: 10px; + padding: 12px 20px; + margin-top: 8px; + background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%); + color: #ffffff !important; + border: 2px solid #60a5fa; + border-radius: 12px; + cursor: pointer; + font-weight: 700; + font-size: 1rem; + font-family: inherit; + box-shadow: 0 4px 20px rgba(37, 99, 235, 0.45); + transition: transform 0.15s, box-shadow 0.15s; + white-space: nowrap; +} +.pwa-install-btn:hover { + transform: translateY(-1px); + box-shadow: 0 6px 24px rgba(37, 99, 235, 0.55); +} +.pwa-install-btn.pwa-ready { + animation: pwa-pulse 2s infinite; +} +@keyframes pwa-pulse { + 0%, 100% { box-shadow: 0 4px 20px rgba(37, 99, 235, 0.45); } + 50% { box-shadow: 0 4px 28px rgba(96, 165, 250, 0.8); } +} +.pwa-install-icon { flex-shrink: 0; border-radius: 6px; } +.pwa-install-text { color: #ffffff !important; } + +/* 安装引导弹窗 */ +.pwa-modal-overlay { + position: fixed; + inset: 0; + z-index: 99999; + background: rgba(0, 0, 0, 0.75); + display: flex; + align-items: center; + justify-content: center; + padding: 20px; +} +.pwa-modal { + position: relative; + background: #1e293b; + border: 2px solid #3b82f6; + border-radius: 16px; + padding: 28px 32px; + max-width: 420px; + width: 100%; + color: #e2e8f0; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); +} +.pwa-modal h3 { color: #ffffff !important; margin: 0 0 12px !important; font-size: 1.25rem !important; } +.pwa-modal p { color: #cbd5e1 !important; line-height: 1.6 !important; } +.pwa-modal ol { color: #e2e8f0 !important; padding-left: 20px !important; line-height: 1.8 !important; } +.pwa-modal-tip { font-size: 0.85rem !important; color: #93c5fd !important; margin-top: 16px !important; } +.pwa-modal-close { + position: absolute; + top: 12px; + right: 14px; + background: #374151; + border: none; + color: #fff; + width: 32px; + height: 32px; + border-radius: 8px; + cursor: pointer; + font-size: 1rem; +} + +@media (max-width: 640px) { + .header-row { flex-direction: column !important; } + .pwa-install-btn { width: 100% !important; justify-content: center !important; margin-top: 12px !important; } } .gradio-container .wrap .readonly textarea { @@ -512,8 +755,9 @@ def build_app() -> gr.Blocks: with gr.Blocks( title="Trading Studio | 交易复盘配音中控", ) as demo: - gr.Markdown( - f""" + with gr.Row(elem_classes=["header-row"]): + gr.Markdown( + f""" # ⚡ Trading Studio **本地量化交易复盘 → B 站配音生产流水线** @@ -525,11 +769,10 @@ def build_app() -> gr.Blocks: | ChatTTS | 本地 GPU 固定音色合成 | > 仓库: [{GIT_REPO_URL}]({GIT_REPO_URL}) - -
📱 安装为 App:手机/平板用浏览器菜单「添加到主屏幕」;电脑 Chrome/Edge 地址栏点击「安装 Trading Studio」图标即可。
- """, - elem_classes=["dark-panel"], - ) + """, + elem_classes=["dark-panel"], + ) + gr.HTML(INSTALL_APP_BUTTON_HTML) with gr.Row(elem_classes=["status-row"]): ollama_status = gr.HTML(value=_status_html("Ollama 节点", "正在检测...", "warn")) @@ -544,9 +787,12 @@ def build_app() -> gr.Blocks: with gr.Tabs(): # ---- Tab 1: 音色锁定 ---- with gr.Tab("🎙️ 音色锁定"): - gr.Markdown( - "上传 **10-30 秒** 干净人声样本,系统将提取 Speaker Embedding " - f"并保存至 `{SPEAKER_EMB_PATH.name}`,后续合成 100% 还原音色。" + gr.HTML( + f'
' + f'上传 10-30 秒 干净人声样本,系统将提取 Speaker Embedding ' + f'并保存至 {SPEAKER_EMB_PATH.name},' + f'后续合成 100% 还原音色。' + f"
" ) with gr.Row(): spk_audio = gr.Audio( @@ -556,8 +802,10 @@ def build_app() -> gr.Blocks: ) spk_transcript = gr.Textbox( label="参考音频精确转写(可选,提升还原度)", - placeholder="尽量与参考音频内容完全一致...", + placeholder="示例:今天开了三单,第一单手贱提前平了,第二单…", + info="请尽量与参考音频内容完全一致,可提升音色还原度", lines=6, + elem_classes=["bright-input"], ) lock_btn = gr.Button("🔒 锁定音色", variant="primary") lock_log = gr.Textbox(label="锁定结果", lines=4, interactive=False) diff --git a/pwa/icons/install.svg b/pwa/icons/install.svg new file mode 100644 index 0000000..23ca517 --- /dev/null +++ b/pwa/icons/install.svg @@ -0,0 +1,7 @@ + + + + + + +