diff --git a/src/multi_swarm/dashboard/nicegui_app.py b/src/multi_swarm/dashboard/nicegui_app.py
index db4c565..4641cbe 100644
--- a/src/multi_swarm/dashboard/nicegui_app.py
+++ b/src/multi_swarm/dashboard/nicegui_app.py
@@ -5,11 +5,15 @@ Default port 8080. Streamlit resta su 8501 durante la migrazione.
Riusa ``dashboard.data`` (Repository helpers) senza modifiche al backend.
-Palette Inter FC:
-- Nero: #000000 (sfondo)
-- Dark navy: #010E80 (heritage Inter, surface)
-- Blu primary: #1E5BC6
-- Blu secondary: #0068A8
+Palette "Modern Fintech Dark":
+- BG: #0A0E1A (deep blue-black)
+- Surface: #131829 (card base)
+- Surface elevata: #1A2138 (hover/active)
+- Primary cyan: #06B6D4 (performance highlights)
+- Secondary purple: #A78BFA (median, secondary curves)
+- Accent amber: #F59E0B (warnings, p90)
+- Success green: #10B981, Danger red: #EF4444
+- Text: #F1F5F9 (primary), #94A3B8 (muted)
"""
from __future__ import annotations
@@ -34,57 +38,178 @@ from multi_swarm.dashboard.data import (
DB_PATH = os.environ.get("DB_PATH", "./runs.db")
REFRESH_INTERVAL_S = 3.0
-# --- Inter FC palette ---
-COLOR_BG = "#000000"
-COLOR_SURFACE = "#010E80"
-COLOR_PRIMARY = "#1E5BC6"
-COLOR_SECONDARY = "#0068A8"
-COLOR_ACCENT = "#FFD700"
-COLOR_TEXT = "#E8EEFC"
-COLOR_TEXT_MUTED = "#8FA3D4"
+# --- Modern Fintech Dark palette ---
+COLOR_BG = "#0A0E1A"
+COLOR_SURFACE = "#131829"
+COLOR_SURFACE_2 = "#1A2138"
+COLOR_BORDER = "rgba(148, 163, 184, 0.12)"
+COLOR_PRIMARY = "#06B6D4"
+COLOR_SECONDARY = "#A78BFA"
+COLOR_ACCENT = "#F59E0B"
+COLOR_SUCCESS = "#10B981"
+COLOR_DANGER = "#EF4444"
+COLOR_TEXT = "#F1F5F9"
+COLOR_TEXT_MUTED = "#94A3B8"
_STATUS_BADGE: dict[str, tuple[str, str]] = {
- "running": ("🟢 running", "positive"),
- "completed": ("✅ completed", "positive"),
- "failed": ("❌ failed", "negative"),
+ "running": ("● running", "positive"),
+ "completed": ("✓ completed", "positive"),
+ "failed": ("✕ failed", "negative"),
}
_CUSTOM_CSS = f"""
"""
@@ -98,29 +223,32 @@ def _apply_theme() -> None:
accent=COLOR_ACCENT,
dark=COLOR_BG,
dark_page=COLOR_BG,
- positive="#00C853",
- negative="#D32F2F",
- info=COLOR_SECONDARY,
- warning="#FFB300",
+ positive=COLOR_SUCCESS,
+ negative=COLOR_DANGER,
+ info=COLOR_PRIMARY,
+ warning=COLOR_ACCENT,
)
def _build_header(active: str) -> None:
- with ui.header().classes("items-center justify-between q-px-md"):
- with ui.row().classes("items-center gap-6"):
- ui.label("⚫🔵 Multi-Swarm Coevolutivo").classes("text-h6").style(
- f"color: {COLOR_TEXT}; font-weight: 700;"
- )
- for path, label in (
- ("/", "Overview"),
- ("/convergence", "GA Convergence"),
- ("/genomes", "Genomes"),
- ):
- cls = "nav-link active" if active == path else "nav-link"
- ui.link(label, path).classes(cls)
- ui.label(f"DB: {Path(DB_PATH).resolve().name}").classes("text-caption").style(
- f"color: {COLOR_TEXT_MUTED};"
- )
+ with ui.header().classes("items-center justify-between q-px-lg q-py-md"):
+ with ui.row().classes("items-center gap-8"):
+ with ui.row().classes("items-center gap-2").classes("brand"):
+ ui.html('')
+ ui.html('Multi-Swarm / Coevolutivo')
+ with ui.row().classes("items-center gap-1"):
+ for path, label in (
+ ("/", "Overview"),
+ ("/convergence", "Convergence"),
+ ("/genomes", "Genomes"),
+ ):
+ cls = "nav-link active" if active == path else "nav-link"
+ ui.link(label, path).classes(cls)
+ with ui.row().classes("items-center gap-3"):
+ ui.html(f''
+ f'⛁ {Path(DB_PATH).resolve().name}')
def _runs_options() -> dict[str, str]:
@@ -206,13 +334,13 @@ def _convergence_figure(gens_df: Any) -> go.Figure:
)
fig.update_layout(
template="plotly_dark",
- paper_bgcolor=COLOR_BG,
- plot_bgcolor=COLOR_BG,
+ paper_bgcolor=COLOR_SURFACE,
+ plot_bgcolor=COLOR_SURFACE,
font={"color": COLOR_TEXT},
- xaxis={"title": "generation", "gridcolor": "rgba(30, 91, 198, 0.2)", "dtick": 1},
- yaxis={"title": "fitness", "gridcolor": "rgba(30, 91, 198, 0.2)"},
+ xaxis={"title": "generation", "gridcolor": "rgba(148, 163, 184, 0.08)", "dtick": 1},
+ yaxis={"title": "fitness", "gridcolor": "rgba(148, 163, 184, 0.08)"},
title={"text": "Fitness convergence", "font": {"color": COLOR_TEXT, "size": 18}},
- legend={"bgcolor": "rgba(1, 14, 128, 0.6)", "bordercolor": COLOR_PRIMARY, "borderwidth": 1},
+ legend={"bgcolor": "rgba(19, 24, 41, 0.95)", "bordercolor": COLOR_PRIMARY, "borderwidth": 1},
margin={"l": 50, "r": 30, "t": 50, "b": 50},
)
return fig
@@ -237,11 +365,11 @@ def _entropy_figure(gens_df: Any) -> go.Figure:
)
fig.update_layout(
template="plotly_dark",
- paper_bgcolor=COLOR_BG,
- plot_bgcolor=COLOR_BG,
+ paper_bgcolor=COLOR_SURFACE,
+ plot_bgcolor=COLOR_SURFACE,
font={"color": COLOR_TEXT},
- xaxis={"title": "generation", "gridcolor": "rgba(30, 91, 198, 0.2)", "dtick": 1},
- yaxis={"title": "entropy", "gridcolor": "rgba(30, 91, 198, 0.2)"},
+ xaxis={"title": "generation", "gridcolor": "rgba(148, 163, 184, 0.08)", "dtick": 1},
+ yaxis={"title": "entropy", "gridcolor": "rgba(148, 163, 184, 0.08)"},
title={"text": "Diversity (fitness entropy)", "font": {"color": COLOR_TEXT, "size": 18}},
margin={"l": 50, "r": 30, "t": 50, "b": 50},
)
@@ -275,16 +403,16 @@ def index() -> None:
eval_bar = ui.linear_progress(0.0, show_value=False).props("size=20px color=accent")
with ui.row().classes("w-full gap-4"):
- with ui.card().classes("flex-grow metric-card"):
+ with ui.card().classes("flex-grow metric-card accent-cyan"):
ui.label("Top fitness").classes("text-caption")
top_lbl = ui.label("—").classes("text-h4")
- with ui.card().classes("flex-grow metric-card"):
+ with ui.card().classes("flex-grow metric-card accent-purple"):
ui.label("Median fitness").classes("text-caption")
median_lbl = ui.label("—").classes("text-h4")
- with ui.card().classes("flex-grow metric-card"):
+ with ui.card().classes("flex-grow metric-card accent-amber"):
ui.label("Parse success").classes("text-caption")
parse_lbl = ui.label("—").classes("text-h4")
- with ui.card().classes("flex-grow metric-card"):
+ with ui.card().classes("flex-grow metric-card accent-green"):
ui.label("Cost (USD)").classes("text-caption")
cost_lbl = ui.label("—").classes("text-h4")
@@ -475,19 +603,19 @@ def genomes() -> None:
).style(f"color: {COLOR_TEXT_MUTED};")
with ui.row().classes("w-full gap-4 q-mt-sm"):
- with ui.card().classes("flex-grow metric-card"):
+ with ui.card().classes("flex-grow metric-card accent-cyan"):
ui.label("fitness").classes("text-caption")
fit_lbl = ui.label("—").classes("text-h4")
- with ui.card().classes("flex-grow metric-card"):
+ with ui.card().classes("flex-grow metric-card accent-purple"):
ui.label("DSR").classes("text-caption")
dsr_lbl = ui.label("—").classes("text-h4")
- with ui.card().classes("flex-grow metric-card"):
+ with ui.card().classes("flex-grow metric-card accent-amber"):
ui.label("Sharpe").classes("text-caption")
sharpe_lbl = ui.label("—").classes("text-h4")
with ui.card().classes("flex-grow metric-card"):
ui.label("max DD").classes("text-caption")
dd_lbl = ui.label("—").classes("text-h4")
- with ui.card().classes("flex-grow metric-card"):
+ with ui.card().classes("flex-grow metric-card accent-green"):
ui.label("trades").classes("text-caption")
trades_lbl = ui.label("—").classes("text-h4")
with ui.card().classes("flex-grow metric-card"):