--- name: terminal_panel kind: component lang: cpp domain: viz version: "1.0.0" purity: impure signature: "void fn_term::open(fn_term::TerminalPanel& panel); void fn_term::render(fn_term::TerminalPanel& panel); void fn_term::send(fn_term::TerminalPanel& panel, const std::string& text); void fn_term::close(fn_term::TerminalPanel& panel);" description: "Emulador TTY embebible en ImGui. Arranca un proceso hijo via PTY (Linux: forkpty) o ConPTY (Windows 10 v1809+), renderiza el scrollback con colores ANSI 16-color, toolbar (clear/copy/reset/scroll-lock) e input box. Scrollback circular configurable. Soporte readonly para tail-only." tags: [terminal, pty, conpty, imgui, viz, ansi, shell, cpp-dashboard-viz] uses_functions: [ansi_parser_cpp_core, logger_cpp_core] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: [atomic, functional, mutex, string, thread, vector] tested: true tests: - "smoke: spawn echo hello and exit, scrollback contains hello" test_file_path: "cpp/tests/test_terminal_panel_smoke.cpp" file_path: "cpp/functions/viz/terminal_panel/terminal_panel.cpp" framework: imgui params: - name: panel desc: "Struct TerminalPanel con config (shell, cwd, env, scrollback_lines, readonly) y estado interno gestionado por open/close/render" output: "render() dibuja toolbar + scrollback con colores ANSI + input box en el area ImGui disponible. open() arranca el proceso hijo y el reader thread. send() escribe texto al stdin del hijo. close() mata el proceso y libera recursos." notes: "Linux: requiere -lutil (libutil) para forkpty. Windows: requiere Windows SDK >= 17763 (v1809) para ConPTY. Si el SDK es anterior, open() loguea error y deja is_open()==false. Anti-scope v1: sin tabs multiples, sin SSH, sin curses pesados (vim/htop)." --- # terminal_panel Emulador TTY embebible en ImGui. Util para: tail de logs en una app de monitoring, ejecutar comandos shell desde un panel de kanban, ver output de compilaciones, consola de debug de agentes. ## Ejemplo ```cpp #include "viz/terminal_panel/terminal_panel.h" static fn_term::TerminalPanel s_term; void render_panel() { // Abrir al primer frame. if (!s_term.is_open()) { s_term.shell = "/bin/bash"; s_term.scrollback_lines = 2000; fn_term::open(s_term); } fn_term::render(s_term); } // Tail readonly de un log: static fn_term::TerminalPanel s_log_tail; void render_log_tail() { if (!s_log_tail.is_open()) { s_log_tail.shell = "/bin/bash"; s_log_tail.readonly = true; fn_term::open(s_log_tail); fn_term::send(s_log_tail, "tail -f /tmp/agent.log\n"); } fn_term::render(s_log_tail); } ``` ## Cuando usarla Cuando necesitas ver output crudo de un proceso (shell, compilacion, curl, tail) sin salir de la app ImGui. Alternativa a abrir un terminal externo. Especialmente util en apps de monitoring (services_monitor, agents_dashboard) y kanban panels de build. ## Gotchas - **Linux**: el CMakeLists del consumidor debe linkar `-lutil` (o `target_link_libraries(... util)`) para resolver `forkpty`. - **Windows**: requiere Windows 10 v1809+ (SDK >= 17763). Si el SDK es anterior, `open()` deja el panel cerrado y loguea error — no hay panic ni crash. - **Anti-scope v1**: sin soporte de curses pesados (vim, htop, top). El parser ANSI maneja SGR color + cursor básico; programas que usen el modo altscreen o muchas secuencias de cursor se verán mal. - **Scrollback circular**: cuando `lines.size() > scrollback_lines`, se elimina la primera fila. Esto puede causar saltos visuales si el contenido se está acumulando muy rápido (ej. `yes "x"`). En v1 el target es 60fps con scrollback de 5000 líneas. - **Thread safety**: `render()` toma el `buf_mutex` por el tiempo del render de cada frame. El reader thread también lo toma al actualizar el buffer. En condiciones normales no hay contención significativa. - **readonly**: si `true`, no se renderiza el input box y `send()` es no-op. Útil para `tail -f` o procesos que no necesitan stdin.