feat(lite): merge TUN mode with IPv6 routing

This commit is contained in:
RoyirinRosmontis
2026-09-19 03:41:41 +08:00
parent 3e29012d9c
commit 50364dc17a
23 changed files with 2996 additions and 237 deletions
+65 -27
View File
@@ -931,14 +931,16 @@ class SettingsServices:
load_icon: object
version: str
get_port: object
get_builtin_pac: object
get_system_pac: object
get_dns: object
get_update_via_proxy: object
get_skip_update_check: object
get_node_display_mode: object
get_close_behavior: object
parse_port: object
set_port: object
set_builtin_pac: object
set_system_pac: object
set_dns: object
set_update_via_proxy: object
set_skip_update_check: object
set_node_display_mode: object
@@ -961,7 +963,7 @@ class SettingsDialog(QDialog):
self._services = services
self.setWindowTitle("设置")
self.setWindowIcon(services.load_icon())
self.setFixedSize(300, 470)
self.setFixedSize(620, 460)
self.setStyleSheet(f"""
QDialog {{ background-color: {C_BG}; }}
QLabel {{ color: {C_TEXT}; font-family: "Microsoft YaHei", "Segoe UI", sans-serif; font-size: 12px; }}
@@ -999,6 +1001,23 @@ class SettingsDialog(QDialog):
layout.addWidget(self.lb_hint)
layout.addSpacing(2)
columns = QHBoxLayout()
columns.setSpacing(24)
left_col = QVBoxLayout()
right_col = QVBoxLayout()
left_col.setSpacing(9)
right_col.setSpacing(9)
left_heading = QLabel("连接与分流")
right_heading = QLabel("节点与维护")
for heading in (left_heading, right_heading):
heading.setStyleSheet(f"color: {C_TITLE}; font-size: 12px; font-weight: bold;")
heading.setAlignment(Qt.AlignCenter)
left_col.addWidget(left_heading)
right_col.addWidget(right_heading)
columns.addLayout(left_col, 1)
columns.addLayout(right_col, 1)
layout.addLayout(columns, 1)
compact_button_style = f"""
QPushButton {{ background-color: {C_CARD}; color: {C_SUBTEXT}; border: 1px solid {C_BORDER};
border-radius: 5px; padding: 1px 7px; min-width: 34px; min-height: 18px;
@@ -1019,7 +1038,7 @@ class SettingsDialog(QDialog):
self.btn_whitelist.setStyleSheet(compact_button_style)
self.btn_whitelist.clicked.connect(self._open_whitelist)
whitelist_row.addWidget(self.btn_whitelist)
layout.addLayout(whitelist_row)
left_col.addLayout(whitelist_row)
jump_row = QHBoxLayout()
jump_label = QLabel("代理跳板")
@@ -1032,7 +1051,7 @@ class SettingsDialog(QDialog):
self.btn_jump.setStyleSheet(self.btn_whitelist.styleSheet())
self.btn_jump.clicked.connect(self._open_jump)
jump_row.addWidget(self.btn_jump)
layout.addLayout(jump_row)
left_col.addLayout(jump_row)
third_party_row = QHBoxLayout()
third_party_label = QLabel("第三方订阅")
@@ -1045,7 +1064,7 @@ class SettingsDialog(QDialog):
self.btn_third_party.setStyleSheet(self.btn_whitelist.styleSheet())
self.btn_third_party.clicked.connect(self._open_third_party)
third_party_row.addWidget(self.btn_third_party)
layout.addLayout(third_party_row)
right_col.addLayout(third_party_row)
log_row = QHBoxLayout()
log_label = QLabel("日志")
@@ -1067,22 +1086,33 @@ class SettingsDialog(QDialog):
self.ed_port.setFixedSize(54, 22)
self.ed_port.setEnabled(not port_locked)
port_row.addWidget(self.ed_port)
layout.addLayout(port_row)
left_col.addLayout(port_row)
builtin_pac_row = QHBoxLayout()
builtin_pac_label = QLabel("使用内置 PAC")
system_pac_row = QHBoxLayout()
builtin_pac_label = QLabel("使用系统 PAC (系统代理)")
builtin_pac_label.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 12px;")
builtin_pac_label.setToolTip(
"系统使用固定的本地代理端口;转发器内部仍按 PAC 规则智能判断直连或代理"
"仅系统代理模式使用;关闭时使用内置 PAC 智能分流"
)
builtin_pac_row.addWidget(builtin_pac_label)
builtin_pac_row.addStretch()
self.cb_builtin_pac = ToggleSwitch(checked=services.get_builtin_pac())
self.cb_builtin_pac.setToolTip(
"系统固定指向本地代理端口,转发器内部保留 PAC 智能分流"
)
builtin_pac_row.addWidget(self.cb_builtin_pac)
layout.addLayout(builtin_pac_row)
system_pac_row.addWidget(builtin_pac_label)
system_pac_row.addStretch()
self.cb_system_pac = ToggleSwitch(checked=services.get_system_pac())
self.cb_system_pac.setToolTip("系统代理且已选 PAC 规则模式时,向 Windows 写入本地 PAC 地址;TUN 模式忽略此项")
system_pac_row.addWidget(self.cb_system_pac)
left_col.addLayout(system_pac_row)
dns_row = QHBoxLayout()
dns_label = QLabel("自定义 DNS (虚拟网卡)")
dns_label.setStyleSheet(f"color: {C_SUBTEXT}; font-size: 12px;")
dns_label.setToolTip("TUN 模式使用;支持 IPv4/IPv6,请填写 1-3 个 DNS 地址")
dns_row.addWidget(dns_label)
dns_row.addStretch()
self.ed_dns = QLineEdit(", ".join(services.get_dns()))
self.ed_dns.setPlaceholderText("8.8.8.8, 1.1.1.1")
self.ed_dns.setFixedSize(120, 22)
self.ed_dns.setEnabled(not port_locked)
dns_row.addWidget(self.ed_dns)
left_col.addLayout(dns_row)
update_row = QHBoxLayout()
update_label = QLabel("使用代理更新")
@@ -1092,7 +1122,7 @@ class SettingsDialog(QDialog):
self.cb_update_proxy = ToggleSwitch(checked=services.get_update_via_proxy())
self.cb_update_proxy.setToolTip("开启后检查更新与下载走节点隧道(CONNECT);默认直连仓库")
update_row.addWidget(self.cb_update_proxy)
layout.addLayout(update_row)
right_col.addLayout(update_row)
skip_row = QHBoxLayout()
skip_label = QLabel("跳过检查更新")
@@ -1102,7 +1132,7 @@ class SettingsDialog(QDialog):
self.cb_skip_update = ToggleSwitch(checked=services.get_skip_update_check())
self.cb_skip_update.setToolTip("跳过 PAC 数据与程序版本检查(节点更新与登录不受影响),被跳过的检查不输出日志")
skip_row.addWidget(self.cb_skip_update)
layout.addLayout(skip_row)
right_col.addLayout(skip_row)
node_display_row = QHBoxLayout()
node_display_label = QLabel("节点信息显示")
@@ -1125,7 +1155,7 @@ class SettingsDialog(QDialog):
self.btn_node_remark.setChecked(True)
else:
self.btn_node_domain.setChecked(True)
layout.addLayout(node_display_row)
right_col.addLayout(node_display_row)
close_behavior_row = QHBoxLayout()
close_behavior_label = QLabel("默认关闭行为")
@@ -1148,7 +1178,7 @@ class SettingsDialog(QDialog):
self.btn_close_tray.setChecked(True)
else:
self.btn_close_exit.setChecked(True)
layout.addLayout(close_behavior_row)
right_col.addLayout(close_behavior_row)
reset_row = QHBoxLayout()
reset_label = QLabel("重置用户配置")
@@ -1162,10 +1192,11 @@ class SettingsDialog(QDialog):
self.btn_reset.setEnabled(not port_locked)
self.btn_reset.clicked.connect(self._reset_user_config)
reset_row.addWidget(self.btn_reset)
layout.addLayout(reset_row)
right_col.addLayout(reset_row)
layout.addLayout(log_row)
layout.addStretch()
right_col.addLayout(log_row)
left_col.addStretch()
right_col.addStretch()
buttons = QHBoxLayout()
buttons.setSpacing(10)
@@ -1233,7 +1264,8 @@ class SettingsDialog(QDialog):
return
self._services.reset_user_config()
self.ed_port.setText(str(self._services.get_port()))
self.cb_builtin_pac.setChecked(self._services.get_builtin_pac())
self.cb_system_pac.setChecked(self._services.get_system_pac())
self.ed_dns.setText(", ".join(self._services.get_dns()))
self.cb_update_proxy.setChecked(self._services.get_update_via_proxy())
self.cb_skip_update.setChecked(self._services.get_skip_update_check())
self.btn_node_domain.setChecked(True)
@@ -1247,8 +1279,14 @@ class SettingsDialog(QDialog):
self.lb_hint.setText("端口应为 1-65535 的数字")
self.lb_hint.setStyleSheet(f"color: {C_RED}; font-size: 11px;")
return
try:
self._services.set_dns(self.ed_dns.text())
except ValueError as exc:
self.lb_hint.setText(str(exc))
self.lb_hint.setStyleSheet(f"color: {C_RED}; font-size: 11px;")
return
self._services.set_port(port)
self._services.set_builtin_pac(bool(self.cb_builtin_pac.isChecked()))
self._services.set_system_pac(bool(self.cb_system_pac.isChecked()))
self._services.set_update_via_proxy(bool(self.cb_update_proxy.isChecked()))
self._services.set_skip_update_check(bool(self.cb_skip_update.isChecked()))
self._services.set_node_display_mode(
+65 -1
View File
@@ -2,7 +2,23 @@
from PyQt5.QtCore import Qt, QRectF, pyqtSignal
from PyQt5.QtGui import QColor, QPainter
from PyQt5.QtWidgets import QCheckBox, QLabel
from PyQt5.QtWidgets import QCheckBox, QLabel, QFrame, QHBoxLayout
try:
from PyQt5.QtWidgets import QGraphicsDropShadowEffect
except ImportError: # 轻量测试桩不提供图形效果类,真实 PyQt5 仍使用阴影。
class QGraphicsDropShadowEffect: # type: ignore[no-redef]
def __init__(self, *_args, **_kwargs):
pass
def setBlurRadius(self, *_args):
pass
def setOffset(self, *_args):
pass
def setColor(self, *_args):
pass
from .theme import C_BORDER, C_GREEN, C_RED, C_TITLE
@@ -102,3 +118,51 @@ class StatusDot(QLabel):
border: 1px solid rgba(136, 153, 170, 50);
}}
""")
class ProxyModeCard(QFrame):
"""整个卡片可点击;描边表示选择,圆点表示所选模式的运行状态。"""
clicked = pyqtSignal()
def __init__(self, title: str, parent=None):
super().__init__(parent)
self.setCursor(Qt.PointingHandCursor)
self._glow = QGraphicsDropShadowEffect(self)
self._glow.setBlurRadius(12)
self._glow.setOffset(0, 1)
self.setGraphicsEffect(self._glow)
row = QHBoxLayout(self)
row.setContentsMargins(11, 10, 11, 10)
row.setSpacing(5)
self.title = QLabel(title)
self.title.setStyleSheet("font-weight: bold; background: transparent;")
self.dot = StatusDot()
for child in (self.title, self.dot):
child.setAttribute(Qt.WA_TransparentForMouseEvents)
row.addWidget(self.title)
row.addStretch()
row.addWidget(self.dot)
self.set_state(False, False)
def set_state(self, selected: bool, running: bool) -> None:
border = C_TITLE if selected else "#e2e8ef"
background = "#f5f7f9" if selected else "#fafcfe"
self.setStyleSheet(
f"QFrame {{ background-color: {background}; border: {'2px' if selected else '1px'} "
f"solid {border}; border-radius: 8px; }}"
"QFrame:hover { border-color: #cbd6e2; background-color: #ffffff; }"
"QLabel { border: none; }"
)
self._glow.setColor(QColor(136, 153, 170, 105 if selected else 52))
if not selected:
self.dot.set_disabled()
elif running:
self.dot.set_on()
else:
self.dot.set_off()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton and self.rect().contains(event.pos()):
self.clicked.emit()
super().mouseReleaseEvent(event)