Download theme.py from mcqueenmater/interaction-intelligence: direct link, hf CLI and curl.
- Browser
- Download file 14.3 kB
-
https://huggingface.co/spaces/mcqueenmater/interaction-intelligence/resolve/main/theme.py
- Command line
-
hf download hf://spaces/mcqueenmater/interaction-intelligence/theme.py
-
curl -L -o theme.py https://huggingface.co/spaces/mcqueenmater/interaction-intelligence/resolve/main/theme.py
14.3 kB
| """Fusion Lab design tokens: dark, clarity-first palette (Linear/Observable/Bloomberg, | |
| not Palantir-dense) plus a Gradio theme + CSS injection built from the same tokens.""" | |
| import gradio as gr | |
| BG_VOID = "#09090b" | |
| BG_SURFACE = "#18181b" | |
| BORDER_HAIRLINE = "#27272a" | |
| TEXT_PRIMARY = "#a1a1aa" | |
| TEXT_SECONDARY = "#71717a" | |
| ACCENT = "#FF5722" | |
| SIG_POSITIVE = "#3DDC97" | |
| SIG_NEUTRAL = "#d4d4d8" | |
| SIG_NEGATIVE = "#FF5722" | |
| SIG_NOT_SIGNIFICANT = "#3f3f46" | |
| CLASS_COLORS = {0: SIG_NEGATIVE, 1: SIG_NEUTRAL, 2: SIG_POSITIVE} | |
| CLASS_NAMES = {0: "Negative", 1: "Neutral", 2: "Positive"} | |
| FONT_MONO = 'ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, monospace' | |
| FONT_SERIF = '"Merriweather", "Georgia", "Times New Roman", serif' | |
| FONT_UI = ( | |
| '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif' | |
| ) | |
| def build_theme() -> gr.themes.Base: | |
| return gr.themes.Base( | |
| primary_hue=gr.themes.colors.emerald, | |
| neutral_hue=gr.themes.colors.slate, | |
| font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"], | |
| font_mono=[gr.themes.GoogleFont("JetBrains Mono"), FONT_MONO], | |
| ).set( | |
| body_background_fill=BG_VOID, | |
| body_background_fill_dark=BG_VOID, | |
| background_fill_primary=BG_SURFACE, | |
| background_fill_primary_dark=BG_SURFACE, | |
| background_fill_secondary=BG_VOID, | |
| background_fill_secondary_dark=BG_VOID, | |
| border_color_primary=BORDER_HAIRLINE, | |
| border_color_primary_dark=BORDER_HAIRLINE, | |
| body_text_color=TEXT_PRIMARY, | |
| body_text_color_dark=TEXT_PRIMARY, | |
| body_text_color_subdued=TEXT_SECONDARY, | |
| body_text_color_subdued_dark=TEXT_SECONDARY, | |
| color_accent=ACCENT, | |
| color_accent_soft=ACCENT, | |
| block_background_fill=BG_SURFACE, | |
| block_background_fill_dark=BG_SURFACE, | |
| block_border_color=BORDER_HAIRLINE, | |
| block_border_color_dark=BORDER_HAIRLINE, | |
| block_radius="4px", | |
| button_primary_background_fill=ACCENT, | |
| button_primary_background_fill_dark=ACCENT, | |
| button_primary_text_color=BG_VOID, | |
| button_primary_text_color_dark=BG_VOID, | |
| input_background_fill=BG_VOID, | |
| input_background_fill_dark=BG_VOID, | |
| input_border_color=BORDER_HAIRLINE, | |
| input_border_color_dark=BORDER_HAIRLINE, | |
| ) | |
| CUSTOM_CSS = f""" | |
| :root {{ | |
| --fl-bg-void: {BG_VOID}; | |
| --fl-bg-surface: {BG_SURFACE}; | |
| --fl-border: {BORDER_HAIRLINE}; | |
| --fl-text-primary: {TEXT_PRIMARY}; | |
| --fl-text-secondary: {TEXT_SECONDARY}; | |
| --fl-accent: {ACCENT}; | |
| --fl-positive: {SIG_POSITIVE}; | |
| --fl-neutral: {SIG_NEUTRAL}; | |
| --fl-negative: {SIG_NEGATIVE}; | |
| --fl-not-sig: {SIG_NOT_SIGNIFICANT}; | |
| }} | |
| .gradio-container {{ | |
| max-width: 1320px !important; | |
| font-family: {FONT_UI} !important; | |
| }} | |
| .fl-mono, .fl-mono * {{ | |
| font-family: {FONT_MONO} !important; | |
| font-variant-numeric: tabular-nums; | |
| }} | |
| /* Scoped sharp-corner + hairline treatment on our own components — deliberately | |
| NOT a blanket wildcard border-radius reset, which fights native Gradio | |
| component chrome (dropdowns/sliders/tab pills) instead of composing with it. */ | |
| .gr-group, .fl-panel, .fl-stat-block, .fl-badge, .fl-turn, .fl-caveat, .fl-diagram-box {{ | |
| border-radius: 0px !important; | |
| }} | |
| .gr-group, .fl-panel {{ | |
| border: 1px solid var(--fl-border) !important; | |
| background: var(--fl-bg-surface) !important; | |
| }} | |
| /* Persistent header ribbon (space/header.py) */ | |
| .fl-ribbon {{ | |
| display: flex; | |
| align-items: center; | |
| gap: 28px; | |
| padding: 14px 20px; | |
| margin-bottom: 4px; | |
| border: 1px solid var(--fl-border); | |
| background: linear-gradient(180deg, var(--fl-bg-surface) 0%, var(--fl-bg-void) 100%); | |
| }} | |
| .fl-wordmark {{ | |
| font-family: {FONT_UI}; | |
| font-weight: 800; | |
| font-size: 16px; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| color: var(--fl-text-primary); | |
| white-space: nowrap; | |
| }} | |
| .fl-wordmark span {{ color: var(--fl-accent); }} | |
| .fl-ribbon-divider {{ | |
| width: 1px; | |
| align-self: stretch; | |
| background: var(--fl-border); | |
| }} | |
| .fl-chip {{ | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1px; | |
| min-width: 108px; | |
| }} | |
| .fl-chip-label {{ | |
| font-size: 10px; | |
| letter-spacing: 0.06em; | |
| text-transform: uppercase; | |
| color: var(--fl-text-secondary); | |
| }} | |
| .fl-chip-value {{ | |
| font-family: {FONT_MONO}; | |
| font-variant-numeric: tabular-nums; | |
| font-size: 18px; | |
| font-weight: 700; | |
| color: var(--fl-text-primary); | |
| }} | |
| .fl-chip-value.fl-accent {{ color: var(--fl-accent); }} | |
| .fl-chip-dot {{ | |
| display: inline-block; | |
| width: 7px; | |
| height: 7px; | |
| border-radius: 50%; | |
| margin-right: 5px; | |
| }} | |
| .fl-stat-block {{ | |
| display: flex; | |
| flex-direction: column; | |
| gap: 4px; | |
| padding: 16px 20px; | |
| border: 1px solid var(--fl-border); | |
| background: var(--fl-bg-surface); | |
| }} | |
| .fl-stat-label {{ | |
| font-size: 12px; | |
| letter-spacing: 0.06em; | |
| text-transform: uppercase; | |
| color: var(--fl-text-secondary); | |
| }} | |
| .fl-stat-value {{ | |
| font-family: {FONT_MONO}; | |
| font-variant-numeric: tabular-nums; | |
| letter-spacing: -0.02em; | |
| font-size: 34px; | |
| font-weight: 700; | |
| color: var(--fl-text-primary); | |
| }} | |
| .fl-stat-value.fl-accent {{ color: var(--fl-accent); }} | |
| .fl-stat-sub {{ | |
| font-size: 12px; | |
| color: var(--fl-text-secondary); | |
| }} | |
| /* Architecture diagram (space/diagram.py) */ | |
| .fl-diagram {{ | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| overflow-x: auto; | |
| padding: 18px 4px; | |
| }} | |
| .fl-diagram-box {{ | |
| flex: 0 0 auto; | |
| border: 1px solid var(--fl-border); | |
| background: var(--fl-bg-surface); | |
| padding: 10px 14px; | |
| text-align: center; | |
| }} | |
| .fl-diagram-box.fl-diagram-highlight {{ | |
| border-color: var(--fl-accent); | |
| box-shadow: 0 0 0 1px rgba(255, 87, 34, 0.25); | |
| }} | |
| .fl-diagram-title {{ | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: var(--fl-text-primary); | |
| white-space: nowrap; | |
| }} | |
| .fl-diagram-dim {{ | |
| font-family: {FONT_MONO}; | |
| font-variant-numeric: tabular-nums; | |
| font-size: 11px; | |
| color: var(--fl-accent); | |
| margin-top: 2px; | |
| }} | |
| .fl-diagram-sub {{ | |
| font-size: 10px; | |
| color: var(--fl-text-secondary); | |
| margin-top: 2px; | |
| max-width: 120px; | |
| }} | |
| .fl-diagram-arrow {{ | |
| flex: 0 0 auto; | |
| color: var(--fl-text-secondary); | |
| font-size: 16px; | |
| }} | |
| .fl-diagram-col {{ | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| }} | |
| /* Dataframe density + tabular numerals */ | |
| .fl-panel table, table.svelte-* {{ font-family: {FONT_UI}; }} | |
| div[data-testid="dataframe"] td, div[data-testid="dataframe"] th {{ | |
| font-family: {FONT_MONO} !important; | |
| font-variant-numeric: tabular-nums; | |
| font-size: 12.5px !important; | |
| padding: 5px 10px !important; | |
| }} | |
| .fl-class-badge {{ | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 5px; | |
| font-size: 12px; | |
| }} | |
| .fl-class-dot {{ | |
| display: inline-block; | |
| width: 7px; | |
| height: 7px; | |
| border-radius: 50%; | |
| flex: 0 0 auto; | |
| }} | |
| .fl-badge {{ | |
| display: inline-block; | |
| padding: 3px 10px; | |
| font-size: 11px; | |
| font-weight: 600; | |
| letter-spacing: 0.04em; | |
| text-transform: uppercase; | |
| border-radius: 3px; | |
| }} | |
| .fl-badge-significant {{ background: rgba(255, 87, 34, 0.12); color: var(--fl-accent); border: 1px solid rgba(255, 87, 34, 0.35); }} | |
| .fl-badge-not-significant {{ background: rgba(74, 85, 104, 0.2); color: var(--fl-text-secondary); border: 1px solid var(--fl-border); }} | |
| .fl-caveat {{ | |
| border-left: 3px solid var(--fl-neutral); | |
| padding: 8px 14px; | |
| background: rgba(201, 162, 39, 0.06); | |
| font-size: 13px; | |
| color: var(--fl-text-secondary); | |
| overflow-wrap: break-word; | |
| }} | |
| .fl-caveat code {{ | |
| overflow-wrap: break-word; | |
| word-break: break-word; | |
| }} | |
| .fl-turn {{ | |
| padding: 10px 14px; | |
| margin-bottom: 6px; | |
| border: 1px solid var(--fl-border); | |
| border-left-width: 3px; | |
| }} | |
| .fl-turn-meta {{ | |
| font-size: 11px; | |
| color: var(--fl-text-secondary); | |
| text-transform: uppercase; | |
| letter-spacing: 0.04em; | |
| margin-bottom: 4px; | |
| }} | |
| .fl-turn-text {{ | |
| font-size: 14px; | |
| color: var(--fl-text-primary); | |
| line-height: 1.5; | |
| }} | |
| .fl-turn-weight {{ | |
| font-family: {FONT_MONO}; | |
| font-variant-numeric: tabular-nums; | |
| color: var(--fl-text-secondary); | |
| font-size: 12px; | |
| }} | |
| .fl-section-header {{ | |
| font-size: 12px; | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| color: var(--fl-text-secondary); | |
| margin: 18px 0 8px 0; | |
| padding-bottom: 4px; | |
| border-bottom: 1px solid var(--fl-border); | |
| }} | |
| .fl-split-hero {{ | |
| display: flex; | |
| flex-direction: row; | |
| align-items: stretch; | |
| background: var(--fl-bg-void); | |
| border: 1px solid var(--fl-border); | |
| margin-bottom: 32px; | |
| }} | |
| .fl-split-left {{ | |
| flex: 1; | |
| padding: 48px; | |
| border-right: 1px solid var(--fl-border); | |
| }} | |
| .fl-split-right {{ | |
| flex: 1; | |
| padding: 48px; | |
| background: rgba(255, 87, 34, 0.03); /* Subtle audio accent background */ | |
| }} | |
| .fl-serif-text {{ | |
| font-family: {FONT_SERIF} !important; | |
| font-size: 24px; | |
| line-height: 1.6; | |
| color: #f4f4f5; | |
| font-style: italic; | |
| }} | |
| .fl-xray-highlight {{ | |
| border-left: 4px solid var(--fl-accent) !important; | |
| background: rgba(255, 87, 34, 0.08) !important; | |
| }} | |
| .fl-xray-highlight .fl-turn-text {{ | |
| color: var(--fl-accent) !important; | |
| font-family: {FONT_MONO} !important; | |
| }} | |
| /* --- Evidence Room: behavioral summary strip, timeline, evidence panel | |
| (space/workspace.py) --- these classes are emitted by workspace.py/transcript.py | |
| but were previously unstyled; restored here. */ | |
| .fl-summary-strip {{ | |
| display: flex; | |
| gap: 1px; | |
| background: var(--fl-border); | |
| border: 1px solid var(--fl-border); | |
| margin-bottom: 10px; | |
| flex-wrap: wrap; | |
| }} | |
| .fl-summary-strip .fl-stat-block {{ | |
| flex: 1; | |
| border: none; | |
| padding: 12px 16px; | |
| }} | |
| .fl-summary-strip .fl-stat-value {{ | |
| font-size: 24px; | |
| }} | |
| .fl-timeline {{ | |
| border: 1px solid var(--fl-border); | |
| background: var(--fl-bg-surface); | |
| padding: 14px 16px; | |
| }} | |
| .fl-timeline-row {{ | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| height: 30px; | |
| margin-bottom: 2px; | |
| }} | |
| .fl-timeline-label {{ | |
| width: 84px; | |
| flex: 0 0 auto; | |
| font-size: 11px; | |
| letter-spacing: 0.04em; | |
| text-transform: uppercase; | |
| color: var(--fl-text-secondary); | |
| }} | |
| .fl-timeline-strip {{ | |
| position: relative; | |
| flex: 1 1 auto; | |
| height: 22px; | |
| background: var(--fl-bg-void); | |
| border: 1px solid var(--fl-border); | |
| }} | |
| .fl-timeline-bar {{ | |
| position: absolute; | |
| bottom: 0; | |
| width: 3px; | |
| background: var(--fl-accent); | |
| cursor: pointer; | |
| transition: opacity 0.1s; | |
| }} | |
| .fl-timeline-bar:hover {{ opacity: 0.7; }} | |
| .fl-timeline-bar.fl-timeline-selected {{ | |
| background: var(--fl-text-primary); | |
| box-shadow: 0 0 0 1px var(--fl-accent); | |
| }} | |
| .fl-timeline-divider {{ | |
| position: absolute; | |
| top: -4px; | |
| bottom: -4px; | |
| width: 1px; | |
| background: var(--fl-neutral); | |
| }} | |
| .fl-timeline-section-label {{ | |
| position: absolute; | |
| top: -18px; | |
| font-size: 10px; | |
| letter-spacing: 0.05em; | |
| text-transform: uppercase; | |
| color: var(--fl-text-secondary); | |
| }} | |
| .fl-timeline-risk-row {{ | |
| position: relative; | |
| height: 14px; | |
| margin: 4px 0 2px 94px; | |
| }} | |
| .fl-risk-dot {{ | |
| position: absolute; | |
| top: 2px; | |
| width: 7px; | |
| height: 7px; | |
| border-radius: 50%; | |
| background: var(--fl-negative); | |
| cursor: pointer; | |
| transform: translateX(-50%); | |
| }} | |
| .fl-risk-dot.fl-timeline-selected {{ | |
| box-shadow: 0 0 0 2px var(--fl-text-primary); | |
| }} | |
| .fl-evidence-panel {{ | |
| border: 1px solid var(--fl-border); | |
| background: var(--fl-bg-surface); | |
| padding: 14px 16px; | |
| min-height: 220px; | |
| }} | |
| .fl-evidence-empty {{ | |
| color: var(--fl-text-secondary); | |
| font-size: 13px; | |
| }} | |
| .fl-evidence-quote {{ | |
| margin-bottom: 10px; | |
| }} | |
| .fl-evidence-bullets {{ | |
| margin: 0 0 10px 0; | |
| padding-left: 18px; | |
| }} | |
| .fl-evidence-bullet {{ | |
| font-size: 12.5px; | |
| }} | |
| /* --- Verdict card (space/workspace.py: render_verdict_card) --- */ | |
| .fl-verdict-card {{ | |
| border: 1px solid var(--fl-border); | |
| background: var(--fl-bg-surface); | |
| margin-bottom: 10px; | |
| overflow: hidden; | |
| }} | |
| .fl-verdict-banner {{ | |
| padding: 12px 18px; | |
| font-size: 15px; | |
| font-weight: 700; | |
| letter-spacing: 0.02em; | |
| text-transform: uppercase; | |
| }} | |
| .fl-verdict-banner-correct {{ | |
| background: rgba(61, 220, 151, 0.12); | |
| color: var(--fl-positive); | |
| border-bottom: 1px solid rgba(61, 220, 151, 0.35); | |
| }} | |
| .fl-verdict-banner-incorrect {{ | |
| background: rgba(255, 87, 34, 0.12); | |
| color: var(--fl-accent); | |
| border-bottom: 1px solid rgba(255, 87, 34, 0.35); | |
| }} | |
| .fl-verdict-tiles {{ | |
| display: flex; | |
| flex-wrap: wrap; | |
| }} | |
| .fl-verdict-tiles .fl-stat-block {{ | |
| flex: 1; | |
| border: none; | |
| border-right: 1px solid var(--fl-border); | |
| padding: 12px 16px; | |
| }} | |
| .fl-verdict-tiles .fl-stat-block:last-child {{ border-right: none; }} | |
| .fl-verdict-tiles .fl-stat-value {{ | |
| font-size: 22px; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| }} | |
| /* --- Mobile --- the layout above is built on Gradio's own responsive Row/Column | |
| grid (columns already collapse to full width below ~320px on their own), but the | |
| raw HTML/CSS blocks below (ribbon, split-hero, panels, timeline) bypass that grid | |
| and need explicit narrow-viewport rules. */ | |
| @media (max-width: 768px) {{ | |
| .fl-ribbon {{ | |
| flex-wrap: wrap; | |
| gap: 14px; | |
| padding: 12px 16px; | |
| }} | |
| .fl-chip {{ | |
| min-width: 88px; | |
| }} | |
| .fl-split-hero {{ | |
| flex-direction: column; | |
| }} | |
| .fl-split-left {{ | |
| padding: 20px; | |
| border-right: none; | |
| border-bottom: 1px solid var(--fl-border); | |
| }} | |
| .fl-split-right {{ | |
| padding: 20px; | |
| }} | |
| .fl-serif-text {{ | |
| font-size: 19px; | |
| }} | |
| .fl-panel {{ | |
| padding: 16px !important; | |
| }} | |
| .fl-timeline-label {{ | |
| width: 60px; | |
| font-size: 10px; | |
| }} | |
| .fl-timeline-row {{ | |
| gap: 6px; | |
| }} | |
| .fl-timeline-risk-row {{ | |
| margin-left: 66px; | |
| }} | |
| .fl-timeline-section-label {{ | |
| font-size: 9px; | |
| }} | |
| }} | |
| @media (max-width: 480px) {{ | |
| .fl-wordmark {{ | |
| font-size: 13px; | |
| }} | |
| .fl-chip {{ | |
| min-width: 74px; | |
| }} | |
| .fl-chip-value {{ | |
| font-size: 15px; | |
| }} | |
| .fl-stat-value {{ | |
| font-size: 26px; | |
| }} | |
| }} | |
| """ | |