From 4d53ee63800938674d67295499e870625ff4e90e Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sat, 25 Apr 2026 21:49:09 +0200 Subject: [PATCH] feat(primitives_gallery): demo de sql_workbench contra registry.db readonly Demo nuevo en demos_sql.cpp: abre registry.db en SQLITE_OPEN_READONLY (resolviendo via FN_REGISTRY_ROOT o cwd ascendente), monta fn::SqlWorkbenchState con readonly=true y query inicial sobre la tabla functions. Wire-up: entry en k_demos[] tras process_runner; declaracion en demos.h; sources sql_workbench.cpp + demos_sql.cpp + link SQLite::SQLite3 en CMakeLists.txt. Co-Authored-By: Claude Opus 4.7 (1M context) --- cpp/apps/primitives_gallery/CMakeLists.txt | 7 ++ cpp/apps/primitives_gallery/demos.h | 1 + cpp/apps/primitives_gallery/demos_sql.cpp | 129 +++++++++++++++++++++ cpp/apps/primitives_gallery/main.cpp | 1 + 4 files changed, 138 insertions(+) create mode 100644 cpp/apps/primitives_gallery/demos_sql.cpp diff --git a/cpp/apps/primitives_gallery/CMakeLists.txt b/cpp/apps/primitives_gallery/CMakeLists.txt index 6812bf9f..805a7feb 100644 --- a/cpp/apps/primitives_gallery/CMakeLists.txt +++ b/cpp/apps/primitives_gallery/CMakeLists.txt @@ -8,10 +8,13 @@ add_imgui_app(primitives_gallery demos_text_editor.cpp demos_gl_texture.cpp demos_extras.cpp + demos_sql.cpp # text_editor + file_watcher (issue 0025) ${CMAKE_SOURCE_DIR}/functions/core/text_editor.cpp ${CMAKE_SOURCE_DIR}/functions/core/file_watcher.cpp ${CMAKE_SOURCE_DIR}/vendor/imgui_text_edit/TextEditor.cpp + # sql_workbench (issue 0032) + ${CMAKE_SOURCE_DIR}/functions/core/sql_workbench.cpp # Core primitives demoed (tokens vive en fn_framework) ${CMAKE_SOURCE_DIR}/functions/core/fullscreen_window.cpp ${CMAKE_SOURCE_DIR}/functions/core/page_header.cpp @@ -61,6 +64,10 @@ target_include_directories(primitives_gallery PRIVATE ${CMAKE_SOURCE_DIR}/vendor/stb ) +# SQLite (sql_workbench) — alias provisto por cpp/CMakeLists.txt: +# system on Linux, vendored amalgamation on Windows cross-compile. +target_link_libraries(primitives_gallery PRIVATE SQLite::SQLite3) + if(WIN32) target_link_libraries(primitives_gallery PRIVATE opengl32) endif() diff --git a/cpp/apps/primitives_gallery/demos.h b/cpp/apps/primitives_gallery/demos.h index b7f22c32..ff527479 100644 --- a/cpp/apps/primitives_gallery/demos.h +++ b/cpp/apps/primitives_gallery/demos.h @@ -21,6 +21,7 @@ void demo_dashboard_panel(); void demo_text_editor(); // wave 1, issue 0025 void demo_file_watcher(); // wave 1, issue 0025 void demo_process_runner(); +void demo_sql_workbench(); // issue 0032 // --- Viz --- void demo_bar_chart(); diff --git a/cpp/apps/primitives_gallery/demos_sql.cpp b/cpp/apps/primitives_gallery/demos_sql.cpp new file mode 100644 index 00000000..01dc4a3e --- /dev/null +++ b/cpp/apps/primitives_gallery/demos_sql.cpp @@ -0,0 +1,129 @@ +// Demo de sql_workbench (Core, issue 0032). +// +// Abre `registry.db` en modo readonly y deja que el componente liste sus +// tablas en la sidebar. La idea es probar el ciclo Run + tabla + historial +// contra una DB real sin riesgo de mutarla. + +#include "demos.h" +#include "demo.h" + +#include "core/sql_workbench.h" +#include "core/tokens.h" + +#include +#include + +#include +#include +#include +#include + +namespace gallery { + +namespace { + +struct SqlDemoState { + sqlite3* db = nullptr; + fn::SqlWorkbenchState wb; + bool tried_open = false; + std::string db_path; + std::string open_error; +}; + +SqlDemoState& state() { + static SqlDemoState s; + return s; +} + +// Resuelve la ruta a registry.db: env FN_REGISTRY_ROOT/registry.db si existe, +// si no, prueba ./registry.db, ../registry.db, ../../registry.db (build tree). +std::string resolve_registry_db() { + if (const char* env = std::getenv("FN_REGISTRY_ROOT")) { + std::string p = std::string(env) + "/registry.db"; + if (FILE* f = std::fopen(p.c_str(), "rb")) { std::fclose(f); return p; } + } + const char* candidates[] = { + "registry.db", + "../registry.db", + "../../registry.db", + "../../../registry.db", + "../../../../registry.db", + }; + for (const char* c : candidates) { + if (FILE* f = std::fopen(c, "rb")) { std::fclose(f); return c; } + } + return ""; +} + +void ensure_open() { + auto& s = state(); + if (s.tried_open) return; + s.tried_open = true; + + s.db_path = resolve_registry_db(); + if (s.db_path.empty()) { + s.open_error = "registry.db not found (tried FN_REGISTRY_ROOT and parent dirs)"; + return; + } + int rc = sqlite3_open_v2(s.db_path.c_str(), &s.db, + SQLITE_OPEN_READONLY, nullptr); + if (rc != SQLITE_OK) { + s.open_error = sqlite3_errmsg(s.db); + if (s.db) { sqlite3_close(s.db); s.db = nullptr; } + return; + } + s.wb.readonly = true; + // Query inicial mas util para el demo: lista de funciones del registry. + s.wb.query = + "SELECT id, kind, purity, domain\n" + "FROM functions\n" + "ORDER BY id\n" + "LIMIT 50;"; +} + +} // namespace + +void demo_sql_workbench() { + using namespace fn_tokens; + + demo_header("sql_workbench", "v1.0.0", + "Workbench SQL: editor con highlighting, schema sidebar, tabla de " + "resultados e historial. Ejecuta queries contra una sqlite3* del caller. " + "En este demo, registry.db abierto en modo readonly."); + + ensure_open(); + auto& s = state(); + + if (!s.open_error.empty()) { + ImGui::PushStyleColor(ImGuiCol_Text, colors::error); + ImGui::TextWrapped("could not open registry.db: %s", s.open_error.c_str()); + ImGui::PopStyleColor(); + ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted); + ImGui::TextWrapped("Set FN_REGISTRY_ROOT to the repo root or run from the repo cwd."); + ImGui::PopStyleColor(); + return; + } + + ImGui::PushStyleColor(ImGuiCol_Text, colors::text_dim); + ImGui::Text("db: %s (readonly)", s.db_path.c_str()); + ImGui::PopStyleColor(); + + section("workbench"); + { + ImVec2 avail = ImGui::GetContentRegionAvail(); + // Reserva un pelin para el code_block de abajo. + float h = avail.y - 110.0f; + if (h < 320.0f) h = 320.0f; + fn::sql_workbench("##gallery_sql", s.db, s.wb, ImVec2(-1, h)); + } + + code_block( + "sqlite3* db = nullptr;\n" + "sqlite3_open_v2(\"registry.db\", &db, SQLITE_OPEN_READONLY, nullptr);\n" + "fn::SqlWorkbenchState st;\n" + "st.readonly = true;\n" + "fn::sql_workbench(\"##sql\", db, st, ImVec2(-1, -1));" + ); +} + +} // namespace gallery diff --git a/cpp/apps/primitives_gallery/main.cpp b/cpp/apps/primitives_gallery/main.cpp index 269bfe6b..22cf0b9b 100644 --- a/cpp/apps/primitives_gallery/main.cpp +++ b/cpp/apps/primitives_gallery/main.cpp @@ -49,6 +49,7 @@ static const DemoEntry k_demos[] = { {"text_editor", "text_editor", "Core", &gallery::demo_text_editor}, // wave 1 {"file_watcher", "file_watcher", "Core", &gallery::demo_file_watcher}, // wave 1 {"process_runner", "process_runner", "Core", &gallery::demo_process_runner}, + {"sql_workbench", "sql_workbench", "Core", &gallery::demo_sql_workbench}, // issue 0032 // Viz {"bar_chart", "bar_chart", "Viz", &gallery::demo_bar_chart}, {"pie_chart", "pie_chart", "Viz", &gallery::demo_pie_chart},