183 lines
9.7 KiB
Markdown
183 lines
9.7 KiB
Markdown
# 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("<Control-a>", 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 по двойному нажатию) |
|