#pragma once // ----------------------------------------------------------------------------- // Терминал в редакторе: работать над проектом, не выходя из него. // // ЧТО ЭТО НЕ ТАКОЕ. Это не эмулятор терминала. Полноценный VT100 (альтернативный // экран, адресация курсора, рамки из псевдографики) нужен ровно одному сорту // программ — интерактивным TUI, и `claude` без ключей именно такой. Написать // его поверх raylib можно, но результат всё равно рассыпался бы на первом же // символе рамки: встроенный шрифт их не знает. // // Поэтому здесь СТРОЧНЫЙ вывод, и это не упрощение, а выбор в пользу того, // как с моделью работают из редактора на самом деле: // // * `claude -p` и `codex exec` печатают обычный текст — им эмулятор не нужен; // * инструменты Tile2D (--tool) печатают таблицы и карты — тоже текст; // * git, cmake, прогоны игры — текст. // // Полный интерактивный CLI открывается одной кнопкой во ВНЕШНЕМ терминале, в // том же рабочем каталоге. Так у каждого способа остаётся то, в чём он хорош. // // Строка ввода работает в трёх режимах (Tab): команда оболочки, вопрос Claude, // вопрос Codex. Одно поле, а не три разных — потому что работа выглядит как // чередование: спросил, посмотрел, проверил инструментом, снова спросил. // ----------------------------------------------------------------------------- #include #include #include struct ScreenRect; enum class AskMode : uint8_t { SHELL = 0, // команда оболочки как есть CLAUDE, // claude -p "..." CODEX, // codex exec "..." COUNT, }; const char* AskModeName(AskMode m); struct Terminal { bool open = false; // Скроллбек. Строки, а не поток байт: их листают, ищут глазами и копируют // в отчёт, и все три действия требуют границ строк. std::vector lines; std::string input; std::vector history; int histPos = -1; // -1 — набирается новая строка, не из истории int scroll = 0; // на сколько строк отмотано вверх от конца AskMode mode = AskMode::SHELL; // Где выполняются команды. Корень репозитория, а не каталог проекта: // работа идёт над всем — код движка, содержимое, документация. std::string workDir; // Какой проект открыт в редакторе. Уходит детям переменной окружения // TILE2D_PROJECT, поэтому `--tool validate` без ключей проверяет ИМЕННО // его — и то же самое видит агент, запущенный отсюда. std::string projectName; void Init(const std::string& dir, const std::string& project = {}); void Shutdown(); bool Running() const; // Ввод и кнопки. true — терминал забрал ввод себе, редактору его не // отдавать: иначе набранная в строке `w` встанет кистью стены на холст. bool Update(int windowW, int windowH, float dt); void Draw(int windowW, int windowH) const; // Забрать то, что процесс успел напечатать. Звать каждый кадр: вывод // обязан идти потоком, а не появляться целиком по завершении. void Poll(); void Submit(); // выполнить строку ввода по режиму void RunShell(const std::string& cmd); // выполнить команду напрямую (UTF-8) // То же, но строка пришла из argv. Аргументы командной строки приходят в // системной кодировке, а всё остальное здесь — UTF-8: без перевода русская // команда из --term отображалась бы вопросами. void RunShellFromArgv(const char* cmd); void Stop(); // прервать текущий процесс void Print(const std::string& s); // своя строка в скроллбек void LaunchExternal(); // интерактивный CLI отдельным окном // Занимаемая часть окна снизу, в пикселях ОКНА. Редактор спрашивает её, // чтобы не рисовать и не ловить мышь под панелью. int PanelTop(int windowH) const; };