# Terminal Audit — Plan & Status > Аудит терминала ServerManager для полной совместимости с TUI-программами > (mc, vim, htop, nano, less, claude, opencode и др.) > Цель: уровень MobaXterm / PuTTY --- ## КРИТИЧНО (без этого TUI не работают) ### 1. [DONE v1.5.3] 256-color / truecolor рендеринг - **Файл:** `gui/widgets/terminal_widget.py` — `_color_tag()`, `_make_tags()` - pyte возвращает hex-строки (`"ff0000"`) для 256/truecolor - Старый код распознавал только 16 именованных ANSI-цветов → все остальные игнорировались - **Решение:** динамическое создание тегов tkinter для любого hex-цвета с кешем (лимит 4096) ### 2. [DONE v1.5.3] Reverse video + strikethrough - **Файл:** `gui/widgets/terminal_widget.py` — `_render()`, `_make_tags()` - `char_data.reverse` и `char_data.strikethrough` не извлекались из буфера - mc/vim/htop активно используют reverse для выделения/меню - **Решение:** добавлены в кортеж атрибутов, reverse swap fg/bg в `_make_tags` ### 3. [DONE v1.5.3] Alternate screen buffer (smcup/rmcup) - **Файл:** `gui/widgets/terminal_widget.py` — класс `_Screen` - pyte 0.8.x не реализует alternate buffer (private modes 47, 1047, 1049) - Все TUI-программы используют alternate screen → при выходе экран был мусором - **Решение:** подкласс `_Screen(pyte.Screen)` с `_enter_alt()` / `_exit_alt()`, сохраняет/восстанавливает buffer + cursor ### 4. [DONE v1.5.3] write_process_input → SSH bridge - **Файл:** `gui/widgets/terminal_widget.py` — `_Screen.write_process_input()` - pyte вызывает `write_process_input()` для DA/DSR ответов (Device Attributes, Cursor Position Report) - Без этого программы зависали ожидая ответа от терминала - **Решение:** переопределён метод → `_write_cb` → `_send()` → SSH ### 5. [DONE v1.5.3] Alt+key комбинации - **Файл:** `gui/widgets/terminal_widget.py` — `_on_key()` - Alt+1/2 для переключения панелей mc, Alt+B/F для readline, Alt+key для nano - Не обрабатывались вообще - **Решение:** проверка `event.state & 0x20000` (Alt на Windows), отправка `ESC + key` ### 6. [DONE v1.5.3] Shift+Arrow / Ctrl+Arrow (модификаторы) - **Файл:** `gui/widgets/terminal_widget.py` — `_on_key()`, `_xterm_modifier()` - Ctrl+Left/Right для навигации по словам, Shift+Arrow для выделения - Не было поддержки модификаторов → отправлялись обычные стрелки - **Решение:** xterm modifier encoding `ESC[1;{mod}A`, функция `_xterm_modifier()` ### 7. [DONE v1.5.3] Ctrl+A блокировался - **Файл:** `gui/widgets/terminal_widget.py` - `bind("", lambda e: "break")` съедал Ctrl+A - Ctrl+A = tmux prefix, readline home, select all в nano - **Решение:** убран отдельный bind, Ctrl+A проходит через общий ctrl-handler → `\x01` ### 8. [DONE v1.5.2] DECCKM (Application Cursor Keys) - **Файл:** `gui/widgets/terminal_widget.py` - mc/vim/htop включают `?1h` (DECCKM) → стрелки должны слать `ESC O A` вместо `ESC [ A` - **Решение:** проверка `_DECCKM in self._screen.mode`, словарь `_APP_CURSOR_MAP` --- ## ВАЖНО (для стабильной работы) ### 9. [DONE v1.5.3] Shift+Tab (backtab) - **Файл:** `gui/widgets/terminal_widget.py` — `_on_key()` - `ESC[Z` — обратная табуляция, нужна в mc и для reverse completion - **Решение:** обработка `ISO_Left_Tab` и `Tab + Shift` ### 10. [DONE v1.5.3] Bracketed paste mode - **Файл:** `gui/widgets/terminal_widget.py` — `_on_ctrl_v()` - Без обёртки `ESC[200~`...`ESC[201~` вставка в vim/nano добавляла auto-indent мусор - **Решение:** проверка `_BRACKETED_PASTE in screen.mode`, оборачивание при вставке ### 11. [DONE v1.5.3] Cursor hidden (DECTCEM) - **Файл:** `gui/widgets/terminal_widget.py` — `_render()` - htop/vim скрывают курсор при перерисовке → курсор-блок рисовался зря - **Решение:** `if is_cursor_row and not cursor_hidden` ### 12. [DONE v1.5.3] Incremental UTF-8 decoder - **Файл:** `gui/widgets/terminal_widget.py` — `feed()` - SSH мог разрезать multi-byte UTF-8 → mojibake - **Решение:** `codecs.getincrementaldecoder("utf-8")("replace")` ### 13. [DONE v1.5.3] Mouse tracking - **Файл:** `gui/widgets/terminal_widget.py` — `_on_mouse_*` - Клики в mc панелях, htop элементах, vim mouse mode - Поддержка: basic (1000), button-event (1002), any-event (1003), SGR (1006) ### 14. [DONE v1.5.3] pyte `dirty` set для оптимизации - **Файл:** `gui/widgets/terminal_widget.py` — `_render()` - Старый код сравнивал каждую строку с prev_buffer - **Решение:** используем `screen.dirty` для пропуска чистых строк ### 15. [DONE v1.5.3] Thread safety: session races - **Файл:** `gui/tabs/terminal_tab.py` - `_session` читалась из main thread и писалась из SSH thread - **Решение:** все записи `_session` через `self.after(0, ...)`, локальные ref в `_send_to_shell` ### 16. [DONE v1.5.3] `_on_disconnected` из неправильного потока - **Файл:** `gui/tabs/terminal_tab.py` - Вызывался из SSH read thread, менял `_session = None` - **Решение:** вся логика обёрнута в `self.after(0, _handle)` ### 17. [DONE v1.5.3] Ctrl+special chars - **Файл:** `gui/widgets/terminal_widget.py` — `_on_key()` - Ctrl+[ → ESC, Ctrl+\\ → `\x1c`, Ctrl+] → `\x1d`, Ctrl+Space → NUL - Не обрабатывались ### 18. [DONE v1.5.4] Smart Ctrl+C - Одинарный Ctrl+C с выделением → копирует - Одинарный Ctrl+C без выделения → SIGINT - Двойной Ctrl+C (<300мс) → всегда SIGINT - Защита от случайного убийства программы ### 19. [DONE v1.5.3] Mousewheel smooth scroll - В alternate screen → 3 стрелки вместо PageUp/Down - С mouse tracking → mouse wheel events (btn 64/65) --- ## ЖЕЛАТЕЛЬНО (полировка) ### 20. [ ] Wide characters (CJK) - **Файл:** `gui/widgets/terminal_widget.py` — `_render()` - CJK символы занимают 2 колонки, pyte ставит stub во вторую - Без обработки → смещение колонок после wide char - **Решение:** проверять `wcwidth()`, пропускать stub-ячейки - **Приоритет:** низкий (актуально для CJK-пользователей) ### 21. [ ] Scrollback buffer - **Файл:** `gui/widgets/terminal_widget.py` - Нет истории: вывод выше экрана потерян - **Решение:** использовать `pyte.HistoryScreen` как базу для `_Screen` - Требует рендеринг scrollback + mousewheel навигация - **Приоритет:** средний (удобство для обычных команд) ### 22. [DONE v1.5.3] Numpad keys - **Файл:** `gui/widgets/terminal_widget.py` — `_on_key()` - KP_0..9, KP_Add, KP_Subtract и др. ### 23. [DONE v1.5.3] Resize debounce 200ms → 100ms - **Файл:** `gui/widgets/terminal_widget.py` — `_on_configure()` ### 24. [DONE v1.5.3] Send errors → disconnect - **Файл:** `core/ssh_client.py` — `send()` - При OSError вызывается `on_disconnect` вместо тихого проглатывания ### 25. [DONE v1.5.3] Right-click context menu - Copy / Paste / Select All - Как в MobaXterm / Windows Terminal ### 26. [DONE v1.5.3] Double-click word select ### 27. [ ] Ctrl+Shift+C/V стиль Windows Terminal - [DONE] Ctrl+Shift+C → копирование - [DONE] Ctrl+Shift+V → вставка - [ ] Возможно добавить визуальный feedback при копировании (flash) ### 28. [ ] TERM variable alignment - Shell открывается с `term="xterm-256color"`, но pyte не поддерживает все xterm features - Пока OK, но если будут проблемы — можно сменить на `"xterm"` ### 29. [ ] Font fallbacks для Unicode - Consolas покрывает box-drawing, но не все Unicode блоки - Tkinter не поддерживает font fallback stacks - **Приоритет:** низкий --- ## Статистика | Статус | Количество | |--------|-----------| | DONE | 26 | | TODO | 3 | ## Версии релизов | Версия | Изменения | |--------|-----------| | v1.5.1 | Убран LNM, дебаунсинг рендера, батчинг данных, recv 64KB | | v1.5.2 | DECCKM, thread-safe queue | | v1.5.3 | Полная переработка: alt buffer, 256/truecolor, reverse, mouse, DA/DSR, Alt+key, modifiers, bracketed paste, UTF-8, dirty optimization, context menu | | v1.5.4 | Smart Ctrl+C (copy/SIGINT по двойному нажатию) |