chore: auto-commit (11 archivos)
- CMakeLists.txt - app.md - main.cpp - panels.cpp - appicon.ico - autoextract_panel.cpp - picker_state.cpp - picker_state.h - py_subprocess.cpp - py_subprocess.h - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+133
@@ -20,6 +20,8 @@
|
||||
#include "local_api.h"
|
||||
#include "cdp_http.h"
|
||||
#include "session_state.h"
|
||||
#include "picker_state.h"
|
||||
#include "py_subprocess.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
@@ -27,6 +29,7 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
@@ -532,6 +535,56 @@ void render_tab_detail_panel(bool* p_open) {
|
||||
ImGui::Text("Browser :%d", port);
|
||||
ImGui::Text("Tab id %s", sel_id.c_str());
|
||||
ImGui::TextWrapped("WS %s", sel_ws.c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
// --- Pick element ---
|
||||
bool active = picker_is_active();
|
||||
if (active) ImGui::PushStyleColor(ImGuiCol_Button, fn_tokens::colors::primary);
|
||||
if (ImGui::Button(active ? (TI_FLASK " Picking... (click to stop)")
|
||||
: (TI_FLASK " Pick element"))) {
|
||||
if (active) {
|
||||
picker_stop();
|
||||
} else {
|
||||
std::string err = picker_start(port, sel_id, sel_ws);
|
||||
if (!err.empty()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::error);
|
||||
ImGui::TextWrapped("Pick error: %s", err.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (active) ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("(injects functions/browser/cdp_pick_element_js.js via CDP)");
|
||||
|
||||
PickedElement last = picker_last();
|
||||
if (last.valid) {
|
||||
ImGui::Separator();
|
||||
ImGui::TextDisabled("Last picked:");
|
||||
if (ImGui::BeginChild("##picked_card", ImVec2(0, 110), true)) {
|
||||
ImGui::Text("tag: %s", last.tag.c_str());
|
||||
ImGui::TextWrapped("selector: %s", last.selector.c_str());
|
||||
ImGui::TextWrapped("xpath: %s", last.xpath.c_str());
|
||||
std::string short_text = last.text;
|
||||
if (short_text.size() > 200) short_text = short_text.substr(0, 200) + "...";
|
||||
ImGui::TextWrapped("text: %s", short_text.c_str());
|
||||
}
|
||||
ImGui::EndChild();
|
||||
if (ImGui::SmallButton("Copy selector")) {
|
||||
ImGui::SetClipboardText(last.selector.c_str());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Save to recipe (new)")) {
|
||||
// Placeholder: futura integracion para crear recipe nueva con un
|
||||
// unico field a partir del selector. Por ahora se copia.
|
||||
ImGui::SetClipboardText(last.selector.c_str());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Clear")) picker_clear_last();
|
||||
} else {
|
||||
ImGui::TextDisabled("(no picked element yet — click 'Pick element' and click on the page)");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped(
|
||||
"Tab Detail (HTML preview + screenshot + Runtime.evaluate REPL) llega "
|
||||
@@ -744,8 +797,88 @@ void draw_request_detail(const NetworkRequest& r, NetworkSession* net) {
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Response")) {
|
||||
// Detect JSON response (content-type: application/json).
|
||||
bool is_json = false;
|
||||
for (const auto& h : r.response_headers) {
|
||||
std::string n = h.name; std::transform(n.begin(), n.end(), n.begin(), ::tolower);
|
||||
if (n == "content-type" && h.value.find("application/json") != std::string::npos) {
|
||||
is_json = true; break;
|
||||
}
|
||||
}
|
||||
if (r.body_fetched && !r.body_text.empty()) {
|
||||
if (ImGui::SmallButton("Copy")) copy_to_clipboard(r.body_text);
|
||||
if (is_json) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(TI_LIST_DETAILS " Parse")) {
|
||||
// Llama infer_json_rows_schema via subprocess.
|
||||
static std::string g_parsed; // sticky entre frames
|
||||
g_parsed.clear();
|
||||
const char* code = R"PY(
|
||||
import sys, os, json, traceback
|
||||
root = os.environ.get('FN_REGISTRY_ROOT', '')
|
||||
if not root:
|
||||
print(json.dumps({"error":"FN_REGISTRY_ROOT not set"})); sys.exit(2)
|
||||
for sub in ('core',):
|
||||
sys.path.insert(0, os.path.join(root, 'python', 'functions', sub))
|
||||
try:
|
||||
from infer_json_rows_schema import infer_json_rows_schema
|
||||
body = sys.stdin.read()
|
||||
obj = json.loads(body)
|
||||
res = infer_json_rows_schema(obj)
|
||||
print(json.dumps(res if isinstance(res, dict) else {"result": res}))
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e), "trace": traceback.format_exc()})); sys.exit(1)
|
||||
)PY";
|
||||
std::vector<std::string> argv;
|
||||
argv.push_back(py_resolve_interpreter());
|
||||
argv.push_back("-c");
|
||||
argv.push_back(code);
|
||||
// Lanza un thread y deja log en g_net_ui.* via clipboard (simple).
|
||||
std::string body = r.body_text;
|
||||
std::thread([argv, body]() {
|
||||
(void)argv; (void)body;
|
||||
// py_run no soporta stdin todavia; usamos un archivo temporal.
|
||||
// Para mantener el patch minimo: escribimos body a archivo temp,
|
||||
// y pasamos su path como argv extra; el script lo lee.
|
||||
char tmp[256];
|
||||
std::snprintf(tmp, sizeof(tmp), "%s%snav_body_%lld.json",
|
||||
#ifdef _WIN32
|
||||
getenv("TEMP") ? getenv("TEMP") : ".", "\\",
|
||||
#else
|
||||
"/tmp", "/",
|
||||
#endif
|
||||
(long long)std::time(nullptr));
|
||||
{
|
||||
std::ofstream f(tmp, std::ios::binary);
|
||||
if (f) f.write(body.data(), body.size());
|
||||
}
|
||||
const char* code2 = R"PY(
|
||||
import sys, os, json, traceback
|
||||
root = os.environ.get('FN_REGISTRY_ROOT', '')
|
||||
if not root:
|
||||
print(json.dumps({"error":"FN_REGISTRY_ROOT not set"})); sys.exit(2)
|
||||
for sub in ('core',):
|
||||
sys.path.insert(0, os.path.join(root, 'python', 'functions', sub))
|
||||
try:
|
||||
from infer_json_rows_schema import infer_json_rows_schema
|
||||
with open(sys.argv[1], 'rb') as f: body = f.read().decode('utf-8','replace')
|
||||
obj = json.loads(body)
|
||||
res = infer_json_rows_schema(obj)
|
||||
print(json.dumps(res if isinstance(res, dict) else {"result": res}))
|
||||
except Exception as e:
|
||||
print(json.dumps({"error": str(e), "trace": traceback.format_exc()})); sys.exit(1)
|
||||
)PY";
|
||||
std::vector<std::string> a2 = {
|
||||
py_resolve_interpreter(), "-c", code2, tmp
|
||||
};
|
||||
PyResult pr = py_run(a2, 30000);
|
||||
ImGui::SetClipboardText(pr.stdout_data.c_str());
|
||||
std::remove(tmp);
|
||||
}).detach();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("(result -> clipboard)");
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::InputTextMultiline("##body", (char*)r.body_text.c_str(), r.body_text.size() + 1,
|
||||
ImVec2(-1, -1), ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
Reference in New Issue
Block a user