diff --git a/acestep/ui/gradio/events/__init__.py b/acestep/ui/gradio/events/__init__.py
index 177b4b58..936a7eea 100644
--- a/acestep/ui/gradio/events/__init__.py
+++ b/acestep/ui/gradio/events/__init__.py
@@ -423,7 +423,7 @@ def setup_event_handlers(demo, dit_handler, llm_handler, dataset_handler, datase
generation_section["custom_mode_group"],
generation_section["generate_btn"],
generation_section["simple_sample_created"],
- generation_section["optional_params_accordion"],
+ generation_section["optional_params_section"],
generation_section["task_type"],
generation_section["src_audio_row"],
generation_section["repainting_group"],
@@ -692,7 +692,7 @@ def setup_event_handlers(demo, dit_handler, llm_handler, dataset_handler, datase
generation_section["custom_mode_group"],
generation_section["generate_btn"],
generation_section["simple_sample_created"],
- generation_section["optional_params_accordion"],
+ generation_section["optional_params_section"],
generation_section["task_type"],
generation_section["src_audio_row"],
generation_section["repainting_group"],
diff --git a/acestep/ui/gradio/events/generation/mode_ui.py b/acestep/ui/gradio/events/generation/mode_ui.py
index 1b61bba4..468a5414 100644
--- a/acestep/ui/gradio/events/generation/mode_ui.py
+++ b/acestep/ui/gradio/events/generation/mode_ui.py
@@ -145,7 +145,7 @@ def compute_mode_ui_updates(mode: str, llm_handler=None, previous_mode: str = "C
gr.update(visible=show_custom_group), # 1: custom_mode_group
generate_btn_update, # 2: generate_btn
False, # 3: simple_sample_created
- gr.Accordion(visible=show_optional, open=False), # 4: optional_params_accordion
+ gr.Column(visible=show_optional), # 4: optional_params_section
gr.update(value=task_type, elem_classes=["has-info-container"]), # 5: task_type
gr.update(visible=show_src_audio), # 6: src_audio_row
gr.update(visible=show_repainting), # 7: repainting_group
diff --git a/acestep/ui/gradio/help_content.py b/acestep/ui/gradio/help_content.py
index 49df6672..12127de1 100644
--- a/acestep/ui/gradio/help_content.py
+++ b/acestep/ui/gradio/help_content.py
@@ -8,6 +8,7 @@
space and can be placed inside any existing row or header without
creating extra blank rows.
"""
+from pathlib import Path
import gradio as gr
from acestep.ui.gradio.i18n import t
@@ -181,7 +182,7 @@ def create_help_button(section_key: str) -> gr.HTML:
""",
- elem_classes=["help-inline-container"],
+ elem_classes=["help-inline-container", "no-grow"],
)
return html
@@ -190,108 +191,5 @@ def create_help_button(section_key: str) -> gr.HTML:
# ---------------------------------------------------------------------------
# CSS to be injected into the main Blocks CSS string.
# ---------------------------------------------------------------------------
-HELP_MODAL_CSS = """
-/* ---- Inline help button container ---- */
-.help-inline-container {
- min-height: 0 !important;
- padding: 0 !important;
- margin: 0 !important;
- display: inline-flex !important;
- align-items: center !important;
- flex-shrink: 0 !important;
- max-width: 32px !important;
- min-width: 32px !important;
- overflow: visible !important;
-}
-
-.help-inline-wrapper {
- display: inline-flex;
- align-items: center;
- line-height: 1;
-}
-
-/* ---- Inline help button ---- */
-.help-inline-btn {
- width: 22px;
- height: 22px;
- border-radius: 50%;
- border: 1.5px solid var(--border-color-primary, #555);
- background: transparent;
- color: var(--body-text-color-subdued, #888);
- font-size: 12px;
- font-weight: 600;
- line-height: 20px;
- text-align: center;
- cursor: pointer;
- padding: 0;
- transition: all 0.15s ease;
- flex-shrink: 0;
-}
-.help-inline-btn:hover {
- background: var(--color-accent, #4a9eff);
- color: #fff;
- border-color: var(--color-accent, #4a9eff);
- transform: scale(1.1);
-}
-
-/* ---- Modal overlay ---- */
-.help-modal-overlay {
- position: fixed;
- top: 0; left: 0; right: 0; bottom: 0;
- background: rgba(0,0,0,0.5);
- z-index: 100000;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-.help-modal-content {
- background: var(--background-fill-primary, #fff);
- color: var(--body-text-color, #222);
- border-radius: 12px;
- max-width: 640px;
- width: 90%;
- max-height: 80vh;
- display: flex;
- flex-direction: column;
- box-shadow: 0 20px 60px rgba(0,0,0,0.3);
- position: relative;
-}
-
-.help-modal-close {
- position: absolute;
- top: 12px; right: 16px;
- background: none;
- border: none;
- font-size: 20px;
- cursor: pointer;
- color: var(--body-text-color, #222);
- z-index: 1;
- opacity: 0.6;
-}
-.help-modal-close:hover { opacity: 1; }
-
-.help-modal-body {
- padding: 28px 32px;
- overflow-y: auto;
- line-height: 1.7;
- font-size: 0.92rem;
-}
-.help-modal-body h3 { margin: 16px 0 8px; font-size: 1.15rem; }
-.help-modal-body h4 { margin: 12px 0 6px; font-size: 1.0rem; }
-.help-modal-body pre {
- background: var(--background-fill-secondary, #f5f5f5);
- padding: 10px;
- border-radius: 6px;
- overflow-x: auto;
- font-size: 0.85rem;
-}
-.help-modal-body code {
- background: var(--background-fill-secondary, #f5f5f5);
- padding: 1px 4px;
- border-radius: 3px;
- font-size: 0.88em;
-}
-.help-modal-body ul { margin: 6px 0; }
-.help-modal-body li { margin: 3px 0; }
-"""
+css_file = Path(__file__).parent / "interfaces" / "css" / "help_modal.css"
+HELP_MODAL_CSS = css_file.read_text()
diff --git a/acestep/ui/gradio/interfaces/__init__.py b/acestep/ui/gradio/interfaces/__init__.py
index 5e0508be..6b67d20e 100644
--- a/acestep/ui/gradio/interfaces/__init__.py
+++ b/acestep/ui/gradio/interfaces/__init__.py
@@ -1,27 +1,10 @@
"""
Gradio UI Components Module
Contains all Gradio interface component definitions and layouts
-
-Layout:
- ┌──────────────────────────────────────┐
- │ Header │
- ├──────────────────────────────────────┤
- │ Dataset Explorer (hidden accordion) │
- ├──────────────────────────────────────┤
- │ Settings (accordion, collapsed) │
- │ ├─ Service Configuration │
- │ ├─ DiT Parameters │
- │ ├─ LM Parameters │
- │ └─ Output / Automation │
- ├──────────────────────────────────────┤
- │ ┌─ Generation ─┬─ Training ──────┐ │
- │ │ Mode Radio │ Dataset/LoRA │ │
- │ │ Inputs │ │ │
- │ │ Results │ │ │
- │ └───────────────┴────────────────┘ │
- └──────────────────────────────────────┘
"""
+from pathlib import Path
import gradio as gr
+from acestep.ui.gradio.interfaces.theme import AceStepTheme
from acestep.ui.gradio.i18n import get_i18n, t
from acestep.ui.gradio.interfaces.dataset import create_dataset_section
from acestep.ui.gradio.interfaces.generation import (
@@ -54,263 +37,80 @@ def create_gradio_interface(dit_handler, llm_handler, dataset_handler, init_para
# Check if running in service mode (hide training tab)
service_mode = init_params is not None and init_params.get('service_mode', False)
-
- with gr.Blocks(
- title=t("app.title"),
- theme=gr.themes.Soft(),
- css="""
- .main-header {
- text-align: center;
- margin-bottom: 2rem;
- }
- .section-header {
- background: linear-gradient(90deg, #4CAF50, #45a049);
- color: white;
- padding: 10px;
- border-radius: 5px;
- margin: 10px 0;
- }
- .lm-hints-row {
- align-items: stretch;
- }
- .lm-hints-col {
- display: flex;
- }
- .lm-hints-col > div {
- flex: 1;
- display: flex;
- }
- .lm-hints-btn button {
- height: 100%;
- width: 100%;
- }
- /* Position Audio time labels lower to avoid scrollbar overlap */
- .component-wrapper > .timestamps {
- transform: translateY(15px);
- }
- /* Equal-height row for instrumental checkbox + enhance lyrics button */
- .instrumental-row {
- align-items: stretch !important;
- }
- .instrumental-row > div {
- display: flex !important;
- align-items: stretch !important;
- }
- .instrumental-row > div > div {
- flex: 1;
- display: flex;
- align-items: center;
- }
- .instrumental-row button {
- height: 100% !important;
- min-height: 42px;
- }
- /* Ensure buttons in instrumental-row fill height */
- .instrumental-row > div > button {
- height: 100% !important;
- min-height: 42px;
- }
- /* Two-line icon buttons: emoji on top, text below */
- .icon-btn-wrap button, .icon-btn-wrap > button {
- word-spacing: 100vw;
- text-align: center;
- line-height: 1.4;
- }
-
- /* --- On-hover Tooltips --- */
- /* Safely ensure parents don't clip the tooltips using the container class */
- .has-info-container {
- overflow: visible !important;
- contain: none !important;
- }
-
- /* Ensure immediate flex parents (like rows, accordions) also allow overflow if they contain an info container */
- .row:has(.has-info-container),
- .column:has(.has-info-container),
- .form:has(.has-info-container),
- .accordion:has(.has-info-container),
- .tabs:has(.has-info-container),
- .gr-block:has(.has-info-container),
- .gr-box:has(.has-info-container) {
- overflow: visible !important;
- contain: none !important;
- }
-
- /* Hide info text by default and format as tooltip.
- In Gradio 6, info is often a div following the span[data-testid="block-info"]. */
- .has-info-container span[data-testid="block-info"] + div,
- .has-info-container span[data-testid="block-info"] + span,
- .checkbox-container + div {
- display: none;
- position: absolute;
- background: rgba(25, 25, 25, 0.98);
- color: #ffffff;
- padding: 12px 16px;
- border-radius: 10px;
- font-size: 0.85rem;
- z-index: 999999;
- max-width: 320px;
- min-width: 180px;
- box-shadow: 0 8px 25px rgba(0,0,0,0.5);
- pointer-events: none;
- line-height: 1.5;
- margin-top: 6px;
- border: 1px solid rgba(255,255,255,0.15);
- backdrop-filter: blur(10px);
- left: 0;
- font-weight: 400;
- text-transform: none;
- }
- /* Prevent tooltip CSS from hiding content inside .no-tooltip components */
- .no-tooltip span[data-testid="block-info"] + div,
- .no-tooltip span[data-testid="block-info"] + span {
- display: block !important;
- position: static !important;
- background: none !important;
- padding: 0 !important;
- border: none !important;
- box-shadow: none !important;
- backdrop-filter: none !important;
- max-width: none !important;
- min-width: 0 !important;
- z-index: auto !important;
- pointer-events: auto !important;
- margin-top: 0 !important;
- color: inherit !important;
- font-size: inherit !important;
- line-height: inherit !important;
- font-weight: inherit !important;
- text-transform: inherit !important;
- border-radius: 0 !important;
- }
- .no-tooltip span[data-testid="block-info"]::after {
- display: none !important;
- }
+ theme = AceStepTheme()
- /* Show tooltips on hover of the label area or the icon */
- .has-info-container span[data-testid="block-info"]:hover + div,
- .has-info-container span[data-testid="block-info"]:hover + span,
- .checkbox-container:hover + div {
- display: block !important;
- }
+ main_css_file = Path(__file__).parent / "css" / "main.css"
+ main_css = main_css_file.read_text()
- /* High-res info icon using SVG, appended to the label text */
- .has-info-container span[data-testid="block-info"]::after,
- .checkbox-container:has(+ div) .label-text::after {
- content: "";
- display: inline-block;
- width: 14px;
- height: 14px;
- margin-left: 8px;
- vertical-align: middle;
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234a9eff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' y1='16' x2='12' y2='12'/%3E%3Cline x1='12' y1='8' x2='12.01' y2='8'/%3E%3C/svg%3E");
- background-repeat: no-repeat;
- background-size: contain;
- opacity: 0.6;
- transition: opacity 0.2s, transform 0.2s;
- cursor: help;
- }
+ utils_css_file = Path(__file__).parent / "css" / "utils.css"
+ utils_css = utils_css_file.read_text()
- /* Hide original Gradio info icon if present */
- .has-info-container span[data-testid="block-info"] svg,
- .has-info-container span[data-testid="block-info"]::before {
- display: none !important;
- }
+ with gr.Blocks(
+ title=t("app.title"),
+ theme=theme,
+ css = utils_css + main_css + HELP_MODAL_CSS,
+ ) as demo:
- .has-info-container span[data-testid="block-info"]:hover::after,
- .checkbox-container:hover .label-text::after {
- opacity: 1;
- transform: scale(1.15);
- }
+ with gr.Row(equal_height=True, elem_classes=["main-header-container"]):
+ create_help_button("getting_started")
+ gr.HTML(f"""
+
+
{t("app.title")}
+
{t("app.subtitle")}
+
+ """, elem_classes=["no-grow"])
- /* --- Auto-toggle checkbox row --- */
- /* Compact row of Auto checkboxes that mirrors the field row above */
- .auto-toggles-row {
- margin-top: -8px !important;
- margin-bottom: 0 !important;
- padding: 0 !important;
- gap: 16px !important;
- min-height: 0 !important;
- }
- .auto-toggle {
- text-align: center !important;
- }
- .auto-toggle label {
- font-size: 0.8rem !important;
- gap: 4px !important;
- white-space: nowrap !important;
- cursor: pointer !important;
- opacity: 0.5;
- transition: opacity 0.15s;
- justify-content: center !important;
- }
- .auto-toggle:hover label {
- opacity: 1;
- }
- .auto-toggle input[type="checkbox"] {
- width: 13px !important;
- height: 13px !important;
- }
- """ + HELP_MODAL_CSS,
- ) as demo:
-
- gr.HTML(f"""
-
-
{t("app.title")}
-
{t("app.subtitle")}
-
- """)
- create_help_button("getting_started")
-
# Dataset Explorer Section (hidden)
dataset_section = create_dataset_section(dataset_handler)
-
- # ═══════════════════════════════════════════
- # Top-level: Settings (contains Service Config + Advanced Settings)
- # ═══════════════════════════════════════════
- settings_section = create_advanced_settings_section(
- dit_handler, llm_handler, init_params=init_params, language=language
- )
-
- # ═══════════════════════════════════════════
- # Tabs: Generation | Training
- # ═══════════════════════════════════════════
- with gr.Tabs():
- # --- Generation Tab ---
- with gr.Tab(t("generation.tab_title")):
- gen_section = create_generation_tab_section(
+
+ with gr.Row(elem_classes=["gap-6"]):
+ with gr.Column(scale=1):
+ # ═══════════════════════════════════════════
+ # Sidebar: Settings (contains Service Config + Advanced Settings)
+ # ═══════════════════════════════════════════
+ settings_section = create_advanced_settings_section(
dit_handler, llm_handler, init_params=init_params, language=language
)
-
- # Results Section (inside the Generation tab, wrapped for visibility control)
- with gr.Column(visible=True) as results_wrapper:
- results_section = create_results_section(dit_handler)
- # Store the wrapper in gen_section so event handlers can toggle it
- gen_section["results_wrapper"] = results_wrapper
-
- # --- Training Tab ---
- with gr.Tab(t("training.tab_title"), visible=not service_mode):
- training_section = create_training_section(
- dit_handler, llm_handler, init_params=init_params
+ with gr.Column(scale=9):
+ # ═══════════════════════════════════════════
+ # Tabs: Generation | Training
+ # ═══════════════════════════════════════════
+ with gr.Tabs():
+ # --- Generation Tab ---
+ with gr.Tab(t("generation.tab_title")):
+ gen_section = create_generation_tab_section(
+ dit_handler, llm_handler, init_params=init_params, language=language
+ )
+
+ # Results Section (inside the Generation tab, wrapped for visibility control)
+ with gr.Column(visible=True) as results_wrapper:
+ results_section = create_results_section(dit_handler)
+ # Store the wrapper in gen_section so event handlers can toggle it
+ gen_section["results_wrapper"] = results_wrapper
+
+ # --- Training Tab ---
+ with gr.Tab(t("training.tab_title"), visible=not service_mode):
+ training_section = create_training_section(
+ dit_handler, llm_handler, init_params=init_params
+ )
+
+ # ═══════════════════════════════════════════
+ # Merge all generation-related component dicts for event wiring
+ # ═══════════════════════════════════════════
+ # The event handlers expect a single "generation_section" dict with all
+ # components from settings (service config + advanced) and generation tab.
+ generation_section = {}
+ generation_section.update(settings_section)
+ generation_section.update(gen_section)
+
+ # Connect event handlers
+ setup_event_handlers(
+ demo, dit_handler, llm_handler, dataset_handler,
+ dataset_section, generation_section, results_section
)
-
- # ═══════════════════════════════════════════
- # Merge all generation-related component dicts for event wiring
- # ═══════════════════════════════════════════
- # The event handlers expect a single "generation_section" dict with all
- # components from settings (service config + advanced) and generation tab.
- generation_section = {}
- generation_section.update(settings_section)
- generation_section.update(gen_section)
-
- # Connect event handlers
- setup_event_handlers(
- demo, dit_handler, llm_handler, dataset_handler,
- dataset_section, generation_section, results_section
- )
-
- # Connect training event handlers
- setup_training_event_handlers(demo, dit_handler, llm_handler, training_section)
-
+
+ # Connect training event handlers
+ setup_training_event_handlers(demo, dit_handler, llm_handler, training_section)
+
return demo
diff --git a/acestep/ui/gradio/interfaces/css/help_modal.css b/acestep/ui/gradio/interfaces/css/help_modal.css
new file mode 100644
index 00000000..eb43659e
--- /dev/null
+++ b/acestep/ui/gradio/interfaces/css/help_modal.css
@@ -0,0 +1,126 @@
+/* ---- Inline help button container ---- */
+.help-inline-container {
+ min-height: 0 !important;
+ padding: 0 !important;
+ margin: 0 !important;
+ display: inline-flex !important;
+ align-items: center !important;
+ flex-shrink: 0 !important;
+ max-width: 32px !important;
+ min-width: 32px !important;
+ overflow: visible !important;
+}
+
+.help-inline-wrapper {
+ display: inline-flex;
+ align-items: center;
+ line-height: 1;
+}
+
+/* ---- Inline help button ---- */
+.help-inline-btn {
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ border: 1.5px solid var(--border-color-primary, #555);
+ background: transparent;
+ color: var(--body-text-color-subdued, #888);
+ font-size: 12px;
+ font-weight: 600;
+ line-height: 20px;
+ text-align: center;
+ cursor: pointer;
+ padding: 0;
+ transition: all 0.15s ease;
+ flex-shrink: 0;
+}
+
+.help-inline-btn:hover {
+ color: var(--color-accent, #4a9eff);
+ border-color: var(--color-accent, #4a9eff);
+ transform: scale(1.1);
+}
+
+/* ---- Modal overlay ---- */
+.help-modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 100000;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.help-modal-content {
+ background: var(--background-fill-primary, #fff);
+ color: var(--body-text-color, #222);
+ border-radius: 12px;
+ max-width: 640px;
+ width: 90%;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
+ position: relative;
+}
+
+.help-modal-close {
+ position: absolute;
+ top: 12px;
+ right: 16px;
+ background: none;
+ border: none;
+ font-size: 20px;
+ cursor: pointer;
+ color: var(--body-text-color, #222);
+ z-index: 1;
+ opacity: 0.6;
+}
+
+.help-modal-close:hover {
+ opacity: 1;
+}
+
+.help-modal-body {
+ padding: 28px 32px;
+ overflow-y: auto;
+ line-height: 1.7;
+ font-size: 0.92rem;
+}
+
+.help-modal-body h3 {
+ margin: 16px 0 8px;
+ font-size: 1.15rem;
+}
+
+.help-modal-body h4 {
+ margin: 12px 0 6px;
+ font-size: 1.0rem;
+}
+
+.help-modal-body pre {
+ background: var(--background-fill-secondary, #f5f5f5);
+ padding: 10px;
+ border-radius: 6px;
+ overflow-x: auto;
+ font-size: 0.85rem;
+}
+
+.help-modal-body code {
+ background: var(--background-fill-secondary, #f5f5f5);
+ padding: 1px 4px;
+ border-radius: 3px;
+ font-size: 0.88em;
+}
+
+.help-modal-body ul {
+ margin: 6px 0;
+}
+
+.help-modal-body li {
+ margin: 3px 0;
+}
\ No newline at end of file
diff --git a/acestep/ui/gradio/interfaces/css/main.css b/acestep/ui/gradio/interfaces/css/main.css
new file mode 100644
index 00000000..7e6c0d35
--- /dev/null
+++ b/acestep/ui/gradio/interfaces/css/main.css
@@ -0,0 +1,277 @@
+.main-header {
+ text-align: center;
+ margin-bottom: 2rem;
+}
+
+.section-header {
+ background: linear-gradient(90deg, #4CAF50, #45a049);
+ color: white;
+ padding: 10px;
+ border-radius: 5px;
+ margin: 10px 0;
+}
+
+.lm-hints-row {
+ align-items: stretch;
+}
+
+.lm-hints-col {
+ display: flex;
+}
+
+.lm-hints-col>div {
+ flex: 1;
+ display: flex;
+}
+
+.lm-hints-btn button {
+ height: 100%;
+ width: 100%;
+}
+
+/* Position Audio time labels lower to avoid scrollbar overlap */
+.component-wrapper>.timestamps {
+ transform: translateY(15px);
+}
+
+/* Ensure buttons in instrumental-row fill height */
+.instrumental-row>div>button {
+ height: 100% !important;
+ min-height: 42px;
+}
+
+/* Two-line icon buttons: emoji on top, text below */
+.icon-btn-wrap button,
+.icon-btn-wrap>button {
+ word-spacing: 100vw;
+ text-align: center;
+ line-height: 1.4;
+}
+
+/* --- On-hover Tooltips --- */
+/* Safely ensure parents don't clip the tooltips using the container class */
+.has-info-container {
+ overflow: visible !important;
+ contain: none !important;
+}
+
+/* Ensure immediate flex parents (like rows, accordions) also allow overflow if they contain an info container */
+.row:has(.has-info-container),
+.column:has(.has-info-container),
+.form:has(.has-info-container),
+.accordion:has(.has-info-container),
+.tabs:has(.has-info-container),
+.gr-block:has(.has-info-container),
+.gr-box:has(.has-info-container) {
+ overflow: visible !important;
+ contain: none !important;
+}
+
+/* Hide info text by default and format as tooltip.
+ In Gradio 6, info is often a div following the span[data-testid="block-info"]. */
+.has-info-container span[data-testid="block-info"]+div,
+.has-info-container span[data-testid="block-info"]+span,
+.checkbox-container+div {
+ display: none;
+ position: absolute;
+ background: rgba(25, 25, 25, 0.98);
+ color: #ffffff;
+ padding: 12px 16px;
+ border-radius: 10px;
+ font-size: 0.85rem;
+ z-index: 999999;
+ max-width: 320px;
+ min-width: 180px;
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.5);
+ pointer-events: none;
+ line-height: 1.5;
+ margin-top: 6px;
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ backdrop-filter: blur(10px);
+ left: 0;
+ font-weight: 400;
+ text-transform: none;
+}
+
+/* Prevent tooltip CSS from hiding content inside .no-tooltip components */
+.no-tooltip span[data-testid="block-info"]+div,
+.no-tooltip span[data-testid="block-info"]+span {
+ display: block !important;
+ position: static !important;
+ background: none !important;
+ padding: 0 !important;
+ border: none !important;
+ box-shadow: none !important;
+ backdrop-filter: none !important;
+ max-width: none !important;
+ min-width: 0 !important;
+ z-index: auto !important;
+ pointer-events: auto !important;
+ margin-top: 0 !important;
+ color: inherit !important;
+ font-size: inherit !important;
+ line-height: inherit !important;
+ font-weight: inherit !important;
+ text-transform: inherit !important;
+ border-radius: 0 !important;
+}
+
+.no-tooltip span[data-testid="block-info"]::after {
+ display: none !important;
+}
+
+/* Show tooltips on hover of the label area or the icon */
+.has-info-container span[data-testid="block-info"]:hover+div,
+.has-info-container span[data-testid="block-info"]:hover+span,
+.checkbox-container:hover+div {
+ display: block !important;
+}
+
+/* High-res info icon using SVG, appended to the label text */
+.has-info-container span[data-testid="block-info"]::after,
+.checkbox-container:has(+ div) .label-text::after {
+ content: "";
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ margin-left: 8px;
+ vertical-align: middle;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%234a9eff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' y1='16' x2='12' y2='12'/%3E%3Cline x1='12' y1='8' x2='12.01' y2='8'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-size: contain;
+ opacity: 0.6;
+ transition: opacity 0.2s, transform 0.2s;
+ cursor: help;
+}
+
+/* Hide original Gradio info icon if present */
+.has-info-container span[data-testid="block-info"] svg,
+.has-info-container span[data-testid="block-info"]::before {
+ display: none !important;
+}
+
+.has-info-container span[data-testid="block-info"]:hover::after,
+.checkbox-container:hover .label-text::after {
+ opacity: 1;
+ transform: scale(1.15);
+}
+
+/* --- Auto-toggle checkbox row --- */
+/* Compact row of Auto checkboxes that mirrors the field row above */
+.auto-toggles-row {
+ margin-top: -8px !important;
+ margin-bottom: 0 !important;
+ padding: 0 !important;
+ gap: 16px !important;
+ min-height: 0 !important;
+}
+
+.auto-toggle label {
+ font-size: 0.8rem !important;
+ gap: 4px !important;
+ white-space: nowrap !important;
+ cursor: pointer !important;
+ justify-content: center !important;
+}
+
+.auto-toggle:hover label {
+ opacity: 1;
+}
+
+.auto-toggle {
+ padding: 0;
+ padding-top: 25px;
+}
+
+.auto-toggle input[type="checkbox"] {
+ width: 13px !important;
+ height: 13px !important;
+}
+
+.fillable {
+ padding-inline-start: 16px !important;
+}
+
+.has-info::after {
+ width: 16px;
+ height: 16px;
+ opacity: 0.75;
+}
+
+body:not(.dark) .has-info::after {
+ filter: saturate(0) brightness(0);
+}
+
+body.dark .has-info::after {
+ filter: saturate(0) brightness(2);
+}
+
+body.dark input[type="range"]::-webkit-slider-runnable-track,
+body.dark input[type="range"]::-moz-range-track {
+ background: var(--neutral-600);
+}
+
+.btn-generate {
+ min-width: 500px;
+ padding: var(--size-3);
+ margin-top: var(--size-4) !important;
+ margin-bottom: var(--size-4) !important;
+}
+
+.btn-primary-important:not(:disabled) {
+ background: #5f00a6;
+ color: white;
+}
+
+.btn-primary-important:not(:disabled):hover {
+ background: #6d00bf;
+}
+
+body.dark .btn-primary-important:disabled {
+ background: white;
+}
+
+.main-header-container {
+ justify-content: center;
+ gap: var(--size-4);
+}
+
+.main-header-container .help-inline-btn {
+ width: 30px;
+ height: 30px;
+}
+
+.optional-params-container .form {
+ background: transparent;
+}
+
+.optional-param-setting-row {
+ align-items: center;
+ justify-content: space-between;
+}
+
+.optional-param-setting {
+ overflow: visible !important;
+ contain: none !important;
+ margin: 0;
+ padding: 0;
+}
+
+body:not(.dark) {
+ --color-accent: black;
+}
+
+@media (max-width: 1024px) {
+ .btn-generate {
+ min-width: 100%;
+ padding: var(--size-2);
+ margin-top: var(--size-3) !important;
+ margin-bottom: var(--size-3) !important;
+ }
+}
+
+@media (max-width: 480px) {
+ .main-header-container {
+ gap: 0;
+ }
+}
\ No newline at end of file
diff --git a/acestep/ui/gradio/interfaces/css/utils.css b/acestep/ui/gradio/interfaces/css/utils.css
new file mode 100644
index 00000000..ba81a9bb
--- /dev/null
+++ b/acestep/ui/gradio/interfaces/css/utils.css
@@ -0,0 +1,141 @@
+.no-grow {
+ flex: 0 0 auto !important;
+ width: auto !important;
+ display: inline-block !important;
+}
+
+.bg-fill {
+ background: var(--block-background-fill);
+}
+
+.text-center {
+ text-align: center;
+}
+
+.margin-auto {
+ margin: 0 auto;
+}
+
+.justify-content-center {
+ justify-content: center;
+}
+
+.justify-content-start {
+ justify-content: flex-start;
+}
+
+.justify-content-end {
+ justify-content: flex-end;
+}
+
+.justify-content-between {
+ justify-content: space-between;
+}
+
+.align-items-center {
+ align-items: center;
+}
+
+.align-self-center {
+ align-self: center;
+}
+
+.align-self-start {
+ align-self: flex-start;
+}
+
+.align-self-end {
+ align-self: flex-end;
+}
+
+.justify-self-center {
+ justify-self: center;
+}
+
+.p-0 {
+ padding: 0;
+}
+
+.p-1 {
+ padding: var(--size-1);
+}
+
+.p-2 {
+ padding: var(--size-2);
+}
+
+.p-3 {
+ padding: var(--size-3);
+}
+
+.p-4 {
+ padding: var(--size-4);
+}
+
+.ps-3 {
+ padding-inline-start: var(--size-3);
+}
+
+.ps-4 {
+ padding-inline-start: var(--size-4);
+}
+
+.pe-3 {
+ padding-inline-end: var(--size-3);
+}
+
+.pe-4 {
+ padding-inline-end: var(--size-4);
+}
+
+.gap-1 {
+ gap: var(--size-1);
+}
+
+.gap-2 {
+ gap: var(--size-2);
+}
+
+.gap-3 {
+ gap: var(--size-3);
+}
+
+.gap-4 {
+ gap: var(--size-4);
+}
+
+.gap-5 {
+ gap: var(--size-5);
+}
+
+.gap-6 {
+ gap: var(--size-6);
+}
+
+.m-0 {
+ margin: 0;
+}
+
+.me-1 {
+ margin-inline-end: var(--size-1);
+}
+
+.me-2 {
+ margin-inline-end: var(--size-2);
+}
+
+.mt-md {
+ margin-top: var(--spacing-md);
+}
+
+.mt-xl {
+ margin-top: var(--spacing-xl);
+}
+
+.mb-1 {
+ margin-bottom: var(--size-1);
+}
+
+.mb-2 {
+ margin-bottom: var(--size-2);
+}
\ No newline at end of file
diff --git a/acestep/ui/gradio/interfaces/generation.py b/acestep/ui/gradio/interfaces/generation.py
index dc99d433..aefb60bb 100644
--- a/acestep/ui/gradio/interfaces/generation.py
+++ b/acestep/ui/gradio/interfaces/generation.py
@@ -196,12 +196,12 @@ def _create_service_config_content(dit_handler, llm_handler, defaults, init_para
)
# Checkboxes
- with gr.Row():
+ with gr.Column():
init_llm_value = init_params.get('init_llm', init_lm_default) if service_pre_initialized else init_lm_default
lm_info_text = t("service.init_llm_info")
if not gpu_config.available_lm_models:
lm_info_text += " ⚠️ LM not available for this GPU tier (VRAM too low)"
- init_llm_checkbox = gr.Checkbox(label=t("service.init_llm_label"), value=init_llm_value, info=lm_info_text)
+ init_llm_checkbox = gr.Checkbox(label=t("service.init_llm_label"), value=init_llm_value, info=lm_info_text, elem_classes=["has-info-container"])
flash_attn_available = dit_handler.is_flash_attention_available(device_value)
use_flash_attention_value = init_params.get('use_flash_attention', flash_attn_available) if service_pre_initialized else flash_attn_available
@@ -248,7 +248,9 @@ def _create_service_config_content(dit_handler, llm_handler, defaults, init_para
init_btn = gr.Button(t("service.init_btn"), variant="primary", size="lg")
init_status_value = init_params.get('init_status', '') if service_pre_initialized else ''
- init_status = gr.Textbox(label=t("service.status_label"), interactive=False, lines=3, value=init_status_value)
+
+ with gr.Row():
+ init_status = gr.Textbox(label=t("service.status_label"), interactive=False, lines=3, value=init_status_value, elem_classes=["has-info-container"])
return {
"service_config_accordion": service_config_accordion,
@@ -308,9 +310,8 @@ def create_advanced_settings_section(dit_handler, llm_handler, init_params=None,
else:
_ui_config = get_ui_control_config(True)
- # Auto-expand Settings when service is not yet initialized so users can init
- settings_open = not service_pre_initialized
- with gr.Accordion(t("generation.advanced_settings"), open=settings_open) as advanced_settings_accordion:
+ with gr.Column() as advanced_settings_section:
+ gr.HTML(f'{t("generation.advanced_settings")}
')
# ═══════════════════════════════════════════
# Service Configuration (sub-accordion)
@@ -324,13 +325,17 @@ def create_advanced_settings_section(dit_handler, llm_handler, init_params=None,
# ═══════════════════════════════════════════
with gr.Accordion("🔧 LoRA Adapter", open=False, elem_classes=["has-info-container"]):
with gr.Row():
- lora_path = gr.Textbox(label="LoRA Path", placeholder="./lora_output/final/adapter", info="Path to trained LoRA adapter directory", scale=3)
- load_lora_btn = gr.Button("📥 Load LoRA", variant="secondary", scale=1)
- unload_lora_btn = gr.Button("🗑️ Unload", variant="secondary", scale=1)
+ with gr.Column():
+ lora_path = gr.Textbox(label="LoRA Path", placeholder="./lora_output/final/adapter", info="Path to trained LoRA adapter directory", scale=3, elem_classes=["has-info-container"])
+ load_lora_btn = gr.Button("📥 Load LoRA", variant="secondary", scale=1)
+ unload_lora_btn = gr.Button("🗑️ Unload", variant="secondary", scale=1)
+ with gr.Row():
+ with gr.Column():
+ use_lora_checkbox = gr.Checkbox(label="Use LoRA", value=False, info="Enable LoRA adapter for inference", scale=1, elem_classes=["has-info-container"])
+ lora_scale_slider = gr.Slider(minimum=0.0, maximum=1.0, value=1.0, step=0.05, label="LoRA Scale", info="LoRA influence strength (0=disabled, 1=full)", scale=2, elem_classes=["has-info-container"])
+
with gr.Row():
- use_lora_checkbox = gr.Checkbox(label="Use LoRA", value=False, info="Enable LoRA adapter for inference", scale=1)
- lora_scale_slider = gr.Slider(minimum=0.0, maximum=1.0, value=1.0, step=0.05, label="LoRA Scale", info="LoRA influence strength (0=disabled, 1=full)", scale=2)
- lora_status = gr.Textbox(label="LoRA Status", value="No LoRA loaded", interactive=False, lines=1, elem_classes=["no-tooltip"])
+ lora_status = gr.Textbox(label="LoRA Status", value="No LoRA loaded", interactive=False, lines=1, elem_classes=["no-tooltip"])
# ═══════════════════════════════════════════
# DiT Diffusion Parameters (with help button)
@@ -411,16 +416,19 @@ def create_advanced_settings_section(dit_handler, llm_handler, init_params=None,
lines=2,
)
with gr.Row():
- use_cot_metas = gr.Checkbox(label=t("generation.cot_metas_label"), value=True, info=t("generation.cot_metas_info"), scale=1, elem_classes=["has-info-container"])
- use_cot_language = gr.Checkbox(label=t("generation.cot_language_label"), value=True, info=t("generation.cot_language_info"), scale=1, elem_classes=["has-info-container"])
- constrained_decoding_debug = gr.Checkbox(
- label=t("generation.constrained_debug_label"), value=False,
- info=t("generation.constrained_debug_info"), scale=1,
- interactive=not service_mode,
- )
+ with gr.Column():
+ use_cot_metas = gr.Checkbox(label=t("generation.cot_metas_label"), value=True, info=t("generation.cot_metas_info"), scale=1, elem_classes=["has-info-container"])
+ use_cot_language = gr.Checkbox(label=t("generation.cot_language_label"), value=True, info=t("generation.cot_language_info"), scale=1, elem_classes=["has-info-container"])
+ constrained_decoding_debug = gr.Checkbox(
+ label=t("generation.constrained_debug_label"), value=False,
+ info=t("generation.constrained_debug_info"), scale=1,
+ interactive=not service_mode,
+ elem_classes=["has-info-container"]
+ )
with gr.Row():
- allow_lm_batch = gr.Checkbox(label=t("generation.parallel_thinking_label"), value=True, info=t("generation.parallel_thinking_info"), scale=1, elem_classes=["has-info-container"])
- use_cot_caption = gr.Checkbox(label=t("generation.caption_rewrite_label"), value=False, info=t("generation.caption_rewrite_info"), scale=1, elem_classes=["has-info-container"])
+ with gr.Column():
+ allow_lm_batch = gr.Checkbox(label=t("generation.parallel_thinking_label"), value=True, info=t("generation.parallel_thinking_info"), scale=1, elem_classes=["has-info-container"])
+ use_cot_caption = gr.Checkbox(label=t("generation.caption_rewrite_label"), value=False, info=t("generation.caption_rewrite_info"), scale=1, elem_classes=["has-info-container"])
# ═══════════════════════════════════════════
# Audio Output & Post-processing
@@ -441,10 +449,11 @@ def create_advanced_settings_section(dit_handler, llm_handler, init_params=None,
scale=1, visible=not service_mode,
)
with gr.Row():
- enable_norm_val = init_params.get("enable_normalization", True) if service_pre_initialized else True
- norm_db_val = init_params.get("normalization_db", -1.0) if service_pre_initialized else -1.0
- enable_normalization = gr.Checkbox(label=t("gen.enable_normalization"), value=enable_norm_val, info=t("gen.enable_normalization_info"), elem_classes=["has-info-container"])
- normalization_db = gr.Slider(label=t("gen.normalization_db"), minimum=-10.0, maximum=0.0, step=0.1, value=norm_db_val, info=t("gen.normalization_db_info"), elem_classes=["has-info-container"])
+ with gr.Column():
+ enable_norm_val = init_params.get("enable_normalization", True) if service_pre_initialized else True
+ norm_db_val = init_params.get("normalization_db", -1.0) if service_pre_initialized else -1.0
+ enable_normalization = gr.Checkbox(label=t("gen.enable_normalization"), value=enable_norm_val, info=t("gen.enable_normalization_info"), elem_classes=["has-info-container"])
+ normalization_db = gr.Slider(label=t("gen.normalization_db"), minimum=-10.0, maximum=0.0, step=0.1, value=norm_db_val, info=t("gen.normalization_db_info"), elem_classes=["has-info-container"])
with gr.Row():
latent_shift_val = init_params.get("latent_shift", 0.0) if service_pre_initialized else 0.0
latent_rescale_val = init_params.get("latent_rescale", 1.0) if service_pre_initialized else 1.0
@@ -460,7 +469,7 @@ def create_advanced_settings_section(dit_handler, llm_handler, init_params=None,
# Merge service components into the return dict
result = {
- "advanced_settings_accordion": advanced_settings_accordion,
+ "advanced_settings_section": advanced_settings_section,
"inference_steps": inference_steps,
"guidance_scale": guidance_scale,
"infer_method": infer_method,
@@ -598,7 +607,10 @@ def create_generation_tab_section(dit_handler, llm_handler, init_params=None, la
with gr.Row(equal_height=True):
create_sample_btn = gr.Button(
- t("generation.create_sample_btn"), variant="primary", size="lg",
+ t("generation.create_sample_btn"),
+ variant="primary",
+ elem_classes=["btn-primary-important", "btn-generate", "no-grow", "margin-auto"],
+ size="lg",
)
simple_sample_created = gr.State(value=False)
@@ -677,7 +689,8 @@ def create_generation_tab_section(dit_handler, llm_handler, init_params=None, la
audio_cover_strength = gr.Slider(
minimum=0.0, maximum=1.0, value=1.0, step=0.01,
label=t("generation.codes_strength_label"),
- info=t("generation.codes_strength_info"), elem_classes=["has-info-container"],
+ info=t("generation.codes_strength_info"),
+ elem_classes=["has-info-container"],
visible=True,
)
@@ -693,46 +706,135 @@ def create_generation_tab_section(dit_handler, llm_handler, init_params=None, la
# --- Custom Mode: Reference Audio | (Caption + Enhance) | (Lyrics + Instrumental + Enhance) | 🎲 ---
with gr.Group(visible=True, elem_classes=["has-info-container"]) as custom_mode_group:
- create_help_button("generation_custom")
- with gr.Row(equal_height=True):
- # Left: Reference Audio
- with gr.Column(scale=2, min_width=200):
- reference_audio = gr.Audio(
- label=t("generation.reference_audio"),
- type="filepath",
- show_label=True,
- )
-
- # Middle: Caption column + Lyrics column
- with gr.Column(scale=8):
- with gr.Row(equal_height=True):
- # Caption sub-column
+ with gr.Row(elem_classes=["bg-fill", "p-3", "gap-3", "align-items-center", "mt-xl", "mb-2"]):
+ gr.HTML(
+ f'{t("generation.tab_title")}
',
+ elem_classes=["no-grow"]
+ )
+ create_help_button("generation_custom")
+
+ with gr.Row(elem_classes=["justify-content-end"]):
+ sample_btn = gr.Button(t("generation.sample_btn"), variant="secondary", size="sm", elem_classes=["no-grow"]) # random example button
+
+ with gr.Column():
+ with gr.Row(elem_classes=["gap-3", "ps-4", "pe-4"]):
+ # Left: Reference Audio
+ with gr.Column(scale=1, min_width=200, elem_classes=["align-self-center"]):
+ reference_audio = gr.Audio(
+ label=t("generation.reference_audio"),
+ type="filepath",
+ show_label=True,
+ )
+
+ with gr.Column(scale=2, elem_classes=["gap-2"]):
+ # Caption
with gr.Column(scale=1):
+ with gr.Row(elem_classes=["ps-3", "pe-3", "gap-4", "bg-fill", "align-items-center", "justify-content-between"]):
+ gr.HTML(f'{t("generation.caption_label")}
', elem_classes="no-grow")
+ format_caption_btn = gr.Button(t("generation.format_caption_btn"), variant="secondary", size="sm", elem_classes="no-grow")
+
captions = gr.Textbox(
+ show_label=False,
label=t("generation.caption_label"),
placeholder=t("generation.caption_placeholder"),
lines=12,
max_lines=12,
)
- with gr.Row(elem_classes="instrumental-row"):
- format_caption_btn = gr.Button(t("generation.format_caption_btn"), variant="secondary", size="sm")
- # Lyrics sub-column
+
+ # Lyrics
with gr.Column(scale=1):
+ with gr.Row(elem_classes=["bg-fill", "ps-3", "pe-3", "justify-content-between", "align-items-center"]):
+ with gr.Row(elem_classes=["gap-4", "align-items-center"]):
+ gr.HTML(
+ f'{t("generation.lyrics_label")}
',
+ elem_classes="no-grow"
+ )
+
+ instrumental_checkbox = gr.Checkbox(
+ label=t("generation.instrumental_label"),
+ value=False,
+ scale=0,
+ elem_classes="p-0"
+ )
+
+ format_lyrics_btn = gr.Button(t("generation.format_lyrics_btn"), variant="secondary", size="sm", elem_classes="no-grow")
+
lyrics = gr.Textbox(
+ show_label=False,
label=t("generation.lyrics_label"),
placeholder=t("generation.lyrics_placeholder"),
lines=12,
max_lines=12,
)
- with gr.Row(elem_classes="instrumental-row"):
- instrumental_checkbox = gr.Checkbox(
- label=t("generation.instrumental_label"), value=False, scale=1,
+
+ with gr.Column(elem_classes=["has-info-container", "optional-params-container"], scale=1) as optional_params_section:
+ with gr.Row():
+ gr.HTML(f'{t("generation.optional_params")}
')
+ reset_all_auto_btn = gr.Button(t("generation.reset_all_auto"), variant="secondary", size="sm", elem_classes=["no-grow"])
+
+ with gr.Column(elem_classes=["gap-2"]):
+ with gr.Row(elem_classes=["optional-param-setting-row"]):
+ bpm_auto = gr.Checkbox(label=t("generation.bpm_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
+ bpm = gr.Number(
+ label=t("generation.bpm_label"),
+ value=None,
+ step=1,
+ info=t("generation.bpm_info"),
+ elem_classes=["optional-param-setting", "has-info-container"],
+ interactive=False
)
- format_lyrics_btn = gr.Button(t("generation.format_lyrics_btn"), variant="secondary", size="sm", scale=2)
- # Right column: 🎲 Random
- with gr.Column(scale=1, min_width=80, elem_classes="icon-btn-wrap"):
- sample_btn = gr.Button(t("generation.sample_btn"), variant="primary", size="lg")
+ with gr.Row(elem_classes=["optional-param-setting-row"]):
+ key_auto = gr.Checkbox(label=t("generation.key_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
+ key_scale = gr.Textbox(
+ label=t("generation.keyscale_label"),
+ placeholder=t("generation.keyscale_placeholder"),
+ value="",
+ info=t("generation.keyscale_info"),
+ elem_classes=["optional-param-setting", "has-info-container"],
+ interactive=False
+ )
+
+ with gr.Row(elem_classes=["optional-param-setting-row"]):
+ timesig_auto = gr.Checkbox(label=t("generation.timesig_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
+ time_signature = gr.Dropdown(
+ choices=["", "2", "3", "4", "6", "N/A"],
+ value="",
+ label=t("generation.timesig_label"),
+ allow_custom_value=True,
+ info=t("generation.timesig_info"),
+ elem_classes=["optional-param-setting", "has-info-container"],
+ interactive=False
+ )
+
+ with gr.Row(elem_classes=["optional-param-setting-row"]):
+ vocal_lang_auto = gr.Checkbox(label=t("generation.vocal_lang_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
+ vocal_language = gr.Dropdown(
+ choices=[(lang if lang != "unknown" else "Instrumental / auto", lang) for lang in VALID_LANGUAGES], value="unknown",
+ label=t("generation.vocal_language_label"),
+ info=t("generation.vocal_language_info"),
+ allow_custom_value=True,
+ elem_classes=["optional-param-setting", "has-info-container"],
+ interactive=False,
+ )
+
+ with gr.Row(elem_classes=["optional-param-setting-row"]):
+ duration_auto = gr.Checkbox(label=t("generation.duration_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
+ audio_duration = gr.Number(
+ label=t("generation.duration_label"), value=-1, minimum=-1,
+ maximum=float(max_duration), step=0.1,
+ info=t("generation.duration_info") + f" (Max: {max_duration}s / {max_duration // 60} min)",
+ elem_classes=["optional-param-setting", "has-info-container"],
+ interactive=False,
+ )
+
+ batch_size_input = gr.Number(
+ label=t("generation.batch_size_label"), value=default_batch_size,
+ minimum=1, maximum=max_batch_size, step=1,
+ info=t("generation.batch_size_info") + f" (Max: {max_batch_size})",
+ elem_classes=["has-info-container", "no-grow", "p-0", "align-self-end", "m-0"],
+ interactive=not service_mode,
+ )
# --- Repainting controls (also used for Lego stem area) ---
with gr.Group(visible=False) as repainting_group:
@@ -742,58 +844,28 @@ def create_generation_tab_section(dit_handler, llm_handler, init_params=None, la
repainting_start = gr.Number(label=t("generation.repainting_start"), value=0.0, step=0.1)
repainting_end = gr.Number(label=t("generation.repainting_end"), value=-1, minimum=-1, step=0.1)
- # --- Optional Parameters (collapsed by default) ---
- with gr.Accordion(t("generation.optional_params"), open=False, visible=True, elem_classes=["has-info-container"]) as optional_params_accordion:
- with gr.Row():
- bpm = gr.Number(label=t("generation.bpm_label"), value=None, step=1, info=t("generation.bpm_info"), elem_classes=["has-info-container"], interactive=False)
- key_scale = gr.Textbox(label=t("generation.keyscale_label"), placeholder=t("generation.keyscale_placeholder"), value="", info=t("generation.keyscale_info"), elem_classes=["has-info-container"], interactive=False)
- time_signature = gr.Dropdown(choices=["", "2", "3", "4", "6", "N/A"], value="", label=t("generation.timesig_label"), allow_custom_value=True, info=t("generation.timesig_info"), elem_classes=["has-info-container"], interactive=False)
- vocal_language = gr.Dropdown(
- choices=[(lang if lang != "unknown" else "Instrumental / auto", lang) for lang in VALID_LANGUAGES], value="unknown",
- label=t("generation.vocal_language_label"),
- info=t("generation.vocal_language_info"),
- allow_custom_value=True,
- elem_classes=["has-info-container"],
- interactive=False,
- )
- with gr.Row(elem_classes=["auto-toggles-row"]):
- bpm_auto = gr.Checkbox(label=t("generation.bpm_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
- key_auto = gr.Checkbox(label=t("generation.key_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
- timesig_auto = gr.Checkbox(label=t("generation.timesig_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
- vocal_lang_auto = gr.Checkbox(label=t("generation.vocal_lang_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
- with gr.Row():
- audio_duration = gr.Number(
- label=t("generation.duration_label"), value=-1, minimum=-1,
- maximum=float(max_duration), step=0.1,
- info=t("generation.duration_info") + f" (Max: {max_duration}s / {max_duration // 60} min)", elem_classes=["has-info-container"],
- interactive=False,
- )
- batch_size_input = gr.Number(
- label=t("generation.batch_size_label"), value=default_batch_size,
- minimum=1, maximum=max_batch_size, step=1,
- info=t("generation.batch_size_info") + f" (Max: {max_batch_size})", elem_classes=["has-info-container"],
- interactive=not service_mode,
- )
- with gr.Row(elem_classes=["auto-toggles-row"]):
- duration_auto = gr.Checkbox(label=t("generation.duration_auto_label"), value=True, container=False, elem_classes=["auto-toggle"])
- gr.HTML("") # spacer to align with batch_size column
- reset_all_auto_btn = gr.Button(t("generation.reset_all_auto"), variant="secondary", size="sm")
-
# --- Generate Button Row (hidden in Simple mode) ---
generate_btn_interactive = init_params.get('enable_generate', False) if service_pre_initialized else False
- with gr.Row(equal_height=True, visible=True) as generate_btn_row:
- with gr.Column(scale=1, variant="compact"):
- think_checkbox = gr.Checkbox(label=t("generation.think_label"), value=lm_initialized, scale=1, interactive=lm_initialized)
- auto_score = gr.Checkbox(label=t("generation.auto_score_label"), value=False, scale=1, interactive=not service_mode)
- with gr.Column(scale=18):
- generate_btn = gr.Button(t("generation.generate_btn"), variant="primary", size="lg", interactive=generate_btn_interactive)
- with gr.Column(scale=1, variant="compact"):
+
+ with gr.Column(visible=True) as generate_btn_row:
+ with gr.Row(elem_classes=["bg-fill", "justify-content-center"]):
+ think_checkbox = gr.Checkbox(label=t("generation.think_label"), value=lm_initialized, scale=0, interactive=lm_initialized)
+ auto_score = gr.Checkbox(label=t("generation.auto_score_label"), value=False, scale=0, interactive=not service_mode)
autogen_checkbox = gr.Checkbox(
- label=t("generation.autogen_label"), value=False, scale=1,
+ label=t("generation.autogen_label"), value=False, scale=0,
interactive=not service_mode,
)
- auto_lrc = gr.Checkbox(label=t("generation.auto_lrc_label"), value=False, scale=1, interactive=not service_mode)
-
+ auto_lrc = gr.Checkbox(label=t("generation.auto_lrc_label"), value=False, scale=0, interactive=not service_mode)
+
+ with gr.Row(elem_classes=["justify-content-center"]):
+ generate_btn = gr.Button(
+ t("generation.generate_btn"),
+ variant="primary",
+ elem_classes=["btn-primary-important", "btn-generate", "no-grow"],
+ size="lg",
+ interactive=generate_btn_interactive
+ )
+
return {
"generation_mode": generation_mode,
"task_type": task_type,
@@ -834,7 +906,7 @@ def create_generation_tab_section(dit_handler, llm_handler, init_params=None, la
"vocal_language": vocal_language,
"format_caption_btn": format_caption_btn,
"format_lyrics_btn": format_lyrics_btn,
- "optional_params_accordion": optional_params_accordion,
+ "optional_params_section": optional_params_section,
"bpm": bpm,
"key_scale": key_scale,
"time_signature": time_signature,
diff --git a/acestep/ui/gradio/interfaces/result.py b/acestep/ui/gradio/interfaces/result.py
index 1a489148..9036b972 100644
--- a/acestep/ui/gradio/interfaces/result.py
+++ b/acestep/ui/gradio/interfaces/result.py
@@ -36,7 +36,7 @@ def _create_audio_column(n, visible=True):
)
save_btn = gr.Button(
t("results.save_btn"),
- variant="primary", size="sm", scale=1
+ variant="secondary", size="sm", scale=1
)
with gr.Accordion(t("results.details_accordion"), open=False, visible=True) as details_accordion:
codes_display = gr.Textbox(
@@ -144,7 +144,7 @@ def create_results_section(dit_handler) -> dict:
)
next_batch_btn = gr.Button(
t("results.next_btn"),
- variant="primary", interactive=False, scale=1, size="sm"
+ variant="secondary", interactive=False, scale=1, size="sm"
)
# One-click restore parameters button
diff --git a/acestep/ui/gradio/interfaces/theme.py b/acestep/ui/gradio/interfaces/theme.py
new file mode 100644
index 00000000..a7139102
--- /dev/null
+++ b/acestep/ui/gradio/interfaces/theme.py
@@ -0,0 +1,127 @@
+from __future__ import annotations
+from typing import Iterable
+import gradio as gr
+from gradio.themes.utils import colors, fonts, sizes
+
+
+class AceStepTheme(gr.themes.Base):
+ def __init__(
+ self,
+ *,
+ primary_hue: colors.Color | str = colors.slate,
+ secondary_hue: colors.Color | str = colors.indigo,
+ neutral_hue: colors.Color | str = colors.gray,
+ spacing_size: sizes.Size | str = sizes.spacing_md,
+ radius_size: sizes.Size | str = sizes.radius_md,
+ text_size: sizes.Size | str = sizes.text_md,
+ font: fonts.Font
+ | str
+ | Iterable[fonts.Font | str] = (
+ fonts.LocalFont("Montserrat"),
+ "ui-sans-serif",
+ "system-ui",
+ "sans-serif",
+ ),
+ font_mono: fonts.Font
+ | str
+ | Iterable[fonts.Font | str] = (
+ fonts.LocalFont("IBM Plex Mono"),
+ "ui-monospace",
+ "Consolas",
+ "monospace",
+ ),
+ ):
+ super().__init__(
+ primary_hue=primary_hue,
+ secondary_hue=secondary_hue,
+ neutral_hue=neutral_hue,
+ spacing_size=spacing_size,
+ radius_size=radius_size,
+ text_size=text_size,
+ font=font,
+ font_mono=font_mono,
+ )
+
+ super().set(
+ # Custom values
+ button_primary_background_fill="*neutral_950",
+ button_primary_background_fill_dark="*primary_100",
+ button_primary_text_color_dark="*neutral_950",
+ button_secondary_background_fill="*primary_200",
+ button_secondary_text_color="*neutral_950",
+ body_background_fill="#f3f4f5",
+ block_background_fill_dark="#0f1624",
+ input_background_fill_dark="*neutral_800",
+ block_border_color="white",
+ block_border_color_dark="#0f1624",
+ slider_color="*neutral_800",
+ slider_color_dark="*primary_200",
+ color_accent="white",
+
+ # The rest of the values were taken from Gradio's Soft theme
+ # Colors
+ background_fill_primary="*neutral_50",
+
+ # Shadows
+ shadow_drop="0 1px 4px 0 rgb(0 0 0 / 0.1)",
+ shadow_drop_lg="0 2px 5px 0 rgb(0 0 0 / 0.1)",
+
+ # Block Labels
+ block_background_fill="white",
+ block_label_padding="*spacing_sm *spacing_md",
+ block_label_radius="*radius_md",
+ block_label_text_size="*text_md",
+ block_label_text_weight="600",
+ block_label_text_color="*primary_500",
+ block_label_text_color_dark="white",
+
+ block_title_radius="*block_label_radius",
+ block_title_padding="*block_label_padding",
+ block_title_background_fill="*block_label_background_fill",
+ block_title_text_weight="600",
+ block_title_text_color="*primary_500",
+ block_title_text_color_dark="white",
+ block_label_margin="*spacing_md",
+
+ # Inputs
+ input_background_fill="white",
+ input_border_color="*neutral_50",
+ input_shadow="*shadow_drop",
+ input_shadow_focus="*shadow_drop_lg",
+ checkbox_shadow="none",
+
+ # Buttons
+ shadow_spread="6px",
+ button_primary_shadow="*shadow_drop_lg",
+ button_primary_shadow_hover="*shadow_drop_lg",
+ button_primary_shadow_active="*shadow_inset",
+ button_secondary_shadow="*shadow_drop_lg",
+ button_secondary_shadow_hover="*shadow_drop_lg",
+ button_secondary_shadow_active="*shadow_inset",
+ checkbox_label_shadow="*shadow_drop_lg",
+ button_primary_background_fill_hover_dark="*primary_500",
+ button_primary_text_color="white",
+ button_secondary_background_fill_hover_dark="*primary_500",
+ button_cancel_background_fill="*button_secondary_background_fill",
+ button_cancel_background_fill_hover="*button_secondary_background_fill_hover",
+ button_cancel_background_fill_hover_dark="*button_secondary_background_fill_hover",
+ button_cancel_text_color="*button_secondary_text_color",
+ checkbox_label_background_fill_selected="*primary_500",
+ checkbox_label_background_fill_selected_dark="*primary_600",
+ checkbox_border_width="1px",
+ checkbox_border_color="*neutral_100",
+ checkbox_border_color_dark="*neutral_600",
+ checkbox_background_color_selected="*primary_600",
+ checkbox_background_color_selected_dark="*primary_700",
+ checkbox_border_color_focus="*primary_500",
+ checkbox_border_color_focus_dark="*primary_600",
+ checkbox_border_color_selected="*primary_600",
+ checkbox_border_color_selected_dark="*primary_700",
+ checkbox_label_text_color_selected="white",
+
+ # Borders
+ block_border_width="0px",
+ panel_border_width="1px",
+ )
+
+theme = AceStepTheme()
\ No newline at end of file
diff --git a/acestep/ui/gradio/interfaces/training.py b/acestep/ui/gradio/interfaces/training.py
index 8dd1fecc..fd118b64 100644
--- a/acestep/ui/gradio/interfaces/training.py
+++ b/acestep/ui/gradio/interfaces/training.py
@@ -61,7 +61,7 @@ def create_training_section(dit_handler, llm_handler, init_params=None) -> dict:
info=t("training.load_dataset_info"), elem_classes=["has-info-container"],
scale=3,
)
- load_json_btn = gr.Button(t("training.load_btn"), variant="primary", scale=1)
+ load_json_btn = gr.Button(t("training.load_btn"), variant="secondary", scale=1)
load_json_status = gr.Textbox(
label=t("training.load_status"),
interactive=False,
@@ -487,6 +487,7 @@ def create_training_section(dit_handler, llm_handler, init_params=None) -> dict:
t("training.start_training_btn"),
variant="primary",
size="lg",
+ elem_classes=["btn-primary-important"]
)
with gr.Column(scale=1):
stop_training_btn = gr.Button(
@@ -522,7 +523,7 @@ def create_training_section(dit_handler, llm_handler, init_params=None) -> dict:
value="./lora_output/final_lora",
placeholder="./lora_output/my_lora",
)
- export_lora_btn = gr.Button(t("training.export_lora_btn"), variant="secondary")
+ export_lora_btn = gr.Button(t("training.export_lora_btn"), variant="primary")
export_status = gr.Textbox(
label=t("training.export_status"),
@@ -674,6 +675,7 @@ def create_training_section(dit_handler, llm_handler, init_params=None) -> dict:
"Start LoKr Training",
variant="primary",
size="lg",
+ elem_classes=["btn-primary-important"]
)
with gr.Column(scale=1):
stop_lokr_training_btn = gr.Button(
@@ -709,7 +711,7 @@ def create_training_section(dit_handler, llm_handler, init_params=None) -> dict:
value="./lokr_output/final_lokr",
placeholder="./lokr_output/my_lokr",
)
- export_lokr_btn = gr.Button("📦 Export LoKr", variant="secondary")
+ export_lokr_btn = gr.Button("📦 Export LoKr", variant="primary")
with gr.Row():
lokr_export_epoch = gr.Dropdown(