chore: auto-commit (4 archivos)
- app.md - appicon.ico - autoextract_panel.cpp - recipes_panel.cpp Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,21 +2,10 @@
|
||||
name: navegator_dashboard
|
||||
lang: cpp
|
||||
domain: tools
|
||||
version: 0.1.0
|
||||
description: "Cuadro de mandos para gestionar instancias Chrome con remote debugging. Lista navegadores corriendo (visibles + headless), permite lanzar/matar perfiles, inspeccionar pestañas, ejecutar JS, ver peticiones de red. Puente WSL→Windows que centraliza el control que hoy hacemos por scripts dispersos."
|
||||
tags: [imgui, browser, cdp, dashboard, windows, navegator, auto-extract, recipes, picker]
|
||||
uses_functions:
|
||||
- data_table_cpp_viz
|
||||
- viz_render_cpp_viz
|
||||
- compute_stage_cpp_core
|
||||
- compute_pipeline_cpp_core
|
||||
- tql_emit_cpp_core
|
||||
- tql_apply_cpp_core
|
||||
- lua_engine_cpp_core
|
||||
- join_tables_cpp_core
|
||||
- auto_detect_type_cpp_core
|
||||
- compute_column_stats_cpp_core
|
||||
- llm_anthropic_cpp_core
|
||||
- tql_to_sql_cpp_core
|
||||
- claude_cli_prompt_py_infra
|
||||
- cdp_get_ax_tree_py_pipelines
|
||||
- llm_propose_scraping_schema_py_infra
|
||||
@@ -132,3 +121,13 @@ Casos de uso:
|
||||
- v1: CDP HTTP/WS in-process + Tabs + Tab Detail + Network panel.
|
||||
- v2: HTTP API local + integracion con `cdp-cli` (cdp-cli puede delegar al dashboard si esta vivo).
|
||||
- v3: streaming live de pestañas (CDP `Page.startScreencast`) — arquitectura ya prevista en issue 0038.
|
||||
|
||||
|
||||
## Capability growth log
|
||||
|
||||
Una linea por bump SemVer. Bump-type segun `.claude/commands/version.md`:
|
||||
- `major`: breaking observable (CLI args, schema BBDD propia, formato wire).
|
||||
- `minor`: feature aditiva (nuevo panel, endpoint, opcion).
|
||||
- `patch`: bugfix sin cambio observable.
|
||||
|
||||
- v0.1.0 (2026-05-18) — baseline.
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 17 KiB |
@@ -525,6 +525,7 @@ void render_autoextract_panel(bool* p_open) {
|
||||
sc_copy = g_ax.schema;
|
||||
}
|
||||
|
||||
// LAYOUT-TABLE — schema editor form con InputText/Checkbox editables inline; keep BeginTable inline.
|
||||
if (ImGui::BeginTable("##ax_schema", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("field");
|
||||
ImGui::TableSetupColumn("selector");
|
||||
|
||||
+80
-36
@@ -10,6 +10,8 @@
|
||||
#include "imgui.h"
|
||||
#include "core/icons_tabler.h"
|
||||
#include "core/tokens.h"
|
||||
#include "data_table/data_table.h"
|
||||
#include "core/data_table_types.h"
|
||||
|
||||
#include "py_subprocess.h"
|
||||
#include "session_state.h"
|
||||
@@ -235,52 +237,94 @@ void render_recipes_panel(bool* p_open) {
|
||||
if (rows_copy.empty()) {
|
||||
ImGui::TextDisabled("No recipes in projects/navegator/profiles/default/recipes/.");
|
||||
ImGui::TextDisabled("Use AutoExtract panel to create one.");
|
||||
} else if (ImGui::BeginTable("##recipes_tbl", 6,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("name");
|
||||
ImGui::TableSetupColumn("url_pattern");
|
||||
ImGui::TableSetupColumn("last_status");
|
||||
ImGui::TableSetupColumn("last_at");
|
||||
ImGui::TableSetupColumn("rows");
|
||||
ImGui::TableSetupColumn("actions");
|
||||
ImGui::TableHeadersRow();
|
||||
} else {
|
||||
// Tabla de recetas — migrado a data_table::render (issue 0107g, Patron B).
|
||||
// Las acciones Run/Edit/Delete/Open se mapean a columnas Button con action_id.
|
||||
// ev.row indexa rows_copy directamente para recuperar yaml_path.
|
||||
static data_table::State g_st_recipes;
|
||||
static std::vector<std::string> g_back_recipes;
|
||||
static std::vector<const char*> g_ptrs_recipes;
|
||||
|
||||
for (size_t i = 0; i < rows_copy.size(); ++i) {
|
||||
const RecipeRow& r = rows_copy[i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID((int)i);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(r.name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s", r.url_pattern.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(r.last_run_status.empty() ? "-" : r.last_run_status.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(r.last_run_at.empty() ? "-" : r.last_run_at.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", r.rows_last_run);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::SmallButton("Run")) run_recipe_async(r.yaml_path);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Edit")) {
|
||||
g_back_recipes.clear();
|
||||
for (const auto& r : rows_copy) {
|
||||
g_back_recipes.push_back(r.name);
|
||||
g_back_recipes.push_back(r.url_pattern);
|
||||
g_back_recipes.push_back(r.last_run_status.empty() ? "-" : r.last_run_status);
|
||||
g_back_recipes.push_back(r.last_run_at.empty() ? "-" : r.last_run_at);
|
||||
g_back_recipes.push_back(std::to_string(r.rows_last_run));
|
||||
g_back_recipes.push_back("Run"); // col 5: accion run
|
||||
g_back_recipes.push_back("Edit"); // col 6: accion edit
|
||||
g_back_recipes.push_back("Delete"); // col 7: accion delete
|
||||
g_back_recipes.push_back("Open"); // col 8: accion open_df
|
||||
}
|
||||
g_ptrs_recipes.clear();
|
||||
for (const auto& s : g_back_recipes) g_ptrs_recipes.push_back(s.c_str());
|
||||
|
||||
data_table::TableInput tbl;
|
||||
tbl.name = "recipes_tbl";
|
||||
tbl.headers = {"name", "url_pattern", "last_status", "last_at", "rows",
|
||||
"run", "edit", "delete", "open"};
|
||||
tbl.types = {
|
||||
data_table::ColumnType::String, data_table::ColumnType::String,
|
||||
data_table::ColumnType::String, data_table::ColumnType::String,
|
||||
data_table::ColumnType::Int,
|
||||
data_table::ColumnType::String, data_table::ColumnType::String,
|
||||
data_table::ColumnType::String, data_table::ColumnType::String,
|
||||
};
|
||||
tbl.cells = g_ptrs_recipes.data();
|
||||
tbl.rows = (int)rows_copy.size();
|
||||
tbl.cols = 9;
|
||||
|
||||
tbl.column_specs.resize(tbl.cols);
|
||||
for (int i = 0; i < tbl.cols; i++) tbl.column_specs[i].id = tbl.headers[i];
|
||||
// last_status → CategoricalChip
|
||||
tbl.column_specs[2].renderer = data_table::CellRenderer::CategoricalChip;
|
||||
tbl.column_specs[2].chips = {
|
||||
{"ok", "#22c55e"}, {"success", "#22c55e"},
|
||||
{"error", "#ef4444"}, {"failed", "#ef4444"},
|
||||
{"-", "#a3a3a3"},
|
||||
};
|
||||
// Botones de accion
|
||||
tbl.column_specs[5].renderer = data_table::CellRenderer::Button;
|
||||
tbl.column_specs[5].button_action = "run_recipe";
|
||||
tbl.column_specs[5].button_label = "Run";
|
||||
tbl.column_specs[5].button_color_hex = "#3b82f6";
|
||||
tbl.column_specs[6].renderer = data_table::CellRenderer::Button;
|
||||
tbl.column_specs[6].button_action = "edit_recipe";
|
||||
tbl.column_specs[6].button_label = "Edit";
|
||||
tbl.column_specs[7].renderer = data_table::CellRenderer::Button;
|
||||
tbl.column_specs[7].button_action = "delete_recipe";
|
||||
tbl.column_specs[7].button_label = "Delete";
|
||||
tbl.column_specs[7].button_color_hex = "#ef4444";
|
||||
tbl.column_specs[8].renderer = data_table::CellRenderer::Button;
|
||||
tbl.column_specs[8].button_action = "open_df";
|
||||
tbl.column_specs[8].button_label = "Open";
|
||||
|
||||
std::vector<data_table::TableEvent> rec_events;
|
||||
ImGui::BeginChild("##recipes_tbl_host", ImVec2(-1, 300));
|
||||
data_table::render("##recipes_dt", {tbl}, g_st_recipes, &rec_events);
|
||||
ImGui::EndChild();
|
||||
|
||||
for (const auto& ev : rec_events) {
|
||||
if (ev.kind != data_table::TableEventKind::ButtonClick) continue;
|
||||
if (ev.row < 0 || ev.row >= (int)rows_copy.size()) continue;
|
||||
const RecipeRow& r = rows_copy[ev.row];
|
||||
if (ev.action_id == "run_recipe") {
|
||||
run_recipe_async(r.yaml_path);
|
||||
} else if (ev.action_id == "edit_recipe") {
|
||||
std::string body = slurp(r.yaml_path);
|
||||
std::lock_guard<std::mutex> lk(g_rs.mu);
|
||||
g_rs.editing_idx = (int)i;
|
||||
g_rs.editing_idx = ev.row;
|
||||
g_rs.edit_buf = body;
|
||||
std::snprintf(g_rs.edit_textarea, sizeof(g_rs.edit_textarea),
|
||||
"%s", body.c_str());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Delete")) delete_recipe(r.yaml_path);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Open in data_factory")) {
|
||||
// placeholder — solo loguea
|
||||
} else if (ev.action_id == "delete_recipe") {
|
||||
delete_recipe(r.yaml_path);
|
||||
} else if (ev.action_id == "open_df") {
|
||||
std::lock_guard<std::mutex> lk(g_rs.mu);
|
||||
g_rs.status = "open in data_factory: " + r.name + " (not wired)";
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (editing_idx >= 0 && editing_idx < (int)rows_copy.size()) {
|
||||
|
||||
Reference in New Issue
Block a user