From 2acba2077b22d5b2926d4e71b582b15c73308f72 Mon Sep 17 00:00:00 2001 From: AdrianoDev Date: Mon, 11 May 2026 22:43:48 +0200 Subject: [PATCH] style(dashboard): redesign Neon Trading Dashboard ispirato a screenshot riferimento MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sostituita palette Modern Fintech con look "neon trading" pink+cyan: - BG #0A0A0F near-black con dual radial glow (pink top-left, cyan bottom-right) - Surface #13131A card base, hover #1C1C26 - Primary pink #FF2D87 (max fitness, key metrics) - Secondary cyan #00D9FF (median, entropy) - Accent amber #FFB800 (p90, warnings) - Success neon green #00E676, danger neon red #FF3D60 Effetti visivi aggiunti: - Card top-border glow gradient (linea pink luminosa) - Card hover: pink shadow esterno + border 0.5 opacity - Inset shadow per dare profondità (subtle highlight top) - Brand-dot pulsing animation 2s (glow pink pulsato) - Header glassmorphic blur(20px) saturate(180%) - Chart fill area gradient sotto le curve max/entropy - Linee Plotly spline smoothing 0.6 (curve morbide) - Marker bordo bianco per pop Co-Authored-By: Claude Opus 4.7 (1M context) --- src/multi_swarm/dashboard/nicegui_app.py | 126 ++++++++++++++--------- 1 file changed, 80 insertions(+), 46 deletions(-) diff --git a/src/multi_swarm/dashboard/nicegui_app.py b/src/multi_swarm/dashboard/nicegui_app.py index 4641cbe..4661d57 100644 --- a/src/multi_swarm/dashboard/nicegui_app.py +++ b/src/multi_swarm/dashboard/nicegui_app.py @@ -5,15 +5,15 @@ Default port 8080. Streamlit resta su 8501 durante la migrazione. Riusa ``dashboard.data`` (Repository helpers) senza modifiche al backend. -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) +Palette "Neon Trading Dashboard" (ispirata screenshot 2026-05-11): +- BG: #0A0A0F (near-black con tinge blu) +- Surface: #13131A (card base) +- Surface elevata: #1C1C26 (hover/active) +- Primary pink: #FF2D87 (highlight key metrics, max fitness) +- Secondary cyan: #00D9FF (median, secondary curves) +- Accent amber: #FFB800 (warnings, p90) +- Success neon green: #00E676, Danger neon red: #FF3D60 +- Text: #FFFFFF (primary), #7A7A8C (muted) """ from __future__ import annotations @@ -38,18 +38,19 @@ from multi_swarm.dashboard.data import ( DB_PATH = os.environ.get("DB_PATH", "./runs.db") REFRESH_INTERVAL_S = 3.0 -# --- 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" +# --- Neon Trading Dashboard palette --- +COLOR_BG = "#0A0A0F" +COLOR_SURFACE = "#13131A" +COLOR_SURFACE_2 = "#1C1C26" +COLOR_BORDER = "rgba(255, 45, 135, 0.12)" +COLOR_BORDER_HOVER = "rgba(255, 45, 135, 0.45)" +COLOR_PRIMARY = "#FF2D87" +COLOR_SECONDARY = "#00D9FF" +COLOR_ACCENT = "#FFB800" +COLOR_SUCCESS = "#00E676" +COLOR_DANGER = "#FF3D60" +COLOR_TEXT = "#FFFFFF" +COLOR_TEXT_MUTED = "#7A7A8C" _STATUS_BADGE: dict[str, tuple[str, str]] = { "running": ("● running", "positive"), @@ -67,18 +68,40 @@ code, pre, .q-code {{ font-family: 'JetBrains Mono', 'Fira Code', monospace !imp body, .q-page-container, .q-page {{ background: {COLOR_BG} !important; color: {COLOR_TEXT}; - background-image: radial-gradient(ellipse at top, rgba(6, 182, 212, 0.04) 0%, transparent 50%); + background-image: + radial-gradient(ellipse 800px 400px at 20% 0%, rgba(255, 45, 135, 0.08) 0%, transparent 60%), + radial-gradient(ellipse 600px 400px at 80% 100%, rgba(0, 217, 255, 0.06) 0%, transparent 60%); + background-attachment: fixed; }} .q-card {{ background: {COLOR_SURFACE} !important; color: {COLOR_TEXT} !important; border: 1px solid {COLOR_BORDER}; - border-radius: 12px !important; - box-shadow: 0 1px 2px rgba(0,0,0,0.3), 0 4px 12px rgba(0,0,0,0.15); - transition: border-color 0.2s, transform 0.2s; + border-radius: 14px !important; + box-shadow: + 0 1px 2px rgba(0,0,0,0.5), + 0 8px 24px rgba(0,0,0,0.25), + inset 0 1px 0 rgba(255,255,255,0.04); + transition: all 0.2s ease; + position: relative; + overflow: hidden; +}} +.q-card::before {{ + content: ''; + position: absolute; + top: 0; left: 0; right: 0; + height: 1px; + background: linear-gradient(90deg, transparent, rgba(255, 45, 135, 0.4), transparent); + opacity: 0.5; +}} +.q-card:hover {{ + border-color: rgba(255, 45, 135, 0.5); + box-shadow: + 0 1px 2px rgba(0,0,0,0.5), + 0 8px 32px rgba(255, 45, 135, 0.15), + inset 0 1px 0 rgba(255,255,255,0.05); }} -.q-card:hover {{ border-color: rgba(6, 182, 212, 0.35); }} .metric-card {{ padding: 20px 16px; @@ -108,11 +131,11 @@ body, .q-page-container, .q-page {{ .metric-card.accent-green .text-h4 {{ color: {COLOR_SUCCESS} !important; }} .q-header {{ - background: rgba(19, 24, 41, 0.85) !important; - backdrop-filter: blur(12px); - -webkit-backdrop-filter: blur(12px); + background: rgba(10, 10, 15, 0.75) !important; + backdrop-filter: blur(20px) saturate(180%); + -webkit-backdrop-filter: blur(20px) saturate(180%); border-bottom: 1px solid {COLOR_BORDER} !important; - box-shadow: none !important; + box-shadow: 0 1px 0 rgba(255, 45, 135, 0.15) !important; }} .nav-link {{ @@ -131,7 +154,7 @@ body, .q-page-container, .q-page {{ }} .nav-link.active {{ color: {COLOR_PRIMARY} !important; - background: rgba(6, 182, 212, 0.08); + background: rgba(255, 45, 135, 0.08); }} .nav-link.active::after {{ content: ''; @@ -153,11 +176,16 @@ body, .q-page-container, .q-page {{ gap: 8px; }} .brand-dot {{ - width: 8px; - height: 8px; + width: 10px; + height: 10px; border-radius: 50%; background: {COLOR_PRIMARY}; - box-shadow: 0 0 12px {COLOR_PRIMARY}; + box-shadow: 0 0 16px {COLOR_PRIMARY}, 0 0 4px {COLOR_PRIMARY}; + animation: pulse-pink 2s ease-in-out infinite; +}} +@keyframes pulse-pink {{ + 0%, 100% {{ box-shadow: 0 0 16px {COLOR_PRIMARY}, 0 0 4px {COLOR_PRIMARY}; }} + 50% {{ box-shadow: 0 0 24px {COLOR_PRIMARY}, 0 0 8px {COLOR_PRIMARY}; }} }} .q-linear-progress {{ height: 8px !important; border-radius: 6px !important; }} @@ -187,8 +215,8 @@ body, .q-page-container, .q-page {{ letter-spacing: 0.06em; }} .q-table tbody tr {{ transition: background 0.15s; }} -.q-table tbody tr:hover {{ background: rgba(6, 182, 212, 0.05) !important; }} -.q-table tbody tr.selected {{ background: rgba(6, 182, 212, 0.12) !important; }} +.q-table tbody tr:hover {{ background: rgba(255, 45, 135, 0.05) !important; }} +.q-table tbody tr.selected {{ background: rgba(255, 45, 135, 0.12) !important; }} .q-table td {{ border-bottom: 1px solid {COLOR_BORDER} !important; font-feature-settings: 'tnum'; }} .text-h6 {{ font-weight: 600 !important; letter-spacing: -0.015em !important; }} @@ -202,11 +230,11 @@ body, .q-page-container, .q-page {{ }} code, pre {{ - background: #0D1220 !important; + background: #0F0F15 !important; color: {COLOR_TEXT} !important; border: 1px solid {COLOR_BORDER}; - border-radius: 8px !important; - padding: 12px !important; + border-radius: 10px !important; + padding: 14px !important; }} .q-badge {{ border-radius: 6px !important; font-weight: 500 !important; padding: 4px 10px !important; font-size: 12px !important; }} @@ -312,15 +340,18 @@ def _convergence_figure(gens_df: Any) -> go.Figure: go.Scatter( x=gens_df["generation_idx"], y=gens_df["fitness_max"], name="max", mode="lines+markers", - line={"color": COLOR_PRIMARY, "width": 3}, - marker={"size": 9, "color": COLOR_PRIMARY}, + line={"color": COLOR_PRIMARY, "width": 3, "shape": "spline", "smoothing": 0.6}, + marker={"size": 9, "color": COLOR_PRIMARY, + "line": {"color": "#fff", "width": 1}}, + fill="tozeroy", + fillcolor="rgba(255, 45, 135, 0.12)", ) ) fig.add_trace( go.Scatter( x=gens_df["generation_idx"], y=gens_df["fitness_p90"], name="p90", mode="lines+markers", - line={"color": COLOR_ACCENT, "width": 2, "dash": "dot"}, + line={"color": COLOR_ACCENT, "width": 2, "dash": "dot", "shape": "spline"}, marker={"size": 7, "color": COLOR_ACCENT}, ) ) @@ -328,7 +359,7 @@ def _convergence_figure(gens_df: Any) -> go.Figure: go.Scatter( x=gens_df["generation_idx"], y=gens_df["fitness_median"], name="median", mode="lines+markers", - line={"color": COLOR_SECONDARY, "width": 2}, + line={"color": COLOR_SECONDARY, "width": 2, "shape": "spline"}, marker={"size": 7, "color": COLOR_SECONDARY}, ) ) @@ -340,7 +371,7 @@ def _convergence_figure(gens_df: Any) -> go.Figure: 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(19, 24, 41, 0.95)", "bordercolor": COLOR_PRIMARY, "borderwidth": 1}, + legend={"bgcolor": "rgba(19, 19, 26, 0.95)", "bordercolor": COLOR_PRIMARY, "borderwidth": 1}, margin={"l": 50, "r": 30, "t": 50, "b": 50}, ) return fig @@ -353,8 +384,11 @@ def _entropy_figure(gens_df: Any) -> go.Figure: go.Scatter( x=gens_df["generation_idx"], y=gens_df["entropy"], mode="lines+markers", - line={"color": COLOR_PRIMARY, "width": 3}, - marker={"size": 9, "color": COLOR_PRIMARY}, + line={"color": COLOR_SECONDARY, "width": 3, "shape": "spline", "smoothing": 0.6}, + marker={"size": 9, "color": COLOR_SECONDARY, + "line": {"color": "#fff", "width": 1}}, + fill="tozeroy", + fillcolor="rgba(0, 217, 255, 0.12)", name="entropy", ) )