Spaces:
Running on Zero
Running on Zero
| """Onyx Amber theme — palette tokens, gr.themes.Base subclass, and CSS string.""" | |
| from __future__ import annotations | |
| import gradio as gr | |
| AMBER: dict[str, str] = { | |
| "body_bg": "#0F0C08", | |
| "panel_bg": "#0F0C08", | |
| "input_bg": "#0F0C08", | |
| "canvas_bg": "#110D08", | |
| "border": "#2A2218", | |
| "text": "#FAF1E3", | |
| "text_dim": "#A89478", | |
| "accent": "#FFB02E", | |
| "accent_text": "#1A1208", | |
| "radius": "8px", | |
| "radius_sm": "6px", | |
| } | |
| class _OnyxAmberBase(gr.themes.Base): | |
| """gr.themes.Base subclass with helpers for iterating over font lists. | |
| Gradio collapses font lists into a CSS string at __init__; we expose the | |
| original lists via .font_list / .font_mono_list for code that needs to | |
| iterate. The public self.font / self.font_mono attributes are left alone so | |
| that _get_theme_css() emits the correct --font / --font-mono CSS variables. | |
| """ | |
| def font_list(self) -> list: | |
| """Return the original font list (GoogleFont + str entries).""" | |
| return self._font | |
| def font_mono_list(self) -> list: | |
| """Return the original monospace font list (GoogleFont + str entries).""" | |
| return self._font_mono | |
| def build_theme() -> gr.themes.Base: | |
| """Return a Gradio theme matching the Onyx Amber palette.""" | |
| return _OnyxAmberBase( | |
| primary_hue=gr.themes.Color( | |
| c50="#FFF8E6", | |
| c100="#FFEFC2", | |
| c200="#FFE08A", | |
| c300="#FFD161", | |
| c400="#FFC042", | |
| c500=AMBER["accent"], | |
| c600="#E69926", | |
| c700="#B37A1F", | |
| c800="#805717", | |
| c900="#4D3510", | |
| c950="#1A1208", | |
| ), | |
| neutral_hue=gr.themes.Color( | |
| c50="#FAF1E3", | |
| c100="#E8DCC4", | |
| c200="#D4C2A1", | |
| c300="#A89478", | |
| c400="#867054", | |
| c500="#5C4D38", | |
| c600="#3C3225", | |
| c700="#2A2218", | |
| c800="#1C170F", | |
| c900="#100C08", | |
| c950="#0A0805", | |
| ), | |
| font=[gr.themes.GoogleFont("Geist"), "system-ui", "sans-serif"], | |
| font_mono=[gr.themes.GoogleFont("Geist Mono"), "ui-monospace", "monospace"], | |
| radius_size=gr.themes.sizes.radius_md, | |
| ).set( | |
| body_background_fill=AMBER["body_bg"], | |
| body_text_color=AMBER["text"], | |
| body_text_color_subdued=AMBER["text_dim"], | |
| background_fill_primary=AMBER["panel_bg"], | |
| background_fill_secondary=AMBER["canvas_bg"], | |
| block_background_fill=AMBER["panel_bg"], | |
| block_border_color=AMBER["border"], | |
| block_border_width="1px", | |
| block_radius=AMBER["radius"], | |
| input_background_fill=AMBER["input_bg"], | |
| input_border_color=AMBER["border"], | |
| button_primary_background_fill=AMBER["accent"], | |
| button_primary_background_fill_hover=AMBER["accent"], | |
| button_primary_text_color=AMBER["accent_text"], | |
| button_primary_border_color=AMBER["accent"], | |
| slider_color=AMBER["accent"], | |
| color_accent=AMBER["accent"], | |
| color_accent_soft="rgba(255,176,46,0.12)", | |
| ) | |
| CSS: str = """ | |
| /* Onyx Amber — atmospheric layer that Gradio's theme can't express alone */ | |
| body, .gradio-container { | |
| background-image: radial-gradient(ellipse 80% 60% at 50% 0%, rgba(255,176,46,0.06), transparent 70%); | |
| } | |
| /* Amber glow on primary button */ | |
| .gradio-container button.primary { | |
| box-shadow: 0 0 0 1px rgba(255,176,46,0.4), 0 8px 24px -8px rgba(255,176,46,0.35); | |
| } | |
| /* Slim status line typography */ | |
| .zis-status { | |
| font-family: 'Geist Mono', ui-monospace, monospace; | |
| font-size: 11px; | |
| letter-spacing: 0.06em; | |
| color: #A89478; | |
| } | |
| /* LoRA file slot — solid amber border + slim icon when a file is loaded */ | |
| .zis-lora.loaded { | |
| border: 1px solid #FFB02E !important; | |
| } | |
| /* ===== Param tooltip — (i) icon next to labels (spec § 4.6) ===== */ | |
| .zis-row-label { | |
| display: inline-flex; align-items: center; | |
| font-size: 11px; color: #A89478; font-weight: 500; | |
| margin-bottom: 6px; | |
| } | |
| .zis-info { | |
| display: inline-flex; align-items: center; justify-content: center; | |
| width: 12px; height: 12px; | |
| font: italic 600 8px 'Geist', system-ui, sans-serif; | |
| border: 1px solid #2A2218; border-radius: 50%; | |
| color: #A89478; vertical-align: super; | |
| margin-left: 3px; cursor: help; position: relative; | |
| transition: border-color 0.12s, color 0.12s; | |
| } | |
| .zis-info:hover { border-color: #FFB02E; color: #FFB02E; } | |
| .zis-info::after { | |
| content: attr(data-info); | |
| position: absolute; bottom: 100%; left: 50%; | |
| transform: translateX(-50%) translateY(-4px); | |
| background: #1C170F; color: #FAF1E3; | |
| border: 1px solid #2A2218; border-radius: 6px; | |
| padding: 6px 10px; | |
| font: 400 11px 'Geist', system-ui, sans-serif; line-height: 1.4; | |
| width: 200px; white-space: normal; | |
| opacity: 0; pointer-events: none; | |
| transition: opacity 0.12s; z-index: 50; | |
| box-shadow: 0 4px 16px rgba(0,0,0,0.4); | |
| } | |
| .zis-info:hover::after, .zis-info.shown::after { opacity: 1; } | |
| /* ===== Custom model selector — 2-col phone / 4-col tablet+ (spec § 4.7) ===== */ | |
| .zis-models { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 8px; | |
| margin-bottom: 10px; | |
| } | |
| @media (min-width: 768px) { | |
| .zis-models { grid-template-columns: repeat(4, 1fr); } | |
| } | |
| .zis-model { | |
| display: flex; align-items: center; gap: 8px; | |
| padding: 10px 12px; | |
| border: 1px solid #2A2218; border-radius: 8px; | |
| background: transparent; cursor: pointer; | |
| color: #FAF1E3; | |
| font: 500 12px 'Geist', system-ui, sans-serif; | |
| text-decoration: none; | |
| transition: opacity 0.15s, border-color 0.15s, background 0.15s; | |
| } | |
| .zis-model .dot { | |
| width: 10px; height: 10px; border-radius: 50%; | |
| border: 1px solid #2A2218; flex-shrink: 0; | |
| } | |
| .zis-model .name { flex: 1; text-align: left; } | |
| .zis-model.on { | |
| background: #FFB02E; color: #1A1208; border-color: #FFB02E; | |
| } | |
| .zis-model.on .dot { background: #1A1208; border-color: #1A1208; } | |
| .zis-model.soon { | |
| opacity: 0.55; | |
| background: rgba(255,176,46,0.04); | |
| border-style: dashed; | |
| position: relative; | |
| } | |
| .zis-model.soon .name { color: #A89478; } | |
| .zis-model.soon .name .ext { | |
| font-size: 10px; color: #FFB02E; | |
| margin-left: 4px; vertical-align: super; | |
| } | |
| .zis-model.soon .soon-tag { | |
| font-family: 'Geist Mono', ui-monospace, monospace; | |
| font-size: 8.5px; letter-spacing: 0.12em; text-transform: uppercase; | |
| background: rgba(255,176,46,0.18); color: #FFB02E; | |
| padding: 2px 6px; border-radius: 100px; | |
| flex-shrink: 0; | |
| } | |
| .zis-model.soon:hover { opacity: 0.78; border-color: #FFB02E; } | |
| .zis-model.soon::after { | |
| content: "Coming soon — opens GitHub"; | |
| position: absolute; bottom: 100%; left: 50%; | |
| transform: translateX(-50%) translateY(-4px); | |
| background: #1C170F; color: #FAF1E3; | |
| border: 1px solid #2A2218; border-radius: 6px; | |
| padding: 6px 10px; | |
| font: 400 11px 'Geist', system-ui, sans-serif; | |
| white-space: nowrap; | |
| opacity: 0; pointer-events: none; | |
| transition: opacity 0.12s; z-index: 50; | |
| box-shadow: 0 4px 16px rgba(0,0,0,0.4); | |
| } | |
| .zis-model.soon:hover::after { opacity: 1; } | |
| """.strip() | |