chore: auto-commit (23 archivos)

- CMakeLists.txt
- app.md
- appicon.ico
- main.cpp
- perf_tests.cpp
- perf_tests.h
- qa_panel.cpp
- qa_panel.h
- qa_state.cpp
- qa_state.h
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 00:31:32 +02:00
commit b15106fc09
23 changed files with 2536 additions and 0 deletions
+296
View File
@@ -0,0 +1,296 @@
// tab_compat — Version selector + side-by-side rendering (simulación educativa).
// Muestra la MISMA TableInput renderizada dos veces con distinta ColumnSpec config:
// Left panel : full renderers (v2.1 current).
// Right panel : renderers degradados según la versión simulada seleccionada.
//
// Issue 0108 fase 2. Patrón: tab_renderers.cpp.
// NOTA: esto es una SIMULACIÓN educativa del compat mode futuro.
// El compat real requiere flag en el módulo (TBD issue futuro).
#include "tabs.h"
#include "qa_state.h"
#include "data_table/data_table.h"
#include <imgui.h>
#include <string>
#include <vector>
namespace tables_qa::tabs {
namespace {
// ---------------------------------------------------------------------------
// Datos: 8 filas × 5 cols (id, name, status, duration_ms, priority).
// ---------------------------------------------------------------------------
qa::TabState g_left; // panel izquierdo — full config v2.1
qa::TabState g_right; // panel derecho — config degradada
static const char* k_headers[] = { "id", "name", "status", "duration_ms", "priority" };
static const int k_cols = 5;
static const int k_rows = 8;
// backing strings: row-major id/name/status/duration_ms/priority
static const char* k_cells_raw[k_rows * k_cols] = {
"1", "auth_service", "ok", "320", "high",
"2", "db_sync", "error", "4800", "critical",
"3", "cache_warmup", "running", "750", "medium",
"4", "report_gen", "pending", "2100", "low",
"5", "email_dispatch", "ok", "180", "high",
"6", "index_rebuild", "error", "5900", "critical",
"7", "metrics_collector", "running", "950", "medium",
"8", "snapshot_export", "pending", "1500", "low",
};
// ---------------------------------------------------------------------------
// Versiones simuladas
// ---------------------------------------------------------------------------
enum class SimVersion {
V21 = 0, // v2.1.0 — full renderers
V20 = 1, // v2.0.0 — full renderers, solo split refactor
V15 = 2, // v1.5.0 — no CategoricalChip → fallback Text en status
V14 = 3, // v1.4.0 — no Dots, no CategoricalChip, no ColorScale → Badge básico
};
static const char* k_version_labels[] = {
"v2.1.0 (current)",
"v2.0.0 (split refactor, MODE: no renderer config)",
"v1.5.0 (legacy, MODE: no CategoricalChip)",
"v1.4.0 (legacy, MODE: no Dots + no ColorScale)",
};
static int g_selected_version = 0;
// ---------------------------------------------------------------------------
// Construir TableInput — cabeceras + tipos + punteros a datos estáticos.
// ColumnSpec se rellena por cada panel por separado.
// ---------------------------------------------------------------------------
static data_table::TableInput make_base_table(const char* tbl_name) {
data_table::TableInput t;
t.name = tbl_name;
t.headers = { "id", "name", "status", "duration_ms", "priority" };
t.types = {
data_table::ColumnType::Int,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::Float,
data_table::ColumnType::String,
};
t.cells = k_cells_raw;
t.rows = k_rows;
t.cols = k_cols;
return t;
}
// Specifica las reglas de CategoricalChip para la col status
static void apply_status_chip(data_table::ColumnSpec& cs) {
cs.renderer = data_table::CellRenderer::CategoricalChip;
cs.chips = {
{ "ok", "#22c55e" },
{ "error", "#ef4444" },
{ "running", "#f59e0b" },
{ "pending", "#a3a3a3" },
};
}
// Specifica Badge básico (fallback sin CategoricalChip)
static void apply_status_badge(data_table::ColumnSpec& cs) {
cs.renderer = data_table::CellRenderer::Badge;
cs.badges = {
{ "ok", "#22c55e", "" },
{ "error", "#ef4444", "ERR" },
{ "running", "#f59e0b", "RUN" },
{ "pending", "#a3a3a3", "..." },
};
}
// Aplica specs para panel LEFT — siempre v2.1 full
static void apply_full_specs(data_table::TableInput& t) {
t.column_specs.resize(k_cols);
for (int i = 0; i < k_cols; i++) t.column_specs[i].id = k_headers[i];
// col 0: id — Text (default)
// col 1: name — Text (default)
// col 2: status — CategoricalChip
apply_status_chip(t.column_specs[2]);
// col 3: duration_ms — Duration renderer
{
auto& cs = t.column_specs[3];
cs.renderer = data_table::CellRenderer::Duration;
cs.duration_warn_ms = 1000.0f;
cs.duration_error_ms = 5000.0f;
}
// col 4: priority — ColorScale
{
auto& cs = t.column_specs[4];
cs.renderer = data_table::CellRenderer::ColorScale;
cs.range_min = 0.0;
cs.range_max = 4.0;
cs.range_alpha = 0.30f;
// stops: low (green) → medium (amber) → high/critical (red)
cs.range_stops = {
{ 0.0f, "#22c55e" },
{ 0.5f, "#f59e0b" },
{ 1.0f, "#ef4444" },
};
}
}
// Aplica specs para panel RIGHT — degradado según versión seleccionada
static void apply_downgraded_specs(data_table::TableInput& t, SimVersion ver) {
t.column_specs.resize(k_cols);
for (int i = 0; i < k_cols; i++) t.column_specs[i].id = k_headers[i];
switch (ver) {
case SimVersion::V21:
// Igual que full (no debería llegar aquí, pero por completitud)
apply_full_specs(t);
break;
case SimVersion::V20:
// v2.0.0: full renderers disponibles, mismo resultado visual.
// La diferencia real es interna (split refactor en el módulo).
// Simulamos: idéntico a v2.1.
apply_status_chip(t.column_specs[2]);
{
auto& cs = t.column_specs[3];
cs.renderer = data_table::CellRenderer::Duration;
cs.duration_warn_ms = 1000.0f;
cs.duration_error_ms = 5000.0f;
}
{
auto& cs = t.column_specs[4];
cs.renderer = data_table::CellRenderer::ColorScale;
cs.range_min = 0.0;
cs.range_max = 4.0;
cs.range_alpha = 0.30f;
cs.range_stops = {
{ 0.0f, "#22c55e" },
{ 0.5f, "#f59e0b" },
{ 1.0f, "#ef4444" },
};
}
break;
case SimVersion::V15:
// v1.5.0: CategoricalChip no disponible → fallback Badge en status.
// Duration + ColorScale sí disponibles.
apply_status_badge(t.column_specs[2]);
{
auto& cs = t.column_specs[3];
cs.renderer = data_table::CellRenderer::Duration;
cs.duration_warn_ms = 1000.0f;
cs.duration_error_ms = 5000.0f;
}
{
auto& cs = t.column_specs[4];
cs.renderer = data_table::CellRenderer::ColorScale;
cs.range_min = 0.0;
cs.range_max = 4.0;
cs.range_alpha = 0.30f;
cs.range_stops = {
{ 0.0f, "#22c55e" },
{ 0.5f, "#f59e0b" },
{ 1.0f, "#ef4444" },
};
}
break;
case SimVersion::V14:
// v1.4.0: ni CategoricalChip, ni ColorScale → Badge básico en status,
// Duration degradada (Badge de rango), priority Text plano.
apply_status_badge(t.column_specs[2]);
// Duration: sin renderer específico → solo Text
t.column_specs[3].renderer = data_table::CellRenderer::Text;
// priority: Text plano
t.column_specs[4].renderer = data_table::CellRenderer::Text;
break;
}
}
} // anon
// ---------------------------------------------------------------------------
// render_compat — entry point
// ---------------------------------------------------------------------------
void render_compat() {
// ---- Cabecera de controles ----
ImGui::TextDisabled("Active module: data_table v2.1.0");
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::SetNextItemWidth(360.0f);
ImGui::Combo("Compare with version", &g_selected_version,
k_version_labels, IM_ARRAYSIZE(k_version_labels));
// Nota informativa sobre la simulación
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::TextDisabled("(educational simulation — real compat requires module flag, TBD)");
ImGui::Spacing();
// Botón TBD
ImGui::BeginDisabled(true);
ImGui::Button("Take screenshots both");
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::TextDisabled("TBD: requires capture API integration");
ImGui::Separator();
// ---- Labels de columnas ----
float col_w = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.4f, 0.9f, 0.4f, 1.0f));
ImGui::Text("v2.1.0 — full renderers (current)");
ImGui::PopStyleColor();
ImGui::SameLine(col_w + ImGui::GetStyle().ItemSpacing.x);
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.9f, 0.75f, 0.3f, 1.0f));
ImGui::Text("%s", k_version_labels[g_selected_version]);
ImGui::PopStyleColor();
ImGui::Spacing();
// ---- Side-by-side layout ----
SimVersion sim_ver = static_cast<SimVersion>(g_selected_version);
float avail_h = ImGui::GetContentRegionAvail().y;
if (ImGui::BeginTable("##compat_layout", 2,
ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableSetupColumn("##col_left", ImGuiTableColumnFlags_None, 1.0f);
ImGui::TableSetupColumn("##col_right", ImGuiTableColumnFlags_None, 1.0f);
ImGui::TableNextRow();
// ---------- Panel izquierdo: v2.1 full ----------
ImGui::TableSetColumnIndex(0);
if (ImGui::BeginChild("##compat_left", ImVec2(-1.0f, avail_h - 4.0f), false)) {
data_table::TableInput tbl_left = make_base_table("compat_left");
apply_full_specs(tbl_left);
g_left.last_events.clear();
data_table::render("##compat_left_tbl", { tbl_left }, g_left.dt,
&g_left.last_events, false);
qa::consume_events(g_left.last_events);
}
ImGui::EndChild();
// ---------- Panel derecho: versión degradada ----------
ImGui::TableSetColumnIndex(1);
if (ImGui::BeginChild("##compat_right", ImVec2(-1.0f, avail_h - 4.0f), false)) {
data_table::TableInput tbl_right = make_base_table("compat_right");
apply_downgraded_specs(tbl_right, sim_ver);
g_right.last_events.clear();
data_table::render("##compat_right_tbl", { tbl_right }, g_right.dt,
&g_right.last_events, false);
qa::consume_events(g_right.last_events);
}
ImGui::EndChild();
ImGui::EndTable();
}
}
} // namespace tables_qa::tabs