- Remove unused imports: json (llm.py), tempfile (stt.py), base64 (tts.py) - Apply ruff format to all Python files
565 lines
16 KiB
Python
565 lines
16 KiB
Python
"""
|
|
Shared Gradio theme for Davies Tech Labs AI demos.
|
|
Consistent styling across all demo applications.
|
|
Cyberpunk aesthetic - dark with yellow/gold accents.
|
|
"""
|
|
|
|
import gradio as gr
|
|
|
|
|
|
# Cyberpunk color palette
|
|
CYBER_YELLOW = "#d4a700"
|
|
CYBER_GOLD = "#ffcc00"
|
|
CYBER_DARK = "#0d0d0d"
|
|
CYBER_DARKER = "#080808"
|
|
CYBER_GRAY = "#1a1a1a"
|
|
CYBER_TEXT = "#e5e5e5"
|
|
CYBER_MUTED = "#888888"
|
|
|
|
|
|
def get_lab_theme() -> gr.Theme:
|
|
"""
|
|
Create a custom Gradio theme matching cyberpunk styling.
|
|
Dark theme with yellow/gold accents.
|
|
"""
|
|
return gr.themes.Base(
|
|
primary_hue=gr.themes.colors.yellow,
|
|
secondary_hue=gr.themes.colors.amber,
|
|
neutral_hue=gr.themes.colors.zinc,
|
|
font=[
|
|
gr.themes.GoogleFont("Space Grotesk"),
|
|
"ui-sans-serif",
|
|
"system-ui",
|
|
"sans-serif",
|
|
],
|
|
font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "monospace"],
|
|
).set(
|
|
# Background colors
|
|
body_background_fill=CYBER_DARK,
|
|
body_background_fill_dark=CYBER_DARKER,
|
|
background_fill_primary=CYBER_GRAY,
|
|
background_fill_primary_dark=CYBER_DARK,
|
|
background_fill_secondary=CYBER_DARKER,
|
|
background_fill_secondary_dark="#050505",
|
|
# Text colors
|
|
body_text_color=CYBER_TEXT,
|
|
body_text_color_dark=CYBER_TEXT,
|
|
body_text_color_subdued=CYBER_MUTED,
|
|
body_text_color_subdued_dark=CYBER_MUTED,
|
|
# Borders
|
|
border_color_primary=CYBER_YELLOW,
|
|
border_color_primary_dark=CYBER_YELLOW,
|
|
border_color_accent=CYBER_GOLD,
|
|
border_color_accent_dark=CYBER_GOLD,
|
|
# Buttons
|
|
button_primary_background_fill=CYBER_YELLOW,
|
|
button_primary_background_fill_dark=CYBER_YELLOW,
|
|
button_primary_background_fill_hover="#b8940a",
|
|
button_primary_background_fill_hover_dark="#b8940a",
|
|
button_primary_text_color=CYBER_DARK,
|
|
button_primary_text_color_dark=CYBER_DARK,
|
|
button_primary_border_color=CYBER_GOLD,
|
|
button_primary_border_color_dark=CYBER_GOLD,
|
|
button_secondary_background_fill="transparent",
|
|
button_secondary_background_fill_dark="transparent",
|
|
button_secondary_text_color=CYBER_YELLOW,
|
|
button_secondary_text_color_dark=CYBER_YELLOW,
|
|
button_secondary_border_color=CYBER_YELLOW,
|
|
button_secondary_border_color_dark=CYBER_YELLOW,
|
|
# Inputs
|
|
input_background_fill=CYBER_DARKER,
|
|
input_background_fill_dark=CYBER_DARKER,
|
|
input_border_color="#333333",
|
|
input_border_color_dark="#333333",
|
|
input_border_color_focus=CYBER_YELLOW,
|
|
input_border_color_focus_dark=CYBER_YELLOW,
|
|
# Shadows and effects
|
|
shadow_drop="0 4px 20px rgba(212, 167, 0, 0.15)",
|
|
shadow_drop_lg="0 8px 40px rgba(212, 167, 0, 0.2)",
|
|
# Block styling
|
|
block_background_fill=CYBER_GRAY,
|
|
block_background_fill_dark=CYBER_GRAY,
|
|
block_border_color="#2a2a2a",
|
|
block_border_color_dark="#2a2a2a",
|
|
block_label_background_fill="#1a1a00",
|
|
block_label_background_fill_dark="#1a1a00",
|
|
block_label_text_color=CYBER_YELLOW,
|
|
block_label_text_color_dark=CYBER_YELLOW,
|
|
block_label_border_color=CYBER_YELLOW,
|
|
block_label_border_color_dark=CYBER_YELLOW,
|
|
block_title_text_color=CYBER_TEXT,
|
|
block_title_text_color_dark=CYBER_TEXT,
|
|
# Table / Dataframe
|
|
table_border_color="#2a2a2a",
|
|
table_even_background_fill="#111111",
|
|
table_even_background_fill_dark="#111111",
|
|
table_odd_background_fill=CYBER_GRAY,
|
|
table_odd_background_fill_dark=CYBER_GRAY,
|
|
table_row_focus="#1f1a00",
|
|
table_row_focus_dark="#1f1a00",
|
|
# Panel / accordion
|
|
panel_background_fill=CYBER_DARK,
|
|
panel_background_fill_dark=CYBER_DARK,
|
|
panel_border_color="#2a2a2a",
|
|
panel_border_color_dark="#2a2a2a",
|
|
# Checkbox / radio
|
|
checkbox_background_color=CYBER_DARKER,
|
|
checkbox_background_color_dark=CYBER_DARKER,
|
|
checkbox_label_background_fill=CYBER_GRAY,
|
|
checkbox_label_background_fill_dark=CYBER_GRAY,
|
|
checkbox_label_text_color=CYBER_TEXT,
|
|
checkbox_label_text_color_dark=CYBER_TEXT,
|
|
# Colors
|
|
color_accent=CYBER_YELLOW,
|
|
color_accent_soft="#1f1a00",
|
|
color_accent_soft_dark="#1f1a00",
|
|
)
|
|
|
|
|
|
# Common CSS for all demos - Cyberpunk theme
|
|
CUSTOM_CSS = """
|
|
/* Cyberpunk font import */
|
|
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
|
|
|
/* Root variables */
|
|
:root {
|
|
--cyber-yellow: #d4a700;
|
|
--cyber-gold: #ffcc00;
|
|
--cyber-dark: #0d0d0d;
|
|
--cyber-gray: #1a1a1a;
|
|
--cyber-text: #e5e5e5;
|
|
}
|
|
|
|
/* Container styling */
|
|
.gradio-container {
|
|
max-width: 1400px !important;
|
|
margin: auto !important;
|
|
background: var(--cyber-dark) !important;
|
|
}
|
|
|
|
/* Header/title styling - glitch effect */
|
|
.title-row, h1 {
|
|
color: var(--cyber-text) !important;
|
|
font-family: 'Space Grotesk', sans-serif !important;
|
|
font-weight: 700 !important;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.15em;
|
|
position: relative;
|
|
}
|
|
|
|
h1::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: -8px;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, var(--cyber-yellow), transparent);
|
|
}
|
|
|
|
/* Yellow accent lines - horizontal separator */
|
|
.cyber-line {
|
|
width: 100%;
|
|
height: 2px;
|
|
background: var(--cyber-yellow);
|
|
margin: 1.5rem 0;
|
|
box-shadow: 0 0 10px var(--cyber-yellow);
|
|
}
|
|
|
|
/* Scrolling Japanese text effect */
|
|
.cyber-marquee {
|
|
overflow: hidden;
|
|
background: linear-gradient(90deg, var(--cyber-dark), transparent 5%, transparent 95%, var(--cyber-dark));
|
|
padding: 0.5rem 0;
|
|
border-top: 1px solid var(--cyber-yellow);
|
|
border-bottom: 1px solid var(--cyber-yellow);
|
|
}
|
|
|
|
.cyber-marquee-content {
|
|
display: inline-block;
|
|
white-space: nowrap;
|
|
animation: marquee 20s linear infinite;
|
|
color: var(--cyber-yellow);
|
|
font-family: 'Space Grotesk', sans-serif;
|
|
letter-spacing: 0.5em;
|
|
}
|
|
|
|
@keyframes marquee {
|
|
0% { transform: translateX(0); }
|
|
100% { transform: translateX(-50%); }
|
|
}
|
|
|
|
/* Status indicators */
|
|
.status-ok {
|
|
color: #00ff88 !important;
|
|
font-weight: 600;
|
|
text-shadow: 0 0 10px #00ff88;
|
|
}
|
|
|
|
.status-error {
|
|
color: #ff3366 !important;
|
|
font-weight: 600;
|
|
text-shadow: 0 0 10px #ff3366;
|
|
}
|
|
|
|
.status-pending {
|
|
color: var(--cyber-yellow) !important;
|
|
font-weight: 600;
|
|
text-shadow: 0 0 10px var(--cyber-yellow);
|
|
}
|
|
|
|
/* Metrics display - terminal style */
|
|
.metrics-box {
|
|
background: rgba(13, 13, 13, 0.9) !important;
|
|
border: 1px solid var(--cyber-yellow) !important;
|
|
border-radius: 0 !important;
|
|
padding: 16px !important;
|
|
font-family: 'JetBrains Mono', monospace !important;
|
|
color: var(--cyber-gold) !important;
|
|
box-shadow: 0 0 20px rgba(212, 167, 0, 0.1);
|
|
}
|
|
|
|
/* Code blocks */
|
|
.code-block, pre, code {
|
|
background: #0a0a0a !important;
|
|
border: 1px solid #333 !important;
|
|
border-left: 3px solid var(--cyber-yellow) !important;
|
|
font-family: 'JetBrains Mono', monospace !important;
|
|
}
|
|
|
|
/* Buttons - cyber style */
|
|
.gr-button-primary {
|
|
background: var(--cyber-yellow) !important;
|
|
color: var(--cyber-dark) !important;
|
|
border: none !important;
|
|
text-transform: uppercase !important;
|
|
letter-spacing: 0.1em !important;
|
|
font-weight: 600 !important;
|
|
transition: all 0.3s ease !important;
|
|
clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
|
|
}
|
|
|
|
.gr-button-primary:hover {
|
|
background: var(--cyber-gold) !important;
|
|
box-shadow: 0 0 30px rgba(212, 167, 0, 0.5) !important;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.gr-button-secondary {
|
|
background: transparent !important;
|
|
color: var(--cyber-yellow) !important;
|
|
border: 1px solid var(--cyber-yellow) !important;
|
|
text-transform: uppercase !important;
|
|
letter-spacing: 0.1em !important;
|
|
}
|
|
|
|
/* Input fields */
|
|
.gr-input, .gr-textbox, textarea, input {
|
|
background: #0a0a0a !important;
|
|
border: 1px solid #333 !important;
|
|
color: var(--cyber-text) !important;
|
|
border-radius: 0 !important;
|
|
transition: border-color 0.3s ease !important;
|
|
}
|
|
|
|
.gr-input:focus, .gr-textbox:focus, textarea:focus, input:focus {
|
|
border-color: var(--cyber-yellow) !important;
|
|
box-shadow: 0 0 10px rgba(212, 167, 0, 0.3) !important;
|
|
}
|
|
|
|
/* Tabs - angular cyber style */
|
|
.gr-tab-nav {
|
|
border-bottom: 2px solid var(--cyber-yellow) !important;
|
|
}
|
|
|
|
.gr-tab {
|
|
background: transparent !important;
|
|
color: var(--cyber-muted) !important;
|
|
border: none !important;
|
|
text-transform: uppercase !important;
|
|
letter-spacing: 0.1em !important;
|
|
}
|
|
|
|
.gr-tab.selected {
|
|
color: var(--cyber-yellow) !important;
|
|
background: rgba(212, 167, 0, 0.1) !important;
|
|
}
|
|
|
|
/* Accordion */
|
|
.gr-accordion {
|
|
border: 1px solid #333 !important;
|
|
background: var(--cyber-gray) !important;
|
|
}
|
|
|
|
/* Labels and text */
|
|
label, .gr-label {
|
|
color: var(--cyber-yellow) !important;
|
|
text-transform: uppercase !important;
|
|
font-size: 0.75rem !important;
|
|
letter-spacing: 0.1em !important;
|
|
}
|
|
|
|
/* Slider styling */
|
|
.gr-slider {
|
|
--slider-color: var(--cyber-yellow) !important;
|
|
}
|
|
|
|
/* Footer - cyber style */
|
|
.footer {
|
|
text-align: center;
|
|
color: #666;
|
|
font-size: 0.8rem;
|
|
padding: 1.5rem;
|
|
border-top: 1px solid #333;
|
|
margin-top: 2rem;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.footer a {
|
|
color: var(--cyber-yellow);
|
|
text-decoration: none;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.footer a:hover {
|
|
text-shadow: 0 0 10px var(--cyber-yellow);
|
|
}
|
|
|
|
/* Cyber badge/tag */
|
|
.cyber-badge {
|
|
display: inline-block;
|
|
padding: 4px 12px;
|
|
background: transparent;
|
|
border: 1px solid var(--cyber-yellow);
|
|
color: var(--cyber-yellow);
|
|
font-size: 0.7rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
font-family: 'JetBrains Mono', monospace;
|
|
}
|
|
|
|
/* Progress bars */
|
|
.progress-bar {
|
|
background: #1a1a1a !important;
|
|
border: 1px solid #333 !important;
|
|
}
|
|
|
|
.progress-bar-fill {
|
|
background: linear-gradient(90deg, var(--cyber-yellow), var(--cyber-gold)) !important;
|
|
}
|
|
|
|
/* Scrollbar styling */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: var(--cyber-dark);
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: #333;
|
|
border: 1px solid var(--cyber-yellow);
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: #444;
|
|
}
|
|
|
|
/* Glowing text effect utility */
|
|
.glow-text {
|
|
text-shadow: 0 0 10px var(--cyber-yellow), 0 0 20px var(--cyber-yellow);
|
|
}
|
|
|
|
/* ── Examples table / Dataframe overrides ── */
|
|
/* Gradio renders Examples as a <table> inside a Dataset component.
|
|
The default styles inject white / light-gray rows that blow out the
|
|
cyberpunk palette. Force them dark here. */
|
|
.gr-samples-table,
|
|
.gr-sample-textbox,
|
|
table.table,
|
|
.gr-examples table,
|
|
div[class*="dataset"] table {
|
|
background: var(--cyber-dark) !important;
|
|
color: var(--cyber-text) !important;
|
|
}
|
|
|
|
.gr-samples-table tr,
|
|
.gr-examples table tr,
|
|
div[class*="dataset"] table tr {
|
|
background: #111111 !important;
|
|
border-bottom: 1px solid #222 !important;
|
|
}
|
|
|
|
.gr-samples-table tr:nth-child(even),
|
|
.gr-examples table tr:nth-child(even),
|
|
div[class*="dataset"] table tr:nth-child(even) {
|
|
background: #0d0d0d !important;
|
|
}
|
|
|
|
.gr-samples-table tr:hover,
|
|
.gr-examples table tr:hover,
|
|
div[class*="dataset"] table tr:hover {
|
|
background: #1f1a00 !important;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.gr-samples-table th,
|
|
.gr-examples table th,
|
|
div[class*="dataset"] table th {
|
|
background: var(--cyber-gray) !important;
|
|
color: var(--cyber-yellow) !important;
|
|
text-transform: uppercase !important;
|
|
font-size: 0.75rem !important;
|
|
letter-spacing: 0.1em !important;
|
|
border-bottom: 2px solid var(--cyber-yellow) !important;
|
|
padding: 10px 16px !important;
|
|
}
|
|
|
|
.gr-samples-table td,
|
|
.gr-examples table td,
|
|
div[class*="dataset"] table td {
|
|
color: #999 !important;
|
|
border-bottom: 1px solid #1a1a1a !important;
|
|
padding: 10px 16px !important;
|
|
font-family: 'JetBrains Mono', monospace !important;
|
|
font-size: 0.85rem !important;
|
|
}
|
|
|
|
/* ── Block label pill (e.g. "GENERATED AUDIO", "STATUS") ── */
|
|
/* These are the small floating labels above each component block */
|
|
span[class*="label-wrap"],
|
|
.gr-block-label,
|
|
.label-wrap {
|
|
background: #1a1a00 !important;
|
|
border: 1px solid var(--cyber-yellow) !important;
|
|
color: var(--cyber-yellow) !important;
|
|
}
|
|
|
|
/* ── Dropdown / select menus ── */
|
|
.gr-dropdown,
|
|
select,
|
|
ul[role="listbox"],
|
|
div[class*="dropdown"],
|
|
.secondary-wrap {
|
|
background: #0a0a0a !important;
|
|
color: var(--cyber-text) !important;
|
|
border-color: #333 !important;
|
|
}
|
|
|
|
ul[role="listbox"] li,
|
|
div[class*="dropdown"] li {
|
|
background: #0a0a0a !important;
|
|
color: var(--cyber-text) !important;
|
|
}
|
|
|
|
ul[role="listbox"] li:hover,
|
|
ul[role="listbox"] li[aria-selected="true"],
|
|
div[class*="dropdown"] li:hover {
|
|
background: #1f1a00 !important;
|
|
color: var(--cyber-yellow) !important;
|
|
}
|
|
|
|
/* ── Audio player ── */
|
|
.gr-audio,
|
|
audio {
|
|
background: var(--cyber-dark) !important;
|
|
border: 1px solid #2a2a2a !important;
|
|
}
|
|
|
|
/* Audio waveform container */
|
|
div[data-testid="waveform-container"],
|
|
div[class*="audio"] {
|
|
background: #0a0a0a !important;
|
|
}
|
|
|
|
/* ── Markdown inside blocks ── */
|
|
.gr-markdown,
|
|
.gr-markdown p,
|
|
.prose {
|
|
color: var(--cyber-text) !important;
|
|
}
|
|
|
|
.gr-markdown h3,
|
|
.gr-markdown h2 {
|
|
color: var(--cyber-yellow) !important;
|
|
letter-spacing: 0.05em !important;
|
|
}
|
|
|
|
.gr-markdown strong {
|
|
color: var(--cyber-gold) !important;
|
|
}
|
|
|
|
/* ── Examples accordion header ("Examples" label) ── */
|
|
.gr-examples .label-wrap,
|
|
div[id*="examples"] .label-wrap,
|
|
span[data-testid="block-label"] {
|
|
background: #1a1a00 !important;
|
|
color: var(--cyber-yellow) !important;
|
|
border: 1px solid var(--cyber-yellow) !important;
|
|
font-size: 0.7rem !important;
|
|
text-transform: uppercase !important;
|
|
letter-spacing: 0.1em !important;
|
|
}
|
|
|
|
/* ── Misc: tooltip, info text ── */
|
|
.gr-info,
|
|
.gr-description {
|
|
color: #666 !important;
|
|
}
|
|
|
|
/* ── Svelte internal: make sure no white backgrounds leak ── */
|
|
.contain > div,
|
|
.wrap > div {
|
|
background: inherit !important;
|
|
}
|
|
|
|
/* ── Tab content panels ── */
|
|
.tabitem {
|
|
background: var(--cyber-dark) !important;
|
|
}
|
|
"""
|
|
|
|
|
|
def create_header(title: str, description: str) -> gr.Markdown:
|
|
"""Create a cyberpunk-style header for demo apps."""
|
|
# Japanese text for marquee effect
|
|
jp_text = "サイバー · コマース · フューチャー · "
|
|
return gr.Markdown(f"""
|
|
<div style="margin-bottom: 2rem;">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
|
<span class="cyber-badge">STORE</span>
|
|
<span class="cyber-badge">v2.0</span>
|
|
<span class="cyber-badge">ONLINE</span>
|
|
</div>
|
|
|
|
<h1 style="font-size: 3rem; margin: 0; letter-spacing: 0.2em;">{title.upper()}</h1>
|
|
<p style="color: #888; margin-top: 0.5rem; font-family: 'JetBrains Mono', monospace; font-size: 0.9rem;">{description}</p>
|
|
|
|
<div class="cyber-line"></div>
|
|
|
|
<div class="cyber-marquee">
|
|
<span class="cyber-marquee-content">{jp_text * 8}</span>
|
|
</div>
|
|
</div>
|
|
""")
|
|
|
|
|
|
def create_footer() -> gr.Markdown:
|
|
"""Create a cyberpunk-style footer for demo apps."""
|
|
return gr.Markdown("""
|
|
<div class="cyber-line"></div>
|
|
<div class="footer">
|
|
<span class="cyber-badge" style="margin-right: 1rem;">AR</span>
|
|
<span style="color: #666;">DAVIES TECH LABS</span>
|
|
<span style="color: #444; margin: 0 1rem;">·</span>
|
|
<a href="https://mlflow.lab.daviestechlabs.io" target="_blank">MLFLOW</a>
|
|
<span style="color: #444; margin: 0 1rem;">·</span>
|
|
<a href="https://kubeflow.lab.daviestechlabs.io" target="_blank">KUBEFLOW</a>
|
|
<span style="color: #444; margin: 0 1rem;">·</span>
|
|
<span style="color: #666;">[ スクロール ]</span>
|
|
</div>
|
|
""")
|