From 570103d83ea7cf384d4c79f97931e008725cb76e Mon Sep 17 00:00:00 2001 From: egutierrez Date: Fri, 24 Apr 2026 14:52:08 +0200 Subject: [PATCH] feat(ui): migrar a tokens + primitivos del design system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dashboard ahora usa: - fn_tokens::apply_dark_theme() al primer frame (colors + spacing + radius + rounding consistentes con @fn_library / Mantine). - page_header_begin/end para el header (en vez de Text + Separator + Button manual). Subtítulo con conteos de entidades. - empty_state en las 4 tablas cuando están vacías — mensaje amable con acción sugerida en lugar de tabla vacía silenciosa. Nuevas deps de compilación (.cpp fuentes añadidas al CMakeLists): tokens.cpp, badge.cpp, empty_state.cpp, page_header.cpp. Co-Authored-By: Claude Opus 4.7 (1M context) --- CMakeLists.txt | 5 +++++ views.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0d65b1..1b69476 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,11 @@ add_imgui_app(registry_dashboard ${CMAKE_SOURCE_DIR}/functions/core/dashboard_grid.cpp ${CMAKE_SOURCE_DIR}/functions/core/fps_overlay.cpp ${CMAKE_SOURCE_DIR}/functions/core/fullscreen_window.cpp + # Design tokens + primitives (fase 1 y 2 del plan del dashboard) + ${CMAKE_SOURCE_DIR}/functions/core/tokens.cpp + ${CMAKE_SOURCE_DIR}/functions/core/badge.cpp + ${CMAKE_SOURCE_DIR}/functions/core/empty_state.cpp + ${CMAKE_SOURCE_DIR}/functions/core/page_header.cpp ) target_include_directories(registry_dashboard PRIVATE diff --git a/views.cpp b/views.cpp index 0303d75..cc4d680 100644 --- a/views.cpp +++ b/views.cpp @@ -11,7 +11,12 @@ #include "core/dashboard_grid.h" #include "core/fps_overlay.h" #include "core/fullscreen_window.h" +#include "core/tokens.h" +#include "core/page_header.h" +#include "core/empty_state.h" +#include "core/badge.h" +#include #include static std::vector to_cstr(const std::vector& v) { @@ -87,6 +92,11 @@ void draw_charts(RegistryData& data, float height) { } void draw_recent_functions(const std::vector& funcs) { + if (funcs.empty()) { + empty_state("( no data )", "No functions yet", + "Run 'fn index' to populate the registry"); + return; + } const char* headers[] = {"Name", "Lang", "Domain", "Kind", "Purity", "Tested", "Created"}; constexpr int cols = 7; std::vector cell_strings; @@ -105,6 +115,11 @@ void draw_recent_functions(const std::vector& funcs) { } void draw_apps_list(const std::vector& apps) { + if (apps.empty()) { + empty_state("( no data )", "No apps registered", + "Clone apps with 'fn app clone ' or run 'fn sync'"); + return; + } const char* headers[] = {"Name", "Lang", "Domain", "Framework", "Description"}; constexpr int cols = 5; std::vector cell_strings; @@ -121,6 +136,11 @@ void draw_apps_list(const std::vector& apps) { } void draw_analysis_list(const std::vector& analyses) { + if (analyses.empty()) { + empty_state("( no data )", "No analysis yet", + "Create one with 'fn run init_jupyter_analysis '"); + return; + } const char* headers[] = {"Name", "Domain", "Description"}; constexpr int cols = 3; std::vector cell_strings; @@ -135,6 +155,11 @@ void draw_analysis_list(const std::vector& analyses) { } void draw_types_list(const std::vector& types) { + if (types.empty()) { + empty_state("( no data )", "No types yet", + "Types are indexed from the registry alongside functions"); + return; + } const char* headers[] = {"Name", "Lang", "Domain", "Algebraic", "Description"}; constexpr int cols = 5; std::vector cell_strings; @@ -151,31 +176,44 @@ void draw_types_list(const std::vector& types) { } void draw_dashboard(RegistryData& data) { + // Aplicar tema una sola vez por vida de la app. + static bool theme_applied = false; + if (!theme_applied) { + fn_tokens::apply_dark_theme(); + theme_applied = true; + } + fps_overlay(); fullscreen_window_begin("##dashboard"); - float win_h = ImGui::GetContentRegionAvail().y; + // Subtitle con conteos — contexto rápido para el usuario + char subtitle[128]; + std::snprintf(subtitle, sizeof(subtitle), + "%d functions · %d types · %d apps · %d analyses", + data.stats.total_functions, data.stats.total_types, + data.stats.total_apps, data.stats.total_analysis); - // Row 1: Header + KPIs (~80px) - ImGui::Text("fn_registry Dashboard"); - ImGui::SameLine(ImGui::GetWindowWidth() - 100); + // Header con acción Reload a la derecha + page_header_begin("fn_registry Dashboard", subtitle); + ImGui::SameLine(ImGui::GetWindowWidth() - 120.0f); if (ImGui::Button("Reload")) { ImGui::GetIO().UserData = reinterpret_cast(1); } - ImGui::Separator(); + page_header_end(); + // KPIs draw_kpi_row(data.stats); - ImGui::Separator(); + ImGui::Dummy(ImVec2(0, fn_tokens::spacing::md)); - // Row 2: Charts — take 35% of remaining height + // Charts — 35% de la altura restante float remaining = ImGui::GetContentRegionAvail().y; float chart_h = remaining * 0.35f; if (chart_h < 150.0f) chart_h = 150.0f; draw_charts(data, chart_h); - ImGui::Separator(); + ImGui::Dummy(ImVec2(0, fn_tokens::spacing::md)); - // Row 3: Tables — fill the rest + // Tables if (ImGui::BeginTabBar("##tables")) { if (ImGui::BeginTabItem("Recent Functions")) { draw_recent_functions(data.recent_funcs);