// odr_console — lanzador GUI de funciones del registry para recolectar datos online. // MVP: panel launcher + placeholders. Ver issue 0066. #include "app_base.h" #include "imgui.h" #include "core/app_menubar.h" #include "core/app_about.h" #include "core/app_settings.h" #include "core/icon_font.h" #include "core/icons_tabler.h" #include "core/tokens.h" #include "core/logger.h" #include "data_registry.h" #include #include #include #include #include static OdrRegistry g_registry; static std::string g_db_path; static char g_search_buf[256] = ""; static std::vector g_results; static int g_selected = -1; static void do_search() { g_results.clear(); g_selected = -1; if (g_search_buf[0] == '\0') { registry_list_recent(g_registry, 50, g_results); } else { registry_search(g_registry, g_search_buf, 50, g_results); } } static bool g_show_launcher = true; static bool g_show_jobs = true; static bool g_show_datasets = true; static void draw_launcher() { if (!g_show_launcher) return; if (!ImGui::Begin(TI_SEARCH " Launcher", &g_show_launcher)) { ImGui::End(); return; } ImGui::PushItemWidth(-1); if (ImGui::InputTextWithHint("##search", "Search functions/pipelines (FTS5)...", g_search_buf, sizeof(g_search_buf), ImGuiInputTextFlags_EnterReturnsTrue)) { do_search(); } ImGui::PopItemWidth(); if (ImGui::Button("Search")) do_search(); ImGui::SameLine(); ImGui::TextDisabled("%zu hits", g_results.size()); ImGui::Separator(); if (ImGui::BeginTable("##results", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable)) { ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("Kind", ImGuiTableColumnFlags_WidthFixed, 80); ImGui::TableSetupColumn("Domain", ImGuiTableColumnFlags_WidthFixed, 100); ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthStretch); ImGui::TableHeadersRow(); for (int i = 0; i < (int)g_results.size(); ++i) { const auto& r = g_results[i]; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); bool sel = (i == g_selected); if (ImGui::Selectable(r.id.c_str(), sel, ImGuiSelectableFlags_SpanAllColumns)) { g_selected = i; } ImGui::TableSetColumnIndex(1); ImGui::TextUnformatted(r.kind.c_str()); ImGui::TableSetColumnIndex(2); ImGui::TextUnformatted(r.domain.c_str()); ImGui::TableSetColumnIndex(3); ImGui::TextUnformatted(r.description.c_str()); } ImGui::EndTable(); } if (g_selected >= 0 && g_selected < (int)g_results.size()) { ImGui::Separator(); const auto& r = g_results[g_selected]; ImGui::Text("Selected: %s", r.id.c_str()); ImGui::TextWrapped("Signature: %s", r.signature.c_str()); ImGui::Spacing(); ImGui::BeginDisabled(true); ImGui::Button(TI_PLAYER_PLAY " Run"); ImGui::EndDisabled(); ImGui::SameLine(); ImGui::TextDisabled("(jobs system pending — issue 0065)"); } ImGui::End(); } static void draw_jobs() { if (!g_show_jobs) return; if (!ImGui::Begin(TI_LIST " Jobs", &g_show_jobs)) { ImGui::End(); return; } ImGui::TextDisabled("Jobs queue panel — pendiente issue 0065"); ImGui::TextWrapped( "Cuando jobs_pool_cpp_core este extraido del graph_explorer al " "registry, este panel mostrara cola/running/done con live progress. " "Ver dev/issues/0067-odr-osint-prereqs-roadmap.md"); ImGui::End(); } static void draw_datasets() { if (!g_show_datasets) return; if (!ImGui::Begin(TI_DATABASE " Datasets", &g_show_datasets)) { ImGui::End(); return; } ImGui::TextDisabled("DuckDB browser — pendiente fase 2 del MVP"); ImGui::End(); } static void render() { static fn_ui::PanelToggle panels[] = { { "Launcher", nullptr, &g_show_launcher }, { "Jobs", nullptr, &g_show_jobs }, { "Datasets", nullptr, &g_show_datasets }, }; fn_ui::app_menubar(panels, sizeof(panels) / sizeof(panels[0]), nullptr); draw_launcher(); draw_jobs(); draw_datasets(); } int main(int argc, char** argv) { // CLI: opcional, primer arg = path a registry.db (override). if (argc >= 2) { g_db_path = argv[1]; } if (g_db_path.empty()) { const char* env = std::getenv("FN_REGISTRY_DB"); if (env && *env) g_db_path = env; } if (g_db_path.empty()) { const char* root = std::getenv("FN_REGISTRY_ROOT"); if (root && *root) { g_db_path = std::string(root) + "/registry.db"; } } if (g_db_path.empty()) { std::fprintf(stderr, "[odr_console] No registry.db path. Pass as arg or set " "FN_REGISTRY_DB / FN_REGISTRY_ROOT.\n"); } else if (!registry_open(g_registry, g_db_path)) { std::fprintf(stderr, "[odr_console] Failed to open registry.db: %s\n", g_db_path.c_str()); } else { registry_list_recent(g_registry, 50, g_results); } fn::AppConfig cfg; cfg.title = "odr_console — online data recopilation"; cfg.width = 1400; cfg.height = 900; cfg.about = { "odr_console", "0.1.0", "Lanzador GUI de funciones del registry para recolectar " "datos online (APIs, scraping, browser CDP). MVP." }; cfg.log = { "odr_console.log", 1 }; int rc = fn::run_app(cfg, render); registry_close(g_registry); return rc; }