From a11b634ba7f0239192dbb74652b8c55ece6b224b Mon Sep 17 00:00:00 2001 From: deexsed Date: Fri, 27 Mar 2026 14:08:51 +0300 Subject: [PATCH] =?UTF-8?q?feat(tray):=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20=D1=81=D1=82=D0=B0=D1=82=D1=83=D1=81=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=BA=D1=81=D0=B8=20=D0=B2=20GUI=20=D0=B8=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 ++++++-- linux.py | 1 + ui/ctk_theme.py | 2 ++ ui/ctk_tray_ui.py | 66 ++++++++++++++++++++++++++++++++++++++- ui/tray_icons.py | 6 ---- utils/tray_proxy_state.py | 12 +++---- windows.py | 1 + 7 files changed, 84 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 47b7c3f..0cf6955 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,20 @@ Telegram Desktop → SOCKS5 (127.0.0.1:1080) → TG WS Proxy → WSS → Telegra **Меню трея:** - **Открыть в Telegram** — автоматически настроить прокси через `tg://socks` ссылку +- **Статус и проверка TCP…** — диалог с фазой прокси (запуск / слушает / ошибка), uptime и локальной проверкой TCP до `host:port` - **Перезапустить прокси** — перезапуск без выхода из приложения -- **Настройки...** — GUI-редактор конфигурации (в т.ч. версия приложения, опциональная проверка обновлений с GitHub) +- **Настройки...** — GUI-редактор конфигурации (см. ниже) - **Открыть логи** — открыть файл логов - **Выход** — остановить прокси и закрыть приложение +**Иконка в трее:** цветной индикатор на иконке — **зелёный** прокси слушает порт, **красный** — ошибка, **жёлтый** — запуск / остановка / ожидание. Подсказка при наведении: адрес, состояние и при работе — uptime. + +Повторный запуск приложения, пока оно уже работает, блокируется (один экземпляр); при конфликте порта показывается сообщение об ошибке. + При первом запуске после старта может появиться запрос об открытии страницы релиза, если на GitHub вышла новая версия (отключается в настройках). +**Окно настроек (Windows / Linux):** секция **«Статус»** — цветной индикатор, текст состояния и uptime (обновляется автоматически). Полная проверка TCP и детали — в меню трея **«Статус и проверка TCP…»**. В блоке **«Обновления»** — время последней проверки GitHub; без интернета имеет смысл отключить **«Проверять обновления при запуске»**. + ### macOS Перейдите на [страницу релизов](https://github.com/Flowseal/tg-ws-proxy/releases) и скачайте **`TgWsProxy_macos_universal.dmg`** — универсальная сборка для Apple Silicon и Intel. @@ -54,6 +61,8 @@ Telegram Desktop → SOCKS5 (127.0.0.1:1080) → TG WS Proxy → WSS → Telegra 2. Перенести **TG WS Proxy.app** в папку **Applications** 3. При первом запуске macOS может попросить подтвердить открытие: **Системные настройки → Конфиденциальность и безопасность → Всё равно открыть** +В строке меню доступны те же действия, что и в трее Windows/Linux (в т.ч. статус и проверка TCP); иконка в меню обновляется с цветным индикатором состояния. Настройки на macOS задаются через системные диалоги, не через окно CustomTkinter. + ### Linux Для Debian/Ubuntu скачайте со [страницы релизов](https://github.com/Flowseal/tg-ws-proxy/releases) пакет **`TgWsProxy_linux_amd64.deb`**. @@ -158,7 +167,7 @@ tg-ws-proxy-tray-linux = "linux:main" ## Структура исходников (tray) -Общая логика tray-приложений (пути данных, один экземпляр процесса, конфиг, логирование, поток с прокси, IPv6, фоновая проверка релизов) находится в `utils/tray_*.py`; вспомогательные части UI — в `ui/tray_*.py`. Точки входа по ОС: `windows.py`, `linux.py`, `macos.py`. +Общая логика tray-приложений (пути данных, один экземпляр процесса, конфиг, логирование, поток с прокси, состояние для UI, локальная диагностика порта/TCP, IPv6, фоновая проверка релизов) находится в `utils/tray_*.py`; вспомогательные части UI — в `ui/tray_*.py` и `ui/ctk_tray_ui.py`. Точки входа по ОС: `windows.py`, `linux.py`, `macos.py`. Для согласованных окончаний строк в репозитории используются `.editorconfig` и `.gitattributes` (LF). diff --git a/linux.py b/linux.py index 342e9e7..c39617a 100644 --- a/linux.py +++ b/linux.py @@ -275,6 +275,7 @@ def _edit_config_dialog(): cfg, DEFAULT_CONFIG, show_autostart=False, + proxy_state=_proxy_state, ) def on_save(): diff --git a/ui/ctk_theme.py b/ui/ctk_theme.py index a09fb1d..3f65fed 100644 --- a/ui/ctk_theme.py +++ b/ui/ctk_theme.py @@ -50,6 +50,8 @@ class CtkTheme: field_border: str = "#d6d9dc" text_primary: str = "#000000" text_secondary: str = "#707579" + status_pill_bg: str = "#f4f6fa" + status_pill_border: str = "#e2e6ed" ui_font_family: str = "Sans" mono_font_family: str = "Monospace" diff --git a/ui/ctk_tray_ui.py b/ui/ctk_tray_ui.py index 1945ef0..0019685 100644 --- a/ui/ctk_tray_ui.py +++ b/ui/ctk_tray_ui.py @@ -12,6 +12,8 @@ from typing import Any, Callable, Dict, List, Optional, Tuple, Union import proxy.tg_ws_proxy as tg_ws_proxy from proxy import __version__ +from ui.tray_icons import badge_rgb_for_phase +from utils.tray_proxy_state import format_uptime_short, phase_label_ru from utils.update_check import RELEASES_PAGE_URL, get_status from ui.ctk_theme import ( @@ -138,6 +140,7 @@ def install_tray_config_form( *, show_autostart: bool = False, autostart_value: bool = False, + proxy_state: Optional[Any] = None, ) -> TrayConfigFormWidgets: """Поля настроек прокси внутри уже созданного `frame`.""" header = ctk.CTkFrame(frame, fg_color="transparent") @@ -216,6 +219,67 @@ def install_tray_config_form( port_entry.pack(anchor="w") attach_tooltip_to_widgets([port_lbl, port_entry, port_col], _TIP_PORT) + if proxy_state is not None: + stat_inner = _config_section(ctk, frame, theme, "Статус") + pill = ctk.CTkFrame( + stat_inner, + fg_color=theme.status_pill_bg, + corner_radius=12, + border_width=1, + border_color=theme.status_pill_border, + ) + pill.pack(fill="x", pady=(2, 0)) + row = ctk.CTkFrame(pill, fg_color="transparent") + row.pack(fill="x", padx=14, pady=12) + dot = ctk.CTkFrame( + row, + width=12, + height=12, + corner_radius=6, + fg_color="#eab308", + ) + dot.pack(side="left", padx=(0, 12)) + dot.pack_propagate(False) + text_col = ctk.CTkFrame(row, fg_color="transparent") + text_col.pack(side="left", fill="x", expand=True) + status_main = ctk.CTkLabel( + text_col, + text="", + font=(theme.ui_font_family, 14), + text_color=theme.text_primary, + anchor="w", + ) + status_main.pack(side="left") + status_uptime = ctk.CTkLabel( + text_col, + text="", + font=(theme.ui_font_family, 13), + text_color=theme.text_secondary, + anchor="w", + ) + status_uptime.pack(side="left") + + def _mini_status_tick() -> None: + if not frame.winfo_exists(): + return + try: + snap = proxy_state.snapshot() + phase = snap["phase"] + r, g, b = badge_rgb_for_phase(phase) + dot.configure(fg_color=f"#{r:02x}{g:02x}{b:02x}") + status_main.configure(text=phase_label_ru(phase)) + ls = snap.get("listening_since") + if phase == "listening" and ls is not None: + status_uptime.configure(text=f" · {format_uptime_short(ls)}") + else: + status_uptime.configure(text="") + except Exception: + pass + if frame.winfo_exists(): + frame.after(1000, _mini_status_tick) + + _mini_status_tick() + dc_inner = _config_section(ctk, frame, theme, "Датацентры Telegram (DC → IP)") dc_lbl = ctk.CTkLabel( dc_inner, @@ -591,7 +655,7 @@ def populate_first_run_window( "порт занят — другой процесс или старый экземпляр; смените порт в настройках;", "IPv6 — трафик может идти мимо прокси; см. предупреждение при первом запуске;", "брандмауэр / антивирус — разрешите локальное прослушивание;", - "меню трея: «Статус» — без поиска по логам.", + "меню трея: «Статус и проверка TCP…» — без поиска по логам.", ): ctk.CTkLabel( frame, diff --git a/ui/tray_icons.py b/ui/tray_icons.py index 6337bc5..dba37b5 100644 --- a/ui/tray_icons.py +++ b/ui/tray_icons.py @@ -6,12 +6,6 @@ from typing import Any, List, Tuple from PIL import Image, ImageDraw, ImageFont -# Подсказка для tooltip (согласована с цветами бейджа) -BADGE_TOOLTIP_HINT = ( - "Бейдж: зелёный — работает, красный — ошибка, жёлтый — запуск/ожидание/остановка" -) - - def _resample_lanczos() -> int: try: return Image.Resampling.LANCZOS # type: ignore[attr-defined] diff --git a/utils/tray_proxy_state.py b/utils/tray_proxy_state.py index 1ac2ad6..f2703ef 100644 --- a/utils/tray_proxy_state.py +++ b/utils/tray_proxy_state.py @@ -90,8 +90,6 @@ def build_tray_tooltip( port: int, state: ProxyRuntimeState, ) -> str: - from ui.tray_icons import BADGE_TOOLTIP_HINT - snap = state.snapshot() phase = snap["phase"] addr = f"{host}:{port}" @@ -99,12 +97,10 @@ def build_tray_tooltip( if phase == "listening" and snap["listening_since"] is not None: up = format_uptime_short(snap["listening_since"]) - base = f"TG WS Proxy | {addr} | {label} | {up}" - elif phase == "error" and snap["detail"]: + return f"TG WS Proxy | {addr} | {label} | {up}" + if phase == "error" and snap["detail"]: short = snap["detail"] if len(short) > 80: short = short[:77] + "…" - base = f"TG WS Proxy | {addr} | {label}: {short}" - else: - base = f"TG WS Proxy | {addr} | {label}" - return f"{base}\n{BADGE_TOOLTIP_HINT}" + return f"TG WS Proxy | {addr} | {label}: {short}" + return f"TG WS Proxy | {addr} | {label}" diff --git a/windows.py b/windows.py index 525e677..cf87b71 100644 --- a/windows.py +++ b/windows.py @@ -394,6 +394,7 @@ def _edit_config_dialog(): DEFAULT_CONFIG, show_autostart=_supports_autostart(), autostart_value=cfg.get("autostart", False), + proxy_state=_proxy_state, ) def on_save():