docs(flows): DoD obligatorio con user-facing surface + abrir issues 0100-0103 (taxonomia, frontmatter migration, dev_console, work dashboard)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+5
-5
@@ -33,10 +33,10 @@ target_link_libraries(navegator_dashboard PRIVATE
|
|||||||
imgui_node_editor
|
imgui_node_editor
|
||||||
)
|
)
|
||||||
|
|
||||||
# fn_table_viz: provides data_table::render(), viz_render, TQL engine, Lua, LLM.
|
# fn_module_data_table: provides data_table::render(), viz_render, TQL engine, Lua, LLM.
|
||||||
# Guard keeps the app compilable in builds where vendor/lua is absent.
|
# Guard keeps the app compilable in builds where vendor/lua is absent.
|
||||||
if(TARGET fn_table_viz)
|
if(TARGET fn_module_data_table)
|
||||||
target_link_libraries(navegator_dashboard PRIVATE fn_table_viz)
|
target_link_libraries(navegator_dashboard PRIVATE fn_module_data_table)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set_target_properties(navegator_dashboard PROPERTIES WIN32_EXECUTABLE TRUE)
|
set_target_properties(navegator_dashboard PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||||
@@ -65,8 +65,8 @@ if(FN_BUILD_TESTS)
|
|||||||
ws2_32
|
ws2_32
|
||||||
imgui_node_editor
|
imgui_node_editor
|
||||||
)
|
)
|
||||||
if(TARGET fn_table_viz)
|
if(TARGET fn_module_data_table)
|
||||||
target_link_libraries(navegator_dashboard_tests PRIVATE fn_table_viz)
|
target_link_libraries(navegator_dashboard_tests PRIVATE fn_module_data_table)
|
||||||
endif()
|
endif()
|
||||||
# Excluye int main() de main.cpp; el harness define su propio main().
|
# Excluye int main() de main.cpp; el harness define su propio main().
|
||||||
target_compile_definitions(navegator_dashboard_tests PRIVATE FN_TEST_BUILD)
|
target_compile_definitions(navegator_dashboard_tests PRIVATE FN_TEST_BUILD)
|
||||||
|
|||||||
@@ -26,10 +26,14 @@ uses_functions:
|
|||||||
- infer_json_rows_schema_py_core
|
- infer_json_rows_schema_py_core
|
||||||
- cdp_pick_element_js_js_browser
|
- cdp_pick_element_js_js_browser
|
||||||
uses_types: []
|
uses_types: []
|
||||||
|
uses_modules: [data_table_cpp]
|
||||||
framework: "imgui"
|
framework: "imgui"
|
||||||
entry_point: "main.cpp"
|
entry_point: "main.cpp"
|
||||||
dir_path: "projects/navegator/apps/navegator_dashboard"
|
dir_path: "projects/navegator/apps/navegator_dashboard"
|
||||||
repo_url: ""
|
repo_url: ""
|
||||||
|
icon:
|
||||||
|
phosphor: "compass"
|
||||||
|
accent: "#2563eb"
|
||||||
|
|
||||||
e2e_checks:
|
e2e_checks:
|
||||||
- id: build_windows
|
- id: build_windows
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "session_state.h"
|
#include "session_state.h"
|
||||||
#include "py_subprocess.h"
|
#include "py_subprocess.h"
|
||||||
#include "picker_state.h"
|
#include "picker_state.h"
|
||||||
|
#include "app_base.h"
|
||||||
|
|
||||||
#include "crude_json.h"
|
#include "crude_json.h"
|
||||||
|
|
||||||
@@ -123,6 +124,9 @@ try:
|
|||||||
out = {"tab_id": tab_id}
|
out = {"tab_id": tab_id}
|
||||||
if isinstance(schema, dict):
|
if isinstance(schema, dict):
|
||||||
out.update(schema)
|
out.update(schema)
|
||||||
|
# llm_propose_scraping_schema returns "schema" key; remap to "fields" for parser.
|
||||||
|
if "schema" in schema and "fields" not in schema:
|
||||||
|
out["fields"] = schema["schema"]
|
||||||
else:
|
else:
|
||||||
out["fields"] = schema
|
out["fields"] = schema
|
||||||
print(json.dumps(out))
|
print(json.dumps(out))
|
||||||
@@ -141,6 +145,16 @@ except Exception as e:
|
|||||||
std::lock_guard<std::mutex> lk(g_ax.mu);
|
std::lock_guard<std::mutex> lk(g_ax.mu);
|
||||||
g_ax.raw_python_output = r.stdout_data;
|
g_ax.raw_python_output = r.stdout_data;
|
||||||
}
|
}
|
||||||
|
// Debug dump: stdout + diagnostic context to disk for offline inspection.
|
||||||
|
{
|
||||||
|
FILE* f = std::fopen(fn::local_path("autoextract_last.txt"), "wb");
|
||||||
|
if (f) {
|
||||||
|
std::fprintf(f, "EXIT=%d ERR=%s\n", r.exit_code, r.error.c_str());
|
||||||
|
std::fprintf(f, "STDOUT_LEN=%zu\n--- STDOUT ---\n", r.stdout_data.size());
|
||||||
|
std::fwrite(r.stdout_data.data(), 1, r.stdout_data.size(), f);
|
||||||
|
std::fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (r.exit_code != 0 || r.stdout_data.empty()) {
|
if (r.exit_code != 0 || r.stdout_data.empty()) {
|
||||||
std::lock_guard<std::mutex> lk(g_ax.mu);
|
std::lock_guard<std::mutex> lk(g_ax.mu);
|
||||||
g_ax.last_error = r.error.empty() ? "python exited non-zero" : r.error;
|
g_ax.last_error = r.error.empty() ? "python exited non-zero" : r.error;
|
||||||
|
|||||||
+29
-4
@@ -13,7 +13,7 @@
|
|||||||
#include "core/icons_tabler.h"
|
#include "core/icons_tabler.h"
|
||||||
#include "core/tokens.h"
|
#include "core/tokens.h"
|
||||||
#include "core/data_table_types.h"
|
#include "core/data_table_types.h"
|
||||||
#include "viz/data_table.h"
|
#include "data_table/data_table.h"
|
||||||
|
|
||||||
#include "chrome_scanner.h"
|
#include "chrome_scanner.h"
|
||||||
#include "chrome_launcher.h"
|
#include "chrome_launcher.h"
|
||||||
@@ -230,7 +230,14 @@ void render_browsers_panel(bool* p_open) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
data_table::render("##dt_browsers", {tbl}, g_browsers.dt_state, false);
|
std::vector<data_table::TableEvent> dt_events;
|
||||||
|
data_table::render("##dt_browsers", {tbl}, g_browsers.dt_state, &dt_events, /*show_chrome=*/false);
|
||||||
|
for (auto& ev : dt_events) {
|
||||||
|
if (ev.kind == data_table::TableEventKind::RowDoubleClick &&
|
||||||
|
ev.row >= 0 && ev.row < static_cast<int>(g_browsers.instances.size())) {
|
||||||
|
g_session().select_browser(g_browsers.instances[ev.row].port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Actions: inline button list per row (no BeginTable — Button renderer not Fase-1) ---
|
// --- Actions: inline button list per row (no BeginTable — Button renderer not Fase-1) ---
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
@@ -445,7 +452,17 @@ void render_tabs_panel(bool* p_open) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
data_table::render("##dt_tabs", {tbl}, g_tabs_ui.dt_state, false);
|
std::vector<data_table::TableEvent> dt_events;
|
||||||
|
data_table::render("##dt_tabs", {tbl}, g_tabs_ui.dt_state, &dt_events, /*show_chrome=*/false);
|
||||||
|
for (auto& ev : dt_events) {
|
||||||
|
if (ev.kind == data_table::TableEventKind::RowDoubleClick &&
|
||||||
|
ev.row >= 0 && ev.row < static_cast<int>(visible_tabs.size())) {
|
||||||
|
const CdpTab* tp = visible_tabs[ev.row];
|
||||||
|
if (tp && !tp->ws_url.empty()) {
|
||||||
|
g_session().select_tab(tp->id, tp->ws_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Actions: inline button list per visible row (no BeginTable — Button renderer not Fase-1) ---
|
// --- Actions: inline button list per visible row (no BeginTable — Button renderer not Fase-1) ---
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
@@ -1301,7 +1318,15 @@ void render_network_panel(bool* p_open) {
|
|||||||
cs.duration_error_ms = 5000.0f;
|
cs.duration_error_ms = 5000.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
data_table::render("##dt_requests", {req_tbl}, g_net_ui.dt_state, true);
|
std::vector<data_table::TableEvent> dt_events;
|
||||||
|
data_table::render("##dt_requests", {req_tbl}, g_net_ui.dt_state, &dt_events, /*show_chrome=*/true);
|
||||||
|
for (auto& ev : dt_events) {
|
||||||
|
if (ev.kind == data_table::TableEventKind::RowDoubleClick &&
|
||||||
|
ev.row >= 0 && ev.row < static_cast<int>(filtered.size())) {
|
||||||
|
g_net_ui.selected_id = filtered[ev.row]->id;
|
||||||
|
g_net_ui.selected_index = ev.row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Context menu + selection: track clicked row by matching Name col
|
// Context menu + selection: track clicked row by matching Name col
|
||||||
// (handled inside data_table via row-click; URL copy available via right-click
|
// (handled inside data_table via row-click; URL copy available via right-click
|
||||||
|
|||||||
+31
-2
@@ -68,6 +68,9 @@ std::string py_resolve_interpreter() {
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::string venv_py = root + "\\python\\.venv\\Scripts\\python.exe";
|
std::string venv_py = root + "\\python\\.venv\\Scripts\\python.exe";
|
||||||
if (file_exists(venv_py)) return venv_py;
|
if (file_exists(venv_py)) return venv_py;
|
||||||
|
// Windows venv no encontrado — intentar via WSL si FN_REGISTRY_ROOT_WSL existe.
|
||||||
|
std::string wsl_root = getenv_str("FN_REGISTRY_ROOT_WSL");
|
||||||
|
if (!wsl_root.empty()) return "wsl.exe"; // sentinel; py_run lo expande
|
||||||
#else
|
#else
|
||||||
std::string venv_py = root + "/python/.venv/bin/python3";
|
std::string venv_py = root + "/python/.venv/bin/python3";
|
||||||
if (file_exists(venv_py)) return venv_py;
|
if (file_exists(venv_py)) return venv_py;
|
||||||
@@ -108,10 +111,36 @@ PyResult py_run(const std::vector<std::string>& argv, int timeout_ms) {
|
|||||||
PyResult res;
|
PyResult res;
|
||||||
if (argv.empty()) { res.error = "argv empty"; return res; }
|
if (argv.empty()) { res.error = "argv empty"; return res; }
|
||||||
|
|
||||||
|
// Si argv[0] es el sentinel "wsl.exe", reescribir el comando para invocar
|
||||||
|
// el python del venv WSL con el contexto Linux correcto:
|
||||||
|
// wsl.exe --cd <linux_root> -- env FN_REGISTRY_ROOT=<linux_root> python3 -c "..." <args>
|
||||||
|
// Esto garantiza que el script Python recibe FN_REGISTRY_ROOT como path Linux,
|
||||||
|
// puede importar funciones del registry y resuelve deps del venv WSL.
|
||||||
|
std::vector<std::string> final_argv;
|
||||||
|
if (argv[0] == "wsl.exe") {
|
||||||
|
std::string wsl_root = getenv_str("FN_REGISTRY_ROOT_WSL");
|
||||||
|
if (wsl_root.empty()) {
|
||||||
|
res.error = "wsl.exe sentinel but FN_REGISTRY_ROOT_WSL not set";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
std::string python3 = wsl_root + "/python/.venv/bin/python3";
|
||||||
|
final_argv.push_back("wsl.exe");
|
||||||
|
final_argv.push_back("--cd");
|
||||||
|
final_argv.push_back(wsl_root);
|
||||||
|
final_argv.push_back("--");
|
||||||
|
final_argv.push_back("env");
|
||||||
|
final_argv.push_back("FN_REGISTRY_ROOT=" + wsl_root);
|
||||||
|
final_argv.push_back(python3);
|
||||||
|
// Append rest of original argv (skip argv[0] = "wsl.exe")
|
||||||
|
for (size_t i = 1; i < argv.size(); ++i) final_argv.push_back(argv[i]);
|
||||||
|
} else {
|
||||||
|
final_argv = argv;
|
||||||
|
}
|
||||||
|
|
||||||
std::string cmd;
|
std::string cmd;
|
||||||
for (size_t i = 0; i < argv.size(); ++i) {
|
for (size_t i = 0; i < final_argv.size(); ++i) {
|
||||||
if (i) cmd += ' ';
|
if (i) cmd += ' ';
|
||||||
cmd += quote_arg_win(argv[i]);
|
cmd += quote_arg_win(final_argv[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE r_pipe = nullptr;
|
HANDLE r_pipe = nullptr;
|
||||||
|
|||||||
+21
-7
@@ -6,8 +6,18 @@
|
|||||||
//
|
//
|
||||||
// Decisiones:
|
// Decisiones:
|
||||||
// - Heredoc inline: el script Python se pasa via -c "<inline>" para evitar archivos temporales.
|
// - Heredoc inline: el script Python se pasa via -c "<inline>" para evitar archivos temporales.
|
||||||
// - PATH: usa "python3" o "python". Fallback: ${FN_REGISTRY_ROOT}/python/.venv/Scripts/python.exe
|
// - PATH: prioridad del interprete:
|
||||||
// (Windows venv layout) o /python/.venv/bin/python3 (POSIX).
|
// Windows: (1) ${FN_REGISTRY_ROOT}\python\.venv\Scripts\python.exe si existe,
|
||||||
|
// (2) wsl.exe sentinel si FN_REGISTRY_ROOT_WSL esta seteado (invoca
|
||||||
|
// el python del venv WSL con: wsl.exe --cd <linux_root> -- env
|
||||||
|
// FN_REGISTRY_ROOT=<linux_root> python/.venv/bin/python3 ...),
|
||||||
|
// (3) "python" en PATH (sistema).
|
||||||
|
// POSIX: ${FN_REGISTRY_ROOT}/python/.venv/bin/python3, luego "python3".
|
||||||
|
// - Cuando se usa el sentinel wsl.exe, py_run reescribe el argv completo para que
|
||||||
|
// el script Python reciba FN_REGISTRY_ROOT como path Linux, pueda importar
|
||||||
|
// funciones del registry y use el venv WSL con todas las deps.
|
||||||
|
// - FN_REGISTRY_ROOT y FN_REGISTRY_ROOT_WSL se propagan via launch_cpp_app_windows
|
||||||
|
// (bash/functions/infra/launch_cpp_app_windows.sh v1.1.0).
|
||||||
// - Stdout: capturado completo. El llamante parsea JSON.
|
// - Stdout: capturado completo. El llamante parsea JSON.
|
||||||
// - Stderr: redirigido a stdout para facilitar diagnostico (logs visibles).
|
// - Stderr: redirigido a stdout para facilitar diagnostico (logs visibles).
|
||||||
// - Sin consola visible en Windows (CREATE_NO_WINDOW).
|
// - Sin consola visible en Windows (CREATE_NO_WINDOW).
|
||||||
@@ -25,11 +35,15 @@ struct PyResult {
|
|||||||
std::string error; // mensaje propio si CreateProcess/popen fallo
|
std::string error; // mensaje propio si CreateProcess/popen fallo
|
||||||
};
|
};
|
||||||
|
|
||||||
// Devuelve la ruta al interprete python a usar. Prioridad:
|
// Devuelve la ruta al interprete python a usar. Prioridad (Windows):
|
||||||
// 1. ${FN_REGISTRY_ROOT}/python/.venv/Scripts/python.exe (Windows)
|
// 1. ${FN_REGISTRY_ROOT}\python\.venv\Scripts\python.exe — venv Windows nativo
|
||||||
// 2. ${FN_REGISTRY_ROOT}/python/.venv/bin/python3 (POSIX/MinGW)
|
// 2. "wsl.exe" (sentinel) — cuando FN_REGISTRY_ROOT_WSL esta seteado.
|
||||||
// 3. "python3" en PATH
|
// py_run() lo expande a: wsl.exe --cd <linux_root> -- env FN_REGISTRY_ROOT=<linux_root>
|
||||||
// 4. "python" en PATH (Windows default)
|
// <linux_root>/python/.venv/bin/python3 ...
|
||||||
|
// 3. "python" — fallback PATH (python.exe del sistema, sin deps del registry)
|
||||||
|
// Prioridad (POSIX):
|
||||||
|
// 1. ${FN_REGISTRY_ROOT}/python/.venv/bin/python3
|
||||||
|
// 2. "python3" en PATH
|
||||||
std::string py_resolve_interpreter();
|
std::string py_resolve_interpreter();
|
||||||
|
|
||||||
// Devuelve FN_REGISTRY_ROOT. Si no esta seteada, intenta deducirla:
|
// Devuelve FN_REGISTRY_ROOT. Si no esta seteada, intenta deducirla:
|
||||||
|
|||||||
Reference in New Issue
Block a user