ACE-Music-Studio / theme.py
techfreakworm's picture
fix(ui): replace gr.Tabs with sidebar nav per wireframes
59b9fee unverified
raw
history blame
8.04 kB
"""Brutalist Mono — pure black/white, no color accent.
Palette tokens are the source of truth; CSS pulls from them. The audio
waveform is the only optionally-colored element (rendered white in v1).
UI architecture (locked):
- Sidebar layout (NOT ``gr.Tabs``) per wireframes at
``docs/superpowers/specs/mockups/``.
- ``.ams-sidebar`` is a flex column at desktop, fixed-width 170-190 px.
- ``.ams-side-radio`` is the mode-nav: a ``gr.Radio`` re-skinned via CSS
so each option renders as a full-width sidebar pill. The native
``:checked`` pseudo-class supplies the "active" highlight.
- ``.ams-content`` is the right column containing 5 ``.ams-tab-pane``
groups; one is visible at a time.
- Media queries: at ``<= 1024 px`` the sidebar shrinks to an icon rail.
At ``<= 640 px`` the sidebar is replaced by a horizontal scroll strip
at the top.
"""
from __future__ import annotations
import gradio as gr
# --- Palette ----------------------------------------------------------------
BG = "#0A0A0A"
SURFACE = "#141414"
SURFACE_STRONG = "#000000"
BORDER = "#1F1F1F"
BORDER_STRONG = "#2A2A2A"
INK = "#E5E5E5"
INK_MUTED = "#6B6B6B"
PRIMARY = "#FFFFFF"
ERROR_BG = "#1A1A1A"
HOVER_BG = "#1A1A1A"
RADIUS = "6px"
FONT_STACK = '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif'
def build_theme() -> gr.themes.Base:
"""Returns a Gradio theme keyed to Brutalist Mono tokens."""
return gr.themes.Base(
primary_hue=gr.themes.colors.gray,
neutral_hue=gr.themes.colors.gray,
font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
).set(
body_background_fill=BG,
body_text_color=INK,
block_background_fill=SURFACE,
block_border_color=BORDER,
block_border_width="1px",
block_radius=RADIUS,
input_background_fill=SURFACE_STRONG,
input_border_color=BORDER_STRONG,
input_border_color_focus=PRIMARY,
button_primary_background_fill=PRIMARY,
button_primary_text_color=BG,
button_primary_background_fill_hover=PRIMARY,
button_secondary_background_fill=SURFACE_STRONG,
button_secondary_text_color=INK,
button_secondary_border_color=BORDER_STRONG,
)
CSS = f"""
/* === Body chrome ======================================================= */
.ams-header {{
display:flex; justify-content:space-between; align-items:baseline;
padding:10px 4px 6px 4px;
}}
.ams-brand {{
font-size:18px; font-weight:600; letter-spacing:-0.01em; color:{INK};
}}
.ams-brand-period {{ color:{PRIMARY}; }}
.ams-status {{
font-size:11px; color:{INK_MUTED};
letter-spacing:0.06em; text-transform:uppercase;
}}
.ams-cta {{
font-size:13px; color:{INK_MUTED};
margin:2px 4px 14px 4px; padding-bottom:12px;
border-bottom:1px solid {BORDER};
}}
.ams-cta strong {{ color:{INK}; }}
.ams-cta-heart {{ color:{PRIMARY}; }}
.ams-cta a {{ color:{INK}; text-decoration:underline; }}
/* === Body row (sidebar + content) ====================================== */
.ams-body {{
gap:16px !important;
align-items:stretch !important;
}}
/* === Sidebar (desktop >= 1024px) ======================================= */
.ams-sidebar {{
background:{SURFACE_STRONG} !important;
padding:14px 8px !important;
border-radius:{RADIUS} !important;
border:1px solid {BORDER} !important;
min-width:190px;
max-width:210px;
}}
/* --- Mode radio (re-skin gr.Radio as a vertical sidebar nav) ----------- */
.ams-side-radio {{
background:transparent !important;
border:none !important;
padding:0 !important;
width:100%;
}}
.ams-side-radio .wrap {{
display:flex !important;
flex-direction:column !important;
gap:2px !important;
background:transparent !important;
border:none !important;
}}
/* Each radio option becomes a sidebar pill */
.ams-side-radio label {{
display:flex !important;
align-items:center !important;
padding:9px 12px !important;
margin:0 !important;
border-radius:4px !important;
border:none !important;
border-left:2px solid transparent !important;
background:transparent !important;
color:{INK_MUTED} !important;
font-size:13px !important;
font-weight:500 !important;
cursor:pointer !important;
transition:background 80ms ease, color 80ms ease, border-color 80ms ease;
min-height:0 !important;
width:100%;
box-sizing:border-box;
}}
.ams-side-radio label:hover {{
background:{HOVER_BG} !important;
color:{INK} !important;
}}
/* Hide the native radio circle */
.ams-side-radio label input[type="radio"] {{
display:none !important;
}}
/* Active state: white text + white left border + dark bg */
.ams-side-radio label.selected,
.ams-side-radio label:has(input[type="radio"]:checked) {{
background:{HOVER_BG} !important;
color:{PRIMARY} !important;
border-left-color:{PRIMARY} !important;
font-weight:600 !important;
}}
/* Hide the (now-empty) form-element-info row that gr.Radio injects */
.ams-side-radio + div:empty {{ display:none !important; }}
/* --- Session history block (below the mode radio) --------------------- */
.ams-history {{
margin-top:14px;
padding-top:10px;
border-top:1px solid {BORDER};
}}
.ams-history-title {{
font-size:10px; color:{INK_MUTED};
letter-spacing:0.1em; text-transform:uppercase;
padding:0 4px 6px 4px;
}}
.ams-history-empty {{
font-size:11px; color:#3F3F3F;
font-style:italic;
padding:6px 4px;
}}
/* === Content area ====================================================== */
.ams-content {{
background:{SURFACE} !important;
border:1px solid {BORDER} !important;
border-radius:{RADIUS} !important;
padding:16px !important;
min-height:540px;
}}
.ams-tab-pane {{
background:transparent !important;
border:none !important;
padding:0 !important;
}}
/* === LoRA chip pill (used in M2+) ====================================== */
.ams-chip {{
display:inline-block; padding:5px 10px; border-radius:14px;
font-size:11px; margin:0 5px 5px 0; background:{SURFACE_STRONG};
border:1px solid {BORDER_STRONG}; color:{INK_MUTED}; cursor:pointer;
}}
.ams-chip.on {{ border-color:{PRIMARY}; color:{PRIMARY}; }}
.ams-chip.upload {{ border-style:dashed; color:{PRIMARY}; }}
/* === LoRA file drop zone (tighten Gradio default ~400px height) ======== */
.ams-lora-file .upload-container {{ min-height:56px !important; }}
/* === Hide Gradio footer ================================================ */
footer {{ display:none !important; }}
/* === Responsive: tablet 640-1024 px ==================================== */
@media (max-width: 1024px) {{
.ams-sidebar {{
min-width:48px !important;
max-width:48px !important;
padding:8px 4px !important;
}}
/* Hide labels, keep only the leading emoji */
.ams-side-radio label {{
font-size:0 !important;
padding:8px 0 !important;
justify-content:center !important;
}}
.ams-side-radio label::first-letter {{
font-size:16px !important;
}}
/* Hide history in tablet rail mode */
.ams-history {{ display:none !important; }}
}}
/* === Responsive: mobile < 640 px ======================================= */
@media (max-width: 640px) {{
.ams-body {{
flex-direction:column !important;
}}
.ams-sidebar {{
min-width:100% !important;
max-width:100% !important;
padding:6px !important;
}}
/* Mobile: switch sidebar to horizontal scroll strip */
.ams-side-radio .wrap {{
flex-direction:row !important;
overflow-x:auto !important;
gap:4px !important;
}}
.ams-side-radio label {{
font-size:11px !important;
white-space:nowrap !important;
border-left:none !important;
border-bottom:2px solid transparent !important;
padding:8px 10px !important;
justify-content:flex-start !important;
}}
.ams-side-radio label::first-letter {{
font-size:13px !important;
}}
.ams-side-radio label:has(input[type="radio"]:checked) {{
border-left-color:transparent !important;
border-bottom-color:{PRIMARY} !important;
}}
.ams-history {{ display:none !important; }}
.ams-cta {{ font-size:11px; }}
}}
"""