File size: 8,043 Bytes
ce2a030
 
 
 
59b9fee
 
 
 
 
 
 
 
 
 
 
 
 
ce2a030
0ea6ca2
ce2a030
 
 
 
 
 
 
 
 
 
 
 
 
 
59b9fee
ce2a030
0ea6ca2
ce2a030
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59b9fee
ce2a030
 
59b9fee
ce2a030
 
59b9fee
ce2a030
 
59b9fee
 
 
 
ce2a030
 
59b9fee
 
ce2a030
 
 
 
 
 
59b9fee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce2a030
59b9fee
 
 
 
ce2a030
 
59b9fee
ce2a030
 
 
 
 
 
 
 
59b9fee
ce2a030
 
59b9fee
ce2a030
 
59b9fee
ce2a030
59b9fee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce2a030
 
59b9fee
ce2a030
59b9fee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce2a030
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
"""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; }}
}}
"""