merge: issue/0032 — sql_workbench

# Conflicts:
#	cpp/apps/primitives_gallery/CMakeLists.txt
#	cpp/apps/primitives_gallery/demos.h
#	cpp/apps/primitives_gallery/main.cpp
This commit is contained in:
2026-04-25 21:55:17 +02:00
8 changed files with 708 additions and 0 deletions
@@ -15,10 +15,13 @@ add_imgui_app(primitives_gallery
${CMAKE_SOURCE_DIR}/functions/core/tween_curves.cpp
${CMAKE_SOURCE_DIR}/functions/core/bezier_editor.cpp
${CMAKE_SOURCE_DIR}/functions/core/timeline.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
@@ -76,6 +79,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()
+1
View File
@@ -24,6 +24,7 @@ void demo_process_runner();
void demo_tween(); // issue 0031
void demo_bezier_editor(); // issue 0031
void demo_timeline(); // issue 0031
void demo_sql_workbench(); // issue 0032
// --- Viz ---
void demo_bar_chart();
+129
View File
@@ -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 <imgui.h>
#include <sqlite3.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
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
+1
View File
@@ -52,6 +52,7 @@ static const DemoEntry k_demos[] = {
{"tween", "tween_curves", "Core", &gallery::demo_tween},
{"bezier_editor", "bezier_editor", "Core", &gallery::demo_bezier_editor},
{"timeline", "timeline", "Core", &gallery::demo_timeline},
{"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},