v1.0.8
This commit is contained in:
@@ -51,12 +51,18 @@ _L = dict(
|
||||
success="#16a34a", error="#dc2626", warning="#ca8a04",
|
||||
)
|
||||
_TH = {"dark": _D, "light": _L}
|
||||
_FN = "Microsoft YaHei UI"
|
||||
_FM = "Consolas"
|
||||
# Platform-aware default fonts (overridable via gui_config.json: font_ui / font_mono)
|
||||
if sys.platform == "darwin":
|
||||
_FN = "PingFang SC"
|
||||
_FM = "Menlo"
|
||||
else:
|
||||
_FN = "Microsoft YaHei UI"
|
||||
_FM = "Consolas"
|
||||
_MENU = [
|
||||
("control", "◉", "控制台"),
|
||||
("navigator", "⬡", "路由导航"),
|
||||
("hook", "◈", "Hook"),
|
||||
("targets", "◎", "服务目标"),
|
||||
("cloud", "☁", "云扫描"),
|
||||
("extract", "◆", "敏感信息提取"),
|
||||
("vconsole", "◇", "调试开关"),
|
||||
@@ -90,6 +96,49 @@ def _save_cfg(data):
|
||||
pass
|
||||
|
||||
|
||||
# Apply font overrides from config.
|
||||
# Editable fields in gui_config.json:
|
||||
# font_ui — UI font family name
|
||||
# font_ui_size — UI base font size (int, default 9)
|
||||
# font_mono — monospace font family name
|
||||
# font_mono_size — monospace font size (int, default 9)
|
||||
_early_cfg = _load_cfg()
|
||||
if _early_cfg.get("font_ui"):
|
||||
_FN = _early_cfg["font_ui"]
|
||||
if _early_cfg.get("font_mono"):
|
||||
_FM = _early_cfg["font_mono"]
|
||||
_FN_SIZE: int = int(_early_cfg.get("font_ui_size", 9))
|
||||
_FM_SIZE: int = int(_early_cfg.get("font_mono_size", 9))
|
||||
|
||||
# Write defaults back so the fields appear in gui_config.json on first launch
|
||||
_init_cfg = dict(_early_cfg)
|
||||
_cfg_dirty = False
|
||||
for _k, _v in [("font_ui", _FN), ("font_ui_size", _FN_SIZE),
|
||||
("font_mono", _FM), ("font_mono_size", _FM_SIZE)]:
|
||||
if _k not in _init_cfg:
|
||||
_init_cfg[_k] = _v
|
||||
_cfg_dirty = True
|
||||
if _cfg_dirty:
|
||||
_save_cfg(_init_cfg)
|
||||
del _init_cfg, _cfg_dirty
|
||||
|
||||
|
||||
def _qfn(offset: int = 0, weight=None):
|
||||
"""Return QFont for UI font with size relative to _FN_SIZE."""
|
||||
f = QFont(_FN, max(6, _FN_SIZE + offset))
|
||||
if weight is not None:
|
||||
f.setWeight(weight)
|
||||
return f
|
||||
|
||||
|
||||
def _qfm(offset: int = 0, weight=None):
|
||||
"""Return QFont for mono font with size relative to _FM_SIZE."""
|
||||
f = QFont(_FM, max(6, _FM_SIZE + offset))
|
||||
if weight is not None:
|
||||
f.setWeight(weight)
|
||||
return f
|
||||
|
||||
|
||||
# ══════════════════════════════════════════
|
||||
# QSS 主题
|
||||
# ══════════════════════════════════════════
|
||||
@@ -614,7 +663,7 @@ def _make_label(text, bold=False, muted=False, mono=False):
|
||||
elif muted:
|
||||
l.setProperty("class", "muted")
|
||||
if mono:
|
||||
l.setFont(QFont(_FM, 10))
|
||||
l.setFont(_qfm(1))
|
||||
return l
|
||||
|
||||
|
||||
@@ -646,8 +695,8 @@ class App(QMainWindow):
|
||||
_ico = os.path.join(_BASE_DIR, "icon.png")
|
||||
if os.path.exists(_ico):
|
||||
self.setWindowIcon(QIcon(_ico))
|
||||
self.resize(960, 620)
|
||||
self.setMinimumSize(780, 500)
|
||||
self.resize(1060, 660)
|
||||
self.setMinimumSize(900, 540)
|
||||
|
||||
self._cfg = _load_cfg()
|
||||
self._tn = self._cfg.get("theme", "dark")
|
||||
@@ -675,6 +724,7 @@ class App(QMainWindow):
|
||||
self._sts_q = queue.Queue()
|
||||
self._rte_q = queue.Queue()
|
||||
self._cld_q = queue.Queue()
|
||||
self._tgt_q = queue.Queue()
|
||||
self._nav_route_idx = -1
|
||||
|
||||
self._sb_items = {}
|
||||
@@ -761,10 +811,10 @@ class App(QMainWindow):
|
||||
row_lay.setSpacing(6)
|
||||
ic = QLabel(icon)
|
||||
ic.setProperty("class", "sb_icon")
|
||||
ic.setFont(QFont(_FN, 13))
|
||||
ic.setFont(_qfn(4))
|
||||
nm = QLabel(name)
|
||||
nm.setProperty("class", "sb_name")
|
||||
nm.setFont(QFont(_FN, 10))
|
||||
nm.setFont(_qfn(1))
|
||||
row_lay.addWidget(ic)
|
||||
row_lay.addWidget(nm, 1)
|
||||
sb_nav_lay.addWidget(row)
|
||||
@@ -783,13 +833,13 @@ class App(QMainWindow):
|
||||
sb_app_card_lay.setSpacing(1)
|
||||
self._sb_app_name = QLabel("未连接")
|
||||
self._sb_app_name.setAlignment(Qt.AlignCenter)
|
||||
self._sb_app_name.setFont(QFont(_FN, 8))
|
||||
self._sb_app_name.setFont(_qfn(-1))
|
||||
self._sb_app_name.setStyleSheet("color: #5c5c6c;")
|
||||
self._sb_app_name.setWordWrap(True)
|
||||
sb_app_card_lay.addWidget(self._sb_app_name)
|
||||
self._sb_app_id = QLabel("")
|
||||
self._sb_app_id.setAlignment(Qt.AlignCenter)
|
||||
self._sb_app_id.setFont(QFont(_FN, 8))
|
||||
self._sb_app_id.setFont(_qfn(-1))
|
||||
self._sb_app_id.setStyleSheet("color: #9e9ea8;")
|
||||
self._sb_app_id.setVisible(False)
|
||||
self._sb_app_id.setWordWrap(True)
|
||||
@@ -801,28 +851,28 @@ class App(QMainWindow):
|
||||
self._sb_theme.setObjectName("sb_theme")
|
||||
self._sb_theme.setAlignment(Qt.AlignCenter)
|
||||
self._sb_theme.setCursor(Qt.PointingHandCursor)
|
||||
self._sb_theme.setFont(QFont(_FN, 9))
|
||||
self._sb_theme.setFont(_qfn())
|
||||
self._sb_theme.mousePressEvent = lambda e: self._toggle_theme()
|
||||
sb_lay.addWidget(self._sb_theme)
|
||||
|
||||
sb_author = QLabel("by vs-olitus")
|
||||
sb_author.setObjectName("sb_theme")
|
||||
sb_author.setAlignment(Qt.AlignCenter)
|
||||
sb_author.setFont(QFont(_FN, 8))
|
||||
sb_author.setFont(_qfn(-1))
|
||||
sb_lay.addWidget(sb_author)
|
||||
sb_gh = QLabel("github.com/Spade-sec/First")
|
||||
sb_gh.setObjectName("sb_theme")
|
||||
sb_gh.setAlignment(Qt.AlignCenter)
|
||||
sb_gh.setFont(QFont(_FN, 7))
|
||||
sb_gh.setFont(_qfn(-2))
|
||||
sb_gh.setCursor(Qt.PointingHandCursor)
|
||||
sb_gh.mousePressEvent = lambda e: (
|
||||
QDesktopServices.openUrl(QUrl("https://github.com/Spade-sec/First")),
|
||||
self._log_add("info", "[gui] 已打开 GitHub 页面"))
|
||||
sb_lay.addWidget(sb_gh)
|
||||
sb_ver = QLabel("v1.0.7")
|
||||
sb_ver = QLabel("v1.0.8")
|
||||
sb_ver.setObjectName("sb_theme")
|
||||
sb_ver.setAlignment(Qt.AlignCenter)
|
||||
sb_ver.setFont(QFont(_FN, 7))
|
||||
sb_ver.setFont(_qfn(-2))
|
||||
sb_lay.addWidget(sb_ver)
|
||||
sb_lay.addSpacing(12)
|
||||
self._update_theme_label()
|
||||
@@ -862,6 +912,7 @@ class App(QMainWindow):
|
||||
self._build_control()
|
||||
self._build_navigator()
|
||||
self._build_hook()
|
||||
self._build_targets()
|
||||
self._build_cloud()
|
||||
self._build_extract()
|
||||
self._build_vconsole()
|
||||
@@ -897,10 +948,10 @@ class App(QMainWindow):
|
||||
# Action row
|
||||
ar = QHBoxLayout()
|
||||
self._btn_start = _make_btn("▶ 启动调试", self._do_start)
|
||||
self._btn_start.setFont(QFont(_FN, 10, QFont.Bold))
|
||||
self._btn_start.setFont(_qfn(1, QFont.Bold))
|
||||
ar.addWidget(self._btn_start)
|
||||
self._btn_stop = _make_btn("■ 停止", self._do_stop)
|
||||
self._btn_stop.setFont(QFont(_FN, 10, QFont.Bold))
|
||||
self._btn_stop.setFont(_qfn(1, QFont.Bold))
|
||||
self._btn_stop.setEnabled(False)
|
||||
ar.addWidget(self._btn_stop)
|
||||
ar.addStretch()
|
||||
@@ -910,13 +961,13 @@ class App(QMainWindow):
|
||||
dt_row = QHBoxLayout()
|
||||
self._devtools_lbl = QLabel("")
|
||||
self._devtools_lbl.setProperty("class", "accent")
|
||||
self._devtools_lbl.setFont(QFont(_FM, 8))
|
||||
self._devtools_lbl.setFont(_qfm(-1))
|
||||
self._devtools_lbl.setCursor(Qt.PointingHandCursor)
|
||||
self._devtools_lbl.mousePressEvent = lambda e: self._copy_devtools_url()
|
||||
dt_row.addWidget(self._devtools_lbl)
|
||||
self._devtools_copy_hint = QLabel("")
|
||||
self._devtools_copy_hint.setProperty("class", "muted")
|
||||
self._devtools_copy_hint.setFont(QFont(_FN, 8))
|
||||
self._devtools_copy_hint.setFont(_qfn(-1))
|
||||
dt_row.addWidget(self._devtools_copy_hint)
|
||||
dt_row.addStretch()
|
||||
lay.addLayout(dt_row)
|
||||
@@ -992,6 +1043,7 @@ class App(QMainWindow):
|
||||
tc_lay = QVBoxLayout(tc)
|
||||
tc_lay.setContentsMargins(0, 0, 0, 0)
|
||||
self._tree = QTreeWidget()
|
||||
self._tree.setFont(_qfn())
|
||||
self._tree.setHeaderHidden(True)
|
||||
self._tree.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self._tree.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
@@ -1138,13 +1190,13 @@ class App(QMainWindow):
|
||||
|
||||
icon_lbl = QLabel("JS")
|
||||
icon_lbl.setProperty("class", "js_badge")
|
||||
icon_lbl.setFont(QFont(_FM, 8, QFont.Bold))
|
||||
icon_lbl.setFont(_qfm(-1, QFont.Bold))
|
||||
icon_lbl.setFixedWidth(30)
|
||||
icon_lbl.setAlignment(Qt.AlignCenter)
|
||||
row_lay.addWidget(icon_lbl)
|
||||
|
||||
name_lbl = QLabel(fn)
|
||||
name_lbl.setFont(QFont(_FN, 10))
|
||||
name_lbl.setFont(_qfn(1))
|
||||
row_lay.addWidget(name_lbl, 1)
|
||||
|
||||
is_global = fn in self._global_hook_scripts
|
||||
@@ -1260,6 +1312,160 @@ class App(QMainWindow):
|
||||
self._hook_auto_inject_globals()
|
||||
self._log_add("info", "[Hook] 自动注入全局脚本")
|
||||
|
||||
# ── 页面目标 ──
|
||||
|
||||
def _build_targets(self):
|
||||
page = QWidget()
|
||||
lay = QVBoxLayout(page)
|
||||
lay.setContentsMargins(24, 12, 24, 16)
|
||||
lay.setSpacing(10)
|
||||
|
||||
tip_row = QHBoxLayout()
|
||||
tip = QLabel("列出当前调试会话可见的微信内置浏览器 / 小程序 / webview 服务")
|
||||
tip.setProperty("class", "muted")
|
||||
tip_row.addWidget(tip)
|
||||
tip_row.addStretch()
|
||||
self._btn_tgt_refresh = _make_btn("刷新目标", self._do_targets_refresh)
|
||||
self._btn_tgt_refresh.setEnabled(False)
|
||||
tip_row.addWidget(self._btn_tgt_refresh)
|
||||
self._btn_tgt_attach = _make_btn("附加到选中目标", self._do_targets_attach)
|
||||
self._btn_tgt_attach.setEnabled(False)
|
||||
tip_row.addWidget(self._btn_tgt_attach)
|
||||
self._btn_tgt_copy = _make_btn("复制 TargetId", self._do_targets_copy)
|
||||
self._btn_tgt_copy.setEnabled(False)
|
||||
tip_row.addWidget(self._btn_tgt_copy)
|
||||
lay.addLayout(tip_row)
|
||||
|
||||
tc = _make_card()
|
||||
tc_lay = QVBoxLayout(tc)
|
||||
tc_lay.setContentsMargins(0, 0, 0, 0)
|
||||
self._targets_tree = QTreeWidget()
|
||||
self._targets_tree.setFont(_qfn())
|
||||
self._targets_tree.header().setFont(_qfn())
|
||||
self._targets_tree.setRootIsDecorated(False)
|
||||
self._targets_tree.setIndentation(0)
|
||||
self._targets_tree.setHeaderLabels(["类型", "标题", "URL", "TargetId"])
|
||||
self._targets_tree.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self._targets_tree.itemSelectionChanged.connect(self._targets_on_select)
|
||||
self._targets_tree.itemDoubleClicked.connect(lambda *_: self._do_targets_attach())
|
||||
hdr = self._targets_tree.header()
|
||||
hdr.setSectionResizeMode(0, QHeaderView.ResizeToContents)
|
||||
hdr.setSectionResizeMode(1, QHeaderView.Interactive)
|
||||
hdr.setSectionResizeMode(2, QHeaderView.Stretch)
|
||||
hdr.setSectionResizeMode(3, QHeaderView.Interactive)
|
||||
hdr.setMinimumSectionSize(50)
|
||||
self._targets_tree.setColumnWidth(1, 200)
|
||||
self._targets_tree.setColumnWidth(3, 290)
|
||||
tc_lay.addWidget(self._targets_tree)
|
||||
lay.addWidget(tc, 1)
|
||||
|
||||
self._targets_status_lbl = QLabel("状态: 未连接小程序")
|
||||
self._targets_status_lbl.setProperty("class", "muted")
|
||||
lay.addWidget(self._targets_status_lbl)
|
||||
|
||||
self._stack.addWidget(page)
|
||||
self._page_map["targets"] = self._stack.count() - 1
|
||||
|
||||
def _targets_btns(self, on):
|
||||
if not hasattr(self, "_btn_tgt_refresh"):
|
||||
return
|
||||
self._btn_tgt_refresh.setEnabled(on)
|
||||
has_sel = bool(on and self._targets_tree.selectedItems())
|
||||
self._btn_tgt_attach.setEnabled(has_sel)
|
||||
self._btn_tgt_copy.setEnabled(has_sel)
|
||||
|
||||
def _targets_on_select(self):
|
||||
self._targets_btns(bool(self._engine and self._miniapp_connected))
|
||||
|
||||
def _selected_target(self):
|
||||
items = self._targets_tree.selectedItems()
|
||||
if not items:
|
||||
self._log_add("error", "[页面目标] 请先选择一个目标")
|
||||
return None
|
||||
return items[0].data(0, Qt.UserRole) or {}
|
||||
|
||||
def _do_targets_refresh(self):
|
||||
if not self._engine or not self._loop or not self._loop.is_running():
|
||||
self._log_add("error", "[页面目标] 请先启动调试并连接小程序")
|
||||
return
|
||||
self._btn_tgt_refresh.setEnabled(False)
|
||||
self._targets_status_lbl.setText("状态: 正在刷新...")
|
||||
asyncio.run_coroutine_threadsafe(self._atargets_refresh(), self._loop)
|
||||
|
||||
async def _atargets_refresh(self):
|
||||
try:
|
||||
resp = await self._engine.send_cdp_command("Target.getTargets", timeout=8.0)
|
||||
infos = resp.get("result", {}).get("targetInfos", [])
|
||||
self._tgt_q.put(("targets", infos))
|
||||
except Exception as e:
|
||||
self._tgt_q.put(("error", f"刷新失败: {e}"))
|
||||
|
||||
def _do_targets_attach(self):
|
||||
target = self._selected_target()
|
||||
if not target or not self._engine or not self._loop or not self._loop.is_running():
|
||||
return
|
||||
target_id = target.get("targetId", "")
|
||||
if not target_id:
|
||||
self._log_add("error", "[页面目标] 选中项没有 TargetId")
|
||||
return
|
||||
self._btn_tgt_attach.setEnabled(False)
|
||||
self._targets_status_lbl.setText("状态: 正在附加...")
|
||||
asyncio.run_coroutine_threadsafe(self._atargets_attach(target_id), self._loop)
|
||||
|
||||
async def _atargets_attach(self, target_id):
|
||||
try:
|
||||
resp = await self._engine.send_cdp_command(
|
||||
"Target.attachToTarget", {"targetId": target_id}, timeout=8.0)
|
||||
self._tgt_q.put(("attached", target_id, resp))
|
||||
except Exception as e:
|
||||
self._tgt_q.put(("error", f"附加失败: {e}"))
|
||||
|
||||
def _do_targets_copy(self):
|
||||
target = self._selected_target()
|
||||
if not target:
|
||||
return
|
||||
target_id = target.get("targetId", "")
|
||||
if target_id:
|
||||
QApplication.clipboard().setText(target_id)
|
||||
self._targets_status_lbl.setText("状态: TargetId 已复制")
|
||||
self._log_add("info", f"[页面目标] 已复制 TargetId: {target_id}")
|
||||
|
||||
def _handle_tgt(self, item):
|
||||
kind = item[0]
|
||||
c = _TH[self._tn]
|
||||
if kind == "targets":
|
||||
targets = item[1]
|
||||
self._targets_tree.clear()
|
||||
for target in targets:
|
||||
target_id = target.get("targetId", "")
|
||||
title = target.get("title", "") or "--"
|
||||
url = target.get("url", "") or "--"
|
||||
typ = target.get("type", "") or "--"
|
||||
row = QTreeWidgetItem([typ, title, url, target_id])
|
||||
row.setData(0, Qt.UserRole, target)
|
||||
if target.get("attached"):
|
||||
for col in range(4):
|
||||
row.setForeground(col, QColor(c["success"]))
|
||||
self._targets_tree.addTopLevelItem(row)
|
||||
self._targets_status_lbl.setText(f"状态: 发现 {len(targets)} 个目标")
|
||||
self._targets_status_lbl.setStyleSheet(f"color: {c['success']};")
|
||||
self._log_add("info", f"[页面目标] 发现 {len(targets)} 个可选目标")
|
||||
self._targets_btns(bool(self._miniapp_connected))
|
||||
elif kind == "attached":
|
||||
_, target_id, resp = item
|
||||
session_id = resp.get("result", {}).get("sessionId", "")
|
||||
suffix = f", sessionId={session_id}" if session_id else ""
|
||||
self._targets_status_lbl.setText(f"状态: 已附加 {target_id}{suffix}")
|
||||
self._targets_status_lbl.setStyleSheet(f"color: {c['success']};")
|
||||
self._log_add("info", f"[页面目标] 已附加到目标: {target_id}{suffix}")
|
||||
self._targets_btns(bool(self._miniapp_connected))
|
||||
elif kind == "error":
|
||||
msg = item[1]
|
||||
self._targets_status_lbl.setText(f"状态: {msg}")
|
||||
self._targets_status_lbl.setStyleSheet(f"color: {c['error']};")
|
||||
self._log_add("error", f"[页面目标] {msg}")
|
||||
self._targets_btns(bool(self._miniapp_connected))
|
||||
|
||||
# ── 云扫描 ──
|
||||
|
||||
def _build_cloud(self):
|
||||
@@ -1299,6 +1505,8 @@ class App(QMainWindow):
|
||||
tc_lay.addLayout(title_row)
|
||||
|
||||
self._cloud_tree = QTreeWidget()
|
||||
self._cloud_tree.setFont(_qfn())
|
||||
self._cloud_tree.header().setFont(_qfn())
|
||||
self._cloud_tree.setRootIsDecorated(False)
|
||||
self._cloud_tree.setIndentation(0)
|
||||
self._cloud_tree.setHeaderLabels(["AppID", "类型", "名称", "参数", "状态", "时间"])
|
||||
@@ -1338,7 +1546,7 @@ class App(QMainWindow):
|
||||
self._cloud_result = QTextEdit()
|
||||
self._cloud_result.setReadOnly(True)
|
||||
self._cloud_result.setFixedHeight(120)
|
||||
self._cloud_result.setFont(QFont(_FM, 9))
|
||||
self._cloud_result.setFont(_qfm())
|
||||
lay.addWidget(self._cloud_result)
|
||||
|
||||
bot = QHBoxLayout()
|
||||
@@ -1462,7 +1670,7 @@ class App(QMainWindow):
|
||||
# 日志区
|
||||
self._ext_logbox = QTextEdit()
|
||||
self._ext_logbox.setReadOnly(True)
|
||||
self._ext_logbox.setFont(QFont(_FM, 9))
|
||||
self._ext_logbox.setFont(_qfm())
|
||||
self._ext_logbox.setMaximumHeight(120)
|
||||
main_lay.addWidget(self._ext_logbox)
|
||||
|
||||
@@ -1531,6 +1739,8 @@ class App(QMainWindow):
|
||||
bc_lay.addWidget(_make_label("内置正则规则 (只读)", bold=True))
|
||||
|
||||
self._ext_builtin_tree = QTreeWidget()
|
||||
self._ext_builtin_tree.setFont(_qfn())
|
||||
self._ext_builtin_tree.header().setFont(_qfn())
|
||||
self._ext_builtin_tree.setHeaderLabels(["分类", "正则/说明"])
|
||||
bh = self._ext_builtin_tree.header()
|
||||
bh.setStretchLastSection(True)
|
||||
@@ -1763,7 +1973,7 @@ class App(QMainWindow):
|
||||
|
||||
lbl_id = QLabel(appid)
|
||||
lbl_id.setFixedWidth(180)
|
||||
lbl_id.setFont(QFont(_FM, 9))
|
||||
lbl_id.setFont(_qfm())
|
||||
lbl_id.setStyleSheet(f"color: {c['text1']};")
|
||||
row_lay.addWidget(lbl_id)
|
||||
|
||||
@@ -1781,7 +1991,7 @@ class App(QMainWindow):
|
||||
app_name = self._ext_get_app_name(appid, pkgs, output_base) if is_decompiled else f"{len(pkgs)} pkg (未反编译)"
|
||||
lbl_name = QLabel(app_name)
|
||||
lbl_name.setMinimumWidth(100)
|
||||
lbl_name.setFont(QFont(_FN, 9))
|
||||
lbl_name.setFont(_qfn())
|
||||
lbl_name.setStyleSheet(f"color: {c['text2']};")
|
||||
row_lay.addWidget(lbl_name, 1)
|
||||
|
||||
@@ -1824,7 +2034,8 @@ class App(QMainWindow):
|
||||
|
||||
self._ext_app_widgets[appid] = {
|
||||
"row": row, "btn_dec": btn_dec, "btn_scan": btn_scan,
|
||||
"btn_view": btn_view, "btn_del": btn_del, "lbl_name": lbl_name,
|
||||
"btn_view": btn_view, "btn_del": btn_del,
|
||||
"lbl_name": lbl_name, "lbl_id": lbl_id,
|
||||
}
|
||||
|
||||
# 插入在最前面(最新的在上面)
|
||||
@@ -2100,7 +2311,7 @@ class App(QMainWindow):
|
||||
title_row.addWidget(btn_fold)
|
||||
|
||||
title_lbl = QLabel(f"{label} ({len(items)})")
|
||||
title_lbl.setFont(QFont(_FN, 11, QFont.Weight.Bold))
|
||||
title_lbl.setFont(_qfn(2, QFont.Weight.Bold))
|
||||
title_lbl.setStyleSheet(
|
||||
f"color: {c['text1']}; border-left: 4px solid {accent}; padding-left: 8px;"
|
||||
f"background: transparent;")
|
||||
@@ -2126,7 +2337,7 @@ class App(QMainWindow):
|
||||
content_lay.setSpacing(0)
|
||||
if items:
|
||||
content_lbl = QLabel()
|
||||
content_lbl.setFont(QFont(_FM, 9))
|
||||
content_lbl.setFont(_qfm())
|
||||
content_lbl.setWordWrap(True)
|
||||
content_lbl.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
|
||||
content_lbl.setStyleSheet(
|
||||
@@ -2319,11 +2530,11 @@ class App(QMainWindow):
|
||||
rl.setSpacing(6)
|
||||
lbl_n = QLabel(name)
|
||||
lbl_n.setFixedWidth(120)
|
||||
lbl_n.setFont(QFont(_FM, 9))
|
||||
lbl_n.setFont(_qfm())
|
||||
lbl_n.setStyleSheet(f"color: {c['text1']};")
|
||||
rl.addWidget(lbl_n)
|
||||
lbl_p = QLabel(pat if len(pat) < 60 else pat[:60] + "...")
|
||||
lbl_p.setFont(QFont(_FM, 9))
|
||||
lbl_p.setFont(_qfm())
|
||||
lbl_p.setStyleSheet(f"color: {c['text2']};")
|
||||
lbl_p.setToolTip(pat)
|
||||
lbl_p.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Preferred)
|
||||
@@ -2458,7 +2669,7 @@ class App(QMainWindow):
|
||||
input_lbl = QLabel("输入测试文本:")
|
||||
lay.addWidget(input_lbl)
|
||||
input_box = QTextEdit()
|
||||
input_box.setFont(QFont(_FM, 9))
|
||||
input_box.setFont(_qfm())
|
||||
input_box.setPlaceholderText("在此粘贴或输入要测试的文本...")
|
||||
lay.addWidget(input_box, 1)
|
||||
|
||||
@@ -2466,7 +2677,7 @@ class App(QMainWindow):
|
||||
lay.addWidget(result_lbl)
|
||||
result_box = QTextEdit()
|
||||
result_box.setReadOnly(True)
|
||||
result_box.setFont(QFont(_FM, 9))
|
||||
result_box.setFont(_qfm())
|
||||
lay.addWidget(result_box, 1)
|
||||
|
||||
def do_test():
|
||||
@@ -2594,7 +2805,7 @@ class App(QMainWindow):
|
||||
warn_lay.setContentsMargins(16, 12, 16, 12)
|
||||
warn_lay.setSpacing(6)
|
||||
warn_title = QLabel("⚠ 风险提示")
|
||||
warn_title.setFont(QFont(_FN, 11, QFont.Bold))
|
||||
warn_title.setFont(_qfn(2, QFont.Bold))
|
||||
warn_title.setStyleSheet("color: #e6a23c;")
|
||||
warn_lay.addWidget(warn_title)
|
||||
warn_text = QLabel(
|
||||
@@ -2637,11 +2848,11 @@ class App(QMainWindow):
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
self._btn_vc_enable = _make_btn("▶ 开启调试", self._do_vc_enable)
|
||||
self._btn_vc_enable.setFont(QFont(_FN, 10, QFont.Bold))
|
||||
self._btn_vc_enable.setFont(_qfn(1, QFont.Bold))
|
||||
self._btn_vc_enable.setEnabled(False)
|
||||
btn_row.addWidget(self._btn_vc_enable)
|
||||
self._btn_vc_disable = _make_btn("■ 关闭调试", self._do_vc_disable)
|
||||
self._btn_vc_disable.setFont(QFont(_FN, 10, QFont.Bold))
|
||||
self._btn_vc_disable.setFont(_qfn(1, QFont.Bold))
|
||||
self._btn_vc_disable.setEnabled(False)
|
||||
btn_row.addWidget(self._btn_vc_disable)
|
||||
btn_row.addStretch()
|
||||
@@ -2789,7 +3000,7 @@ class App(QMainWindow):
|
||||
lc_lay.setContentsMargins(0, 0, 0, 0)
|
||||
self._logbox = QTextEdit()
|
||||
self._logbox.setReadOnly(True)
|
||||
self._logbox.setFont(QFont(_FM, 9))
|
||||
self._logbox.setFont(_qfm())
|
||||
lc_lay.addWidget(self._logbox)
|
||||
lay.addWidget(lc, 1)
|
||||
|
||||
@@ -2831,10 +3042,37 @@ class App(QMainWindow):
|
||||
self._update_theme_label()
|
||||
self._update_toggle_colors()
|
||||
self._refresh_sb_app_card()
|
||||
self._ext_restyle_rows()
|
||||
self._ext_refresh_custom_patterns()
|
||||
self._hl_sb()
|
||||
self._auto_save()
|
||||
|
||||
def _ext_restyle_rows(self):
|
||||
"""切换主题时更新应用列表行的内联样式。"""
|
||||
c = _TH[self._tn]
|
||||
row_ss = (
|
||||
f"QFrame {{ background: {c['input']}; border-radius: 8px; }}"
|
||||
f"QFrame QLabel {{ background: transparent; }}"
|
||||
)
|
||||
for appid, widgets in self._ext_app_widgets.items():
|
||||
row = widgets.get("row")
|
||||
if row:
|
||||
row.setStyleSheet(row_ss)
|
||||
lbl_id = widgets.get("lbl_id")
|
||||
if lbl_id:
|
||||
lbl_id.setStyleSheet(f"color: {c['text1']};")
|
||||
lbl_name = widgets.get("lbl_name")
|
||||
if lbl_name:
|
||||
lbl_name.setStyleSheet(f"color: {c['text2']};")
|
||||
# 反编译按钮颜色也跟主题走
|
||||
btn_dec = widgets.get("btn_dec")
|
||||
if btn_dec:
|
||||
state = self._ext_app_states.get(appid, {})
|
||||
if state.get("decompiled"):
|
||||
btn_dec.setStyleSheet(
|
||||
f"QPushButton {{ background: {c['success']}; color: #111; "
|
||||
f"border-radius: 6px; padding: 4px 8px; font-size: 9px; }}")
|
||||
|
||||
def _update_theme_label(self):
|
||||
txt = "☀ 浅色模式" if self._tn == "dark" else "☽ 深色模式"
|
||||
self._sb_theme.setText(txt)
|
||||
@@ -2864,6 +3102,10 @@ class App(QMainWindow):
|
||||
"auto_decompile": self._tog_auto_dec.isChecked(),
|
||||
"auto_scan": self._tog_auto_scan.isChecked(),
|
||||
"global_hook_scripts": list(self._global_hook_scripts),
|
||||
"font_ui": _FN,
|
||||
"font_ui_size": _FN_SIZE,
|
||||
"font_mono": _FM,
|
||||
"font_mono_size": _FM_SIZE,
|
||||
}
|
||||
_save_cfg(data)
|
||||
|
||||
@@ -2963,6 +3205,7 @@ class App(QMainWindow):
|
||||
self._btn_stop.setEnabled(False)
|
||||
self._btn_fetch.setEnabled(False)
|
||||
self._nav_btns(False)
|
||||
self._targets_btns(False)
|
||||
if self._loop and self._loop.is_running():
|
||||
self._loop.call_soon_threadsafe(self._loop.stop)
|
||||
|
||||
@@ -2985,10 +3228,13 @@ class App(QMainWindow):
|
||||
self._btn_stop.setEnabled(False)
|
||||
self._btn_fetch.setEnabled(False)
|
||||
self._nav_btns(False)
|
||||
self._targets_btns(False)
|
||||
self._btn_autostop.setEnabled(False)
|
||||
self._redirect_guard_on = False
|
||||
self._guard_switch.setChecked(False)
|
||||
self._guard_label.setText("防跳转: 关闭")
|
||||
self._targets_tree.clear()
|
||||
self._targets_status_lbl.setText("状态: 未连接小程序")
|
||||
self._devtools_lbl.setText("")
|
||||
self._devtools_copy_hint.setText("")
|
||||
# 引擎停止,清除侧栏和运行状态卡片的小程序信息
|
||||
@@ -3064,6 +3310,8 @@ class App(QMainWindow):
|
||||
if not self._miniapp_connected:
|
||||
return
|
||||
self._nav_btns(True)
|
||||
self._targets_btns(True)
|
||||
self._targets_status_lbl.setText("状态: 可刷新目标")
|
||||
self._btn_vc_enable.setEnabled(True)
|
||||
self._btn_vc_disable.setEnabled(True)
|
||||
self._vc_status_lbl.setText("状态: 就绪")
|
||||
@@ -3622,6 +3870,12 @@ class App(QMainWindow):
|
||||
except queue.Empty:
|
||||
break
|
||||
self._handle_cld(item)
|
||||
for _ in range(50):
|
||||
try:
|
||||
item = self._tgt_q.get_nowait()
|
||||
except queue.Empty:
|
||||
break
|
||||
self._handle_tgt(item)
|
||||
for _ in range(50):
|
||||
try:
|
||||
item = self._ext_q.get_nowait()
|
||||
@@ -3641,6 +3895,8 @@ class App(QMainWindow):
|
||||
if not is_connected and self._miniapp_connected:
|
||||
if not self._all_routes:
|
||||
self._nav_btns(False)
|
||||
self._targets_btns(False)
|
||||
self._targets_status_lbl.setText("状态: 未连接小程序")
|
||||
self._btn_vc_enable.setEnabled(False)
|
||||
self._btn_vc_disable.setEnabled(False)
|
||||
self._vc_status_lbl.setText("状态: 未连接小程序")
|
||||
@@ -3936,7 +4192,7 @@ if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
app.setFont(QFont(_FN, 9))
|
||||
app.setFont(QFont(_FN, _FN_SIZE))
|
||||
_ico = os.path.join(_BASE_DIR, "icon.png")
|
||||
if os.path.exists(_ico):
|
||||
app.setWindowIcon(QIcon(_ico))
|
||||
|
||||
Reference in New Issue
Block a user