From 60d46a495127349fa9a4b955c55842ba32d25323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=AB=E5=9C=B0=E5=83=A7?= <357099073@qq.com> Date: Wed, 17 Jun 2026 00:22:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0cursor=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codegraph/.gitignore | 5 + __pycache__/main.cpython-313.pyc | Bin 153899 -> 202890 bytes main.py | 1174 +++++++++++++++++++++++++++--- main_backup.py | 2 +- 4 files changed, 1072 insertions(+), 109 deletions(-) create mode 100644 .codegraph/.gitignore diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 0000000..d20c0fe --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,5 @@ +# CodeGraph data files — local to each machine, not for committing. +# Ignore everything in .codegraph/ except this file itself, so transient +# files (the database, daemon.pid, sockets, logs) never show up in git. +* +!.gitignore diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index 91f8fe1f09294be1365093e0caca718686f77860..41bfc3a44b7c7a567bd56457bdb21018f4608b1e 100644 GIT binary patch delta 68548 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 Path: + home = Path.home() + if sys.platform == "win32": + return home / "AppData" / "Roaming" / "Windsurf" / "User" + elif sys.platform == "darwin": + return home / "Library" / "Application Support" / "Windsurf" / "User" + else: + return home / ".config" / "Windsurf" / "User" + + +def reset_windsurf_machine_id(log_callback) -> bool: + """重置 Windsurf 的机器码""" + try: + config_dir = get_windsurf_user_path() + global_storage_dir = config_dir / "globalStorage" + if not global_storage_dir.exists(): + log_callback("⚠️ 未找到 Windsurf 配置目录,请确认是否已安装并运行过 Windsurf。") + return False + + # 1. 检查 Windsurf 是否在运行 + windsurf_exe_names = ['windsurf.exe', 'windsurf'] + for proc in psutil.process_iter(["name"]): + try: + name = proc.info["name"] + if name and name.lower() in windsurf_exe_names: + log_callback("⚠️ 检测到 Windsurf 正在运行,请先关闭 Windsurf 软件!") + return False + except (psutil.NoSuchProcess, psutil.AccessDenied): + continue + + # 2. 备份并更新 storage.json + storage_file = global_storage_dir / "storage.json" + if storage_file.exists(): + log_callback("📖 读取 storage.json...") + backup_file = global_storage_dir / "storage.json.backup" + if backup_file.exists(): + backup_file.unlink() + shutil.copy2(storage_file, backup_file) + + with open(storage_file, "r", encoding="utf-8") as f: + content = f.read() + data = json.loads(content) if content.strip() else {} + + new_machine_id = hashlib.sha256(os.urandom(32)).hexdigest() + new_dev_device_id = str(uuid.uuid4()) + new_sqm_id = "{" + str(uuid.uuid4()).upper() + "}" + + data["telemetry.machineId"] = new_machine_id + data["telemetry.devDeviceId"] = new_dev_device_id + data["telemetry.sqmId"] = new_sqm_id + + log_callback("🔧 已生成新的 storage.json Telemetry ID") + + with open(storage_file, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + else: + log_callback("⚠️ 未找到 storage.json") + + # 3. 备份并更新 state.vscdb + db_path = global_storage_dir / "state.vscdb" + if db_path.exists(): + log_callback("📖 连接 Windsurf 数据库...") + db_backup = global_storage_dir / "state.vscdb.backup" + if db_backup.exists(): + db_backup.unlink() + shutil.copy2(db_path, db_backup) + + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + new_service_machine_id = str(uuid.uuid4()) + + cursor.execute( + "INSERT OR REPLACE INTO ItemTable (key, value) VALUES ('storage.serviceMachineId', ?)", + (new_service_machine_id,) + ) + + # 清除 codeium 登录会话 + cursor.execute( + "DELETE FROM ItemTable WHERE key LIKE 'secret://%codeium.windsurf%'" + ) + + conn.commit() + conn.close() + log_callback("🔧 已更新 state.vscdb 数据库机器码,并清除了旧的 Codeium 会话") + else: + log_callback("⚠️ 未找到 state.vscdb 数据库") + + log_callback("✅ Windsurf 机器码重置成功!") + return True + except Exception as e: + log_callback(f"❌ Windsurf 重置失败: {str(e)}") + return False + + +def clear_windsurf_cache(log_callback) -> bool: + """清除 Windsurf 缓存(删除 %APPDATA%/Windsurf 文件夹)""" + try: + path = Path.home() / "AppData" / "Roaming" / "Windsurf" + if not path.exists(): + log_callback("⚠️ 未找到 Windsurf 数据文件夹,无需清除。") + return True + + if is_windsurf_running(): + log_callback("⚠️ 检测到 Windsurf 正在运行,请先关闭 Windsurf 软件!") + return False + + log_callback(f"🧹 正在删除 Windsurf 数据文件夹: {path}") + + def remove_readonly(func, p, excinfo): + import stat + os.chmod(p, stat.S_IWRITE) + func(p) + + import stat + shutil.rmtree(path, onerror=remove_readonly) + + log_callback("✅ Windsurf 数据文件夹已成功清除!") + return True + except Exception as e: + log_callback(f"❌ 清除 Windsurf 数据失败: {str(e)}") + return False + + def get_device_info() -> str: """获取 CPU/RAM/磁盘等设备信息""" try: @@ -1385,9 +1594,83 @@ class MainWindow(QMainWindow): # 无感检测相关组件 self.txtSilentToken = self.findChild(QLineEdit, "txtSilentToken") + self.btnSilentIdMinus = QPushButton("-") + self.btnSilentIdPlus = QPushButton("+") self.btnSilentChange = self.findChild(QPushButton, "btnSilentChange") self.btnSilentUnavailable = self.findChild(QPushButton, "btnSilentUnavailable") self.btnSilentCopyToken = self.findChild(QPushButton, "btnSilentCopyToken") + + # 动态添加“可用”按钮,并应用红绿蓝配色样式 + self.btnSilentAvailable = QPushButton("可用") + self.btnSilentAvailable.setMinimumHeight(42) + + # 样式定义:无感换号-蓝色,不可用-红色,可用-绿色 + if self.btnSilentChange: + self.btnSilentChange.setStyleSheet(""" + QPushButton { + background-color: #1e88e5; + color: white; + border: none; + border-radius: 4px; + font-weight: bold; + } + QPushButton:hover { + background-color: #1565c0; + } + QPushButton:pressed { + background-color: #0d47a1; + } + QPushButton:disabled { + background-color: #cccccc; + color: #666666; + } + """) + + if self.btnSilentUnavailable: + self.btnSilentUnavailable.setStyleSheet(""" + QPushButton { + background-color: #e53935; + color: white; + border: none; + border-radius: 4px; + font-weight: bold; + } + QPushButton:hover { + background-color: #c62828; + } + QPushButton:pressed { + background-color: #b71c1c; + } + QPushButton:disabled { + background-color: #cccccc; + color: #666666; + } + """) + + self.btnSilentAvailable.setStyleSheet(""" + QPushButton { + background-color: #43a047; + color: white; + border: none; + border-radius: 4px; + font-weight: bold; + } + QPushButton:hover { + background-color: #2e7d32; + } + QPushButton:pressed { + background-color: #1b5e20; + } + QPushButton:disabled { + background-color: #cccccc; + color: #666666; + } + """) + + # 插入到布局中,放置在“不可用”按钮的旁边(即“不可用”后面,“复制 Token”前面) + layout = self.findChild(QHBoxLayout, "horizontalLayout_silentButtons") + if layout: + layout.insertWidget(2, self.btnSilentAvailable) self.lblSilentToken = self.findChild(QLabel, "lblSilentToken") self.lblCurrentDetectId = self.findChild(QLabel, "lblCurrentDetectId") self.lblCurrentDetectToken = self.findChild(QTextEdit, "lblCurrentDetectToken") @@ -1400,6 +1683,37 @@ class MainWindow(QMainWindow): if self.txtSilentToken: self.txtSilentToken.setPlaceholderText("在此输入数字 ID,例如:11") self.txtSilentToken.setMaximumWidth(150) + for btn in (self.btnSilentIdMinus, self.btnSilentIdPlus): + btn.setFixedSize(28, 28) + btn.setStyleSheet(""" + QPushButton { + border: 1px solid palette(mid); + border-radius: 4px; + font-weight: bold; + background: palette(button); + color: palette(button-text); + } + QPushButton:hover { + background: palette(alternate-base); + } + QPushButton:pressed { + background: palette(midlight); + } + """) + silent_token_layout = self.txtSilentToken.parentWidget().layout() if self.txtSilentToken.parentWidget() else None + if silent_token_layout and hasattr(silent_token_layout, "indexOf") and hasattr(silent_token_layout, "insertWidget"): + token_input_index = silent_token_layout.indexOf(self.txtSilentToken) + if token_input_index >= 0: + silent_token_row = QWidget(self.txtSilentToken.parentWidget()) + silent_token_row_layout = QHBoxLayout(silent_token_row) + silent_token_row_layout.setContentsMargins(0, 0, 0, 0) + silent_token_row_layout.setSpacing(4) + silent_token_layout.removeWidget(self.txtSilentToken) + silent_token_row_layout.addWidget(self.txtSilentToken) + silent_token_row_layout.addWidget(self.btnSilentIdMinus) + silent_token_row_layout.addWidget(self.btnSilentIdPlus) + silent_token_row_layout.addStretch() + silent_token_layout.insertWidget(token_input_index, silent_token_row) if self.btnSilentChange: self.btnSilentChange.setText("无感换号") if self.btnSilentUnavailable: @@ -1409,7 +1723,7 @@ class MainWindow(QMainWindow): if self.lblCurrentDetectId: self.lblCurrentDetectId.setText("当前检测 ID:-") if self.lblCurrentDetectToken: - self.lblCurrentDetectToken.setText("当前 Token:-") + self.lblCurrentDetectToken.setText("") if self.groupHelpSilentDetect: self.groupHelpSilentDetect.setTitle("帮助-无感检测(ID 换号版)") if self.lblHelpSilentDetectDesc: @@ -1419,6 +1733,415 @@ class MainWindow(QMainWindow): self.current_detect_id = "" self.current_detect_token = "" + # 无感检测按钮已移入应急检修的工具面板弹窗中 + + # 从左侧帮助导航列表中移除“无感检测”项与“应急检修”项,并在帮助页面进行偏置连接 + self.helpTabList = self.findChild(QListWidget, "helpTabList") + help_tab_list = self.helpTabList + self.helpContentStack = self.findChild(QStackedWidget, "helpContentStack") + if help_tab_list: + help_tab_list.setMinimumWidth(100) + help_tab_list.setMaximumWidth(130) + if help_tab_list.count() > 4: + help_tab_list.takeItem(4) # 移除“无感检测” + if help_tab_list.count() > 0: + help_tab_list.takeItem(0) # 移除“应急检修” + + if self.helpContentStack: + try: + help_tab_list.currentRowChanged.disconnect() + except Exception: + pass + help_tab_list.currentRowChanged.connect(lambda row: self.helpContentStack.setCurrentIndex(row + 1)) + help_tab_list.setCurrentRow(0) + self.helpContentStack.setCurrentIndex(1) + + # 直接在“帮助 -> 捐赠支持”页面中嵌入捐赠内容,不再使用弹窗 + group_help_donate = self.findChild(QGroupBox, "groupHelpDonate") + if group_help_donate: + lbl_help_donate_desc = self.findChild(QLabel, "lblHelpDonateDesc") + if lbl_help_donate_desc: + lbl_help_donate_desc.hide() + if self.btnDonate: + self.btnDonate.hide() + + layout_donate = group_help_donate.layout() + if not layout_donate: + layout_donate = QVBoxLayout(group_help_donate) + + title_donate = QLabel("您的捐赠将用于维护和改进本软件。\n\n感谢各位,为爱发电,作者需要大家的支持与关注!\n\n有问题请联系QQ:1066960883", group_help_donate) + title_donate_font = QFont() + title_donate_font.setPointSize(11) + title_donate_font.setBold(True) + title_donate.setFont(title_donate_font) + title_donate.setAlignment(Qt.AlignCenter) + layout_donate.addWidget(title_donate) + + layout_donate.addSpacing(15) + + images_layout = QHBoxLayout() + + wx_layout = QVBoxLayout() + wx_label = QLabel("微信支付", group_help_donate) + wx_label.setAlignment(Qt.AlignCenter) + wx_label.setStyleSheet("font-weight: bold;") + wx_layout.addWidget(wx_label) + + wx_path = get_resource_path(os.path.join("assets", "images", "donate", "wx.jpg")) + self.wx_image_embedded = ImageClickLabel(wx_path, parent=group_help_donate, max_w=180, max_h=230) + self.wx_image_embedded.clicked.connect(self.show_full_image) + wx_layout.addWidget(self.wx_image_embedded, 0, Qt.AlignCenter) + images_layout.addLayout(wx_layout) + + images_layout.addSpacing(20) + + zfb_layout = QVBoxLayout() + zfb_label = QLabel("支付宝", group_help_donate) + zfb_label.setAlignment(Qt.AlignCenter) + zfb_label.setStyleSheet("font-weight: bold;") + zfb_layout.addWidget(zfb_label) + + zfb_path = get_resource_path(os.path.join("assets", "images", "donate", "zfb.jpg")) + self.zfb_image_embedded = ImageClickLabel(zfb_path, parent=group_help_donate, max_w=180, max_h=230) + self.zfb_image_embedded.clicked.connect(self.show_full_image) + zfb_layout.addWidget(self.zfb_image_embedded, 0, Qt.AlignCenter) + images_layout.addLayout(zfb_layout) + + layout_donate.addLayout(images_layout) + layout_donate.addStretch() + + # 直接在“帮助 -> 关于软件”页面中嵌入关于内容,不再使用弹窗 + group_help_about = self.findChild(QGroupBox, "groupHelpAbout") + if group_help_about: + lbl_help_about_desc = self.findChild(QLabel, "lblHelpAboutDesc") + if lbl_help_about_desc: + lbl_help_about_desc.hide() + if self.btnAbout: + self.btnAbout.hide() + + layout_about = group_help_about.layout() + if not layout_about: + layout_about = QVBoxLayout(group_help_about) + + about_label = QLabel(group_help_about) + about_label.setText( + f"

牛马Cursor登录器

" + f"

当前版本:{__VERSION__}

" + f"

用途简介:用于 Cursor 账号登录、配置管理和常用工具操作。

" + f"

技术交流群:720797421 (QQ群)

" + ) + about_label.setStyleSheet("color: palette(text); font-size: 11pt; line-height: 1.6;") + about_label.setOpenExternalLinks(True) + layout_about.addWidget(about_label) + layout_about.addStretch() + + # 动态重组左侧菜单与页面层级,支持二级菜单 + self.tabList = self.findChild(QListWidget, "tabList") + if self.tabList: + self.tabList.setMinimumWidth(100) + self.tabList.setMaximumWidth(130) + self.contentStack = self.findChild(QStackedWidget, "contentStack") + + if self.tabList and self.contentStack: + # 1. 提取并暂存原有 UI 设计中的主页面控件 + page_login = self.findChild(QWidget, "pageLogin") + page_renewal = self.findChild(QWidget, "pageRenewal") + page_cursor_config = self.findChild(QWidget, "pageCursorConfig") + page_help = self.findChild(QWidget, "pageHelp") + + # 2. 从主 contentStack 中移除这些原有页面 + if page_login: + self.contentStack.removeWidget(page_login) + if page_renewal: + self.contentStack.removeWidget(page_renewal) + if page_cursor_config: + self.contentStack.removeWidget(page_cursor_config) + if page_help: + self.contentStack.removeWidget(page_help) + + # 样式定义:使二级侧边栏样式一致 + sidebar_style = """ + QListWidget { + border: 1px solid palette(mid); + border-radius: 4px; + background: palette(base); + color: palette(text); + outline: none; + } + QListWidget::item { + min-height: 34px; + padding: 8px 12px; + border-bottom: 1px solid palette(midlight); + } + QListWidget::item:selected { + color: palette(highlighted-text); + background: palette(highlight); + } + QListWidget::item:hover:!selected { + background: palette(alternate-base); + } + """ + + # 3. 创建全新的 "Cursor" 归集主页面,采用双栏二级子菜单布局 + self.pageCursor = QWidget() + layout_cursor = QHBoxLayout(self.pageCursor) + layout_cursor.setContentsMargins(0, 0, 0, 0) + + # Cursor 左侧二级菜单 + self.cursorTabList = QListWidget(self.pageCursor) + self.cursorTabList.setMinimumWidth(100) + self.cursorTabList.setMaximumWidth(130) + self.cursorTabList.setStyleSheet(sidebar_style) + self.cursorTabList.addItem("Token切换") + self.cursorTabList.addItem("一键续杯") + self.cursorTabList.addItem("Cursor配置") + self.cursorTabList.addItem("应急检修") + self.cursorTabList.setCurrentRow(0) + layout_cursor.addWidget(self.cursorTabList) + + # Cursor 右侧内容堆栈 + self.cursorContentStack = QStackedWidget(self.pageCursor) + layout_cursor.addWidget(self.cursorContentStack) + + if page_login: + self.cursorContentStack.addWidget(page_login) + if page_renewal: + self.cursorContentStack.addWidget(page_renewal) + if page_cursor_config: + self.cursorContentStack.addWidget(page_cursor_config) + + # 创建“应急检修”子页面,并将按钮直接显示在右侧 + self.pageCursorEmergency = QWidget() + layout_cursor_emergency = QVBoxLayout(self.pageCursorEmergency) + layout_cursor_emergency.setContentsMargins(10, 10, 10, 10) + + group_emergency = QGroupBox("应急检修", self.pageCursorEmergency) + group_emergency_layout = QVBoxLayout(group_emergency) + + desc_label = QLabel("应急检修:提供 Cursor 故障排查、缓存清理、Token 提取等应急工具。", group_emergency) + desc_label.setStyleSheet("color: palette(text); font-size: 10pt; margin-bottom: 10px;") + group_emergency_layout.addWidget(desc_label) + + grid_layout_emergency = QGridLayout() + grid_layout_emergency.setSpacing(10) + + self.btnDownloadDB = QPushButton("下载DB Browser", group_emergency) + self.btnDownloadDB.setMinimumHeight(45) + self.btnDownloadDB.setMinimumWidth(180) + self.btnDownloadDB.setMaximumWidth(260) + + self.btnClearCursorCache = QPushButton("清除Cursor缓存", group_emergency) + self.btnClearCursorCache.setMinimumHeight(45) + self.btnClearCursorCache.setMinimumWidth(180) + self.btnClearCursorCache.setMaximumWidth(260) + + self.btnExtractToken = QPushButton("Token提取", group_emergency) + self.btnExtractToken.setMinimumHeight(45) + self.btnExtractToken.setMinimumWidth(180) + self.btnExtractToken.setMaximumWidth(260) + + self.btnSilentDetect = QPushButton("无感检测", group_emergency) + self.btnSilentDetect.setMinimumHeight(45) + self.btnSilentDetect.setMinimumWidth(180) + self.btnSilentDetect.setMaximumWidth(260) + + # 应用与 self.btnEmergencyRepair 一致的样式 + for btn in (self.btnDownloadDB, self.btnClearCursorCache, self.btnExtractToken, self.btnSilentDetect): + if self.btnEmergencyRepair: + btn.setStyleSheet(self.btnEmergencyRepair.styleSheet()) + btn.setFont(self.btnEmergencyRepair.font()) + else: + btn.setStyleSheet(""" + QPushButton { + background-color: #1e88e5; + color: white; + border: none; + border-radius: 4px; + font-weight: bold; + font-size: 10pt; + } + QPushButton:hover { + background-color: #1565c0; + } + QPushButton:pressed { + background-color: #0d47a1; + } + """) + + grid_layout_emergency.addWidget(self.btnDownloadDB, 0, 0) + grid_layout_emergency.addWidget(self.btnClearCursorCache, 0, 1) + grid_layout_emergency.addWidget(self.btnExtractToken, 1, 0) + grid_layout_emergency.addWidget(self.btnSilentDetect, 1, 1) + grid_layout_emergency.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 2) + + group_emergency_layout.addLayout(grid_layout_emergency) + group_emergency_layout.addStretch() + + layout_cursor_emergency.addWidget(group_emergency) + layout_cursor_emergency.addStretch() + + self.cursorContentStack.addWidget(self.pageCursorEmergency) + + # 连接应急检修的按钮信号 + self.btnDownloadDB.clicked.connect(self.download_db_tool) + self.btnClearCursorCache.clicked.connect(self.clear_cursor_cache) + self.btnExtractToken.clicked.connect(self.on_token_extract_clicked) + self.btnSilentDetect.clicked.connect(self.on_silent_detect_menu_clicked) + + self.cursorTabList.currentRowChanged.connect(self.cursorContentStack.setCurrentIndex) + + # 4. 创建全新的 "Windsurf" 归集主页面,双栏二级子菜单布局 + self.pageWindsurf = QWidget() + layout_windsurf = QHBoxLayout(self.pageWindsurf) + layout_windsurf.setContentsMargins(0, 0, 0, 0) + + # Windsurf 左侧二级菜单 + self.windsurfTabList = QListWidget(self.pageWindsurf) + self.windsurfTabList.setMinimumWidth(100) + self.windsurfTabList.setMaximumWidth(130) + self.windsurfTabList.setStyleSheet(sidebar_style) + self.windsurfTabList.addItem("Winsurf配置") + self.windsurfTabList.setCurrentRow(0) + layout_windsurf.addWidget(self.windsurfTabList) + + # Windsurf 右侧内容堆栈 + self.windsurfContentStack = QStackedWidget(self.pageWindsurf) + layout_windsurf.addWidget(self.windsurfContentStack) + + self.pageWindsurfConfig = QWidget() + layout_windsurf_config = QVBoxLayout(self.pageWindsurfConfig) + layout_windsurf_config.setContentsMargins(10, 10, 10, 10) + + group_windsurf = QGroupBox("Winsurf配置", self.pageWindsurfConfig) + group_layout = QVBoxLayout(group_windsurf) + + grid_layout = QGridLayout() + grid_layout.setSpacing(10) + + self.btnResetWindsurf = QPushButton("重置机器码", group_windsurf) + self.btnResetWindsurf.setMinimumHeight(45) + self.btnClearWindsurf = QPushButton("清除缓存", group_windsurf) + self.btnClearWindsurf.setMinimumHeight(45) + + for btn in (self.btnResetWindsurf, self.btnClearWindsurf): + if self.btnEmergencyRepair: + btn.setStyleSheet(self.btnEmergencyRepair.styleSheet()) + btn.setFont(self.btnEmergencyRepair.font()) + else: + btn.setStyleSheet(""" + QPushButton { + background-color: #1e88e5; + color: white; + border: none; + border-radius: 4px; + font-weight: bold; + font-size: 10pt; + } + QPushButton:hover { + background-color: #1565c0; + } + QPushButton:pressed { + background-color: #0d47a1; + } + """) + + grid_layout.addWidget(self.btnResetWindsurf, 0, 0) + grid_layout.addWidget(self.btnClearWindsurf, 0, 1) + grid_layout.addItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 2) + + group_layout.addLayout(grid_layout) + group_layout.addStretch() + + layout_windsurf_config.addWidget(group_windsurf) + layout_windsurf_config.addStretch() + + self.windsurfContentStack.addWidget(self.pageWindsurfConfig) + self.windsurfContentStack.setCurrentIndex(0) + self.windsurfTabList.currentRowChanged.connect(self.windsurfContentStack.setCurrentIndex) + + self.btnResetWindsurf.clicked.connect(self.on_reset_windsurf_clicked) + self.btnClearWindsurf.clicked.connect(self.on_clear_windsurf_clicked) + + # 5. 创建全新 "Kiro" 主页面,双栏布局占位 + self.pageKiro = QWidget() + layout_kiro = QHBoxLayout(self.pageKiro) + layout_kiro.setContentsMargins(0, 0, 0, 0) + + self.kiroTabList = QListWidget(self.pageKiro) + self.kiroTabList.setMinimumWidth(100) + self.kiroTabList.setMaximumWidth(130) + self.kiroTabList.setStyleSheet(sidebar_style) + self.kiroTabList.addItem("Kiro配置") + self.kiroTabList.setCurrentRow(0) + layout_kiro.addWidget(self.kiroTabList) + + self.kiroContentStack = QStackedWidget(self.pageKiro) + layout_kiro.addWidget(self.kiroContentStack) + + self.pageKiroConfig = QWidget() + layout_kiro_config = QVBoxLayout(self.pageKiroConfig) + layout_kiro_config.setContentsMargins(10, 10, 10, 10) + + group_kiro = QGroupBox("Kiro配置", self.pageKiroConfig) + group_kiro_layout = QVBoxLayout(group_kiro) + + kiro_label = QLabel("Kiro 应急检修工具正在开发中,敬请期待...", group_kiro) + kiro_label.setStyleSheet("color: palette(text); font-size: 10pt;") + group_kiro_layout.addWidget(kiro_label) + group_kiro_layout.addStretch() + + layout_kiro_config.addWidget(group_kiro) + layout_kiro_config.addStretch() + + self.kiroContentStack.addWidget(self.pageKiroConfig) + self.kiroContentStack.setCurrentIndex(0) + self.kiroTabList.currentRowChanged.connect(self.kiroContentStack.setCurrentIndex) + + # 6. 清空主内容堆栈并按新排序添加页面:Cursor、Windsurf、Kiro、帮助 + while self.contentStack.count() > 0: + self.contentStack.removeWidget(self.contentStack.widget(0)) + + self.contentStack.addWidget(self.pageCursor) # index 0 + self.contentStack.addWidget(self.pageWindsurf) # index 1 + self.contentStack.addWidget(self.pageKiro) # index 2 + if page_help: + self.contentStack.addWidget(page_help) # index 3 + + # 7. 重建左侧主 tabList 导航项并定位到第一项 + self.tabList.clear() + self.tabList.addItem("Cursor") + self.tabList.addItem("Windsurf") + self.tabList.addItem("Kiro") + self.tabList.addItem("帮助") + self.tabList.setCurrentRow(0) + + # 8. 获取并配置拖拽分割条,使主页面和日志区域大小可交互可拖动,并增强把手的视觉提示 + self.splitterMainLog = self.findChild(QSplitter, "splitterMainLog") + if self.splitterMainLog: + self.splitterMainLog.setStyleSheet(""" + QSplitter::handle:vertical { + background-color: palette(mid); + height: 5px; + } + QSplitter::handle:vertical:hover { + background-color: palette(highlight); + } + """) + # 设置初始比例:主区域 430,日志面板 170 + self.splitterMainLog.setSizes([430, 170]) + + self.splitterBody = self.findChild(QSplitter, "splitterBody") + if self.splitterBody: + self.splitterBody.setStyleSheet(""" + QSplitter::handle:horizontal { + background-color: palette(mid); + width: 5px; + } + QSplitter::handle:horizontal:hover { + background-color: palette(highlight); + } + """) + self._load_cached_logs_to_ui() # 调试信息 @@ -1486,10 +2209,16 @@ class MainWindow(QMainWindow): self.btnRefreshMemberStatus.clicked.connect(lambda: self.on_refresh_member_status_clicked(show_popup=True)) if self.btnOneClickRenewal: self.btnOneClickRenewal.clicked.connect(self.on_one_click_renewal_clicked) + if self.btnSilentIdMinus: + self.btnSilentIdMinus.clicked.connect(lambda: self._adjust_silent_id(-1)) + if self.btnSilentIdPlus: + self.btnSilentIdPlus.clicked.connect(lambda: self._adjust_silent_id(1)) if self.btnSilentChange: self.btnSilentChange.clicked.connect(self.on_silent_detect_clicked) if self.btnSilentUnavailable: self.btnSilentUnavailable.clicked.connect(self.on_silent_unavailable_clicked) + if self.btnSilentAvailable: + self.btnSilentAvailable.clicked.connect(self.on_silent_available_clicked) if self.btnSilentCopyToken: self.btnSilentCopyToken.clicked.connect(self.on_silent_copy_token_clicked) if self.actionExit: @@ -2234,20 +2963,48 @@ class MainWindow(QMainWindow): self.thread.start() def on_donate_clicked(self): - """打开捐赠对话框""" - dialog = DonateDialog(self) - dialog.exec() + """捐赠支持:跳转到帮助 -> 捐赠支持。""" + if self.tabList and hasattr(self, 'helpTabList') and self.helpTabList: + self.tabList.setCurrentRow(3) + self.helpTabList.setCurrentRow(1) def on_about_clicked(self): - """显示关于软件信息。""" - QMessageBox.about( - self, - "关于软件", - f"牛马Cursor登录器\n\n" - f"当前版本:{__VERSION__}\n\n" - "用于 Cursor 账号登录、配置管理和常用工具操作。\n\n" - "QQ群:720797421" - ) + """关于软件:跳转到帮助 -> 关于软件。""" + if self.tabList and hasattr(self, 'helpTabList') and self.helpTabList: + self.tabList.setCurrentRow(3) + self.helpTabList.setCurrentRow(2) + + def show_full_image(self, image_path): + """显示全屏图片""" + def launch(): + dialog = QDialog(self) + dialog.setWindowTitle("图片预览") + dialog.setMinimumSize(600, 700) + + layout = QVBoxLayout(dialog) + + scroll = QScrollArea(dialog) + scroll.setWidgetResizable(True) + + label = QLabel(scroll) + pixmap = QPixmap(image_path) + label.setPixmap(pixmap) + label.setAlignment(Qt.AlignCenter) + + scroll.setWidget(label) + layout.addWidget(scroll) + + close_btn = QPushButton("关闭", dialog) + close_btn.clicked.connect(dialog.accept) + close_btn.setMinimumWidth(100) + + btn_layout = QHBoxLayout() + btn_layout.addStretch() + btn_layout.addWidget(close_btn) + layout.addLayout(btn_layout) + + dialog.exec() + QTimer.singleShot(0, launch) def on_usage_guide_clicked(self): """打开使用说明图片(非模态,可与其他窗口并行)。""" @@ -2340,49 +3097,185 @@ class MainWindow(QMainWindow): dialog.show() def on_emergency_repair_clicked(self): - """应急检修:弹出工具面板。""" - if self._emergency_dialog and self._emergency_dialog.isVisible(): - self._emergency_dialog.raise_() - self._emergency_dialog.activateWindow() - return + """应急检修:切换到 Cursor 归集二级菜单下的“应急检修”子页面。""" + if self.tabList and self.cursorTabList: + self.tabList.setCurrentRow(0) + self.cursorTabList.setCurrentRow(3) + def on_silent_detect_menu_clicked(self): + """无感检测密码验证,通过后打开无感检测窗口。""" + pwd, ok = QInputDialog.getText( + self, + "无感检测", + "请输入密码:", + QLineEdit.Password, + ) + if not ok: + return + if pwd != "920103": + self.log("❌ 无感检测密码错误") + QMessageBox.warning(self, "提示", "密码错误,无法使用无感检测。") + return + + self.on_silent_detect_popup_clicked() + + def on_silent_detect_popup_clicked(self): + """点击无感检测按钮,弹窗显示无感检测功能""" + if not self.groupHelpSilentDetect: + QMessageBox.warning(self, "提示", "未找到无感检测组件") + return + dialog = QDialog(self) - dialog.setWindowTitle("应急检修") - dialog.setMinimumSize(400, 300) + dialog.setWindowTitle("无感检测") + dialog.setMinimumSize(450, 250) dialog.setModal(False) dialog.setWindowModality(Qt.NonModal) - - layout = QVBoxLayout(dialog) - layout.addWidget(QLabel("请选择要执行的操作:")) - - actions_widget = QWidget(dialog) - actions_layout = QVBoxLayout(actions_widget) - actions_layout.setContentsMargins(0, 0, 0, 0) - actions_layout.setSpacing(10) - actions_layout.setAlignment(Qt.AlignTop | Qt.AlignLeft) - - btn_download = QPushButton("下载DB Browser") - btn_clear_cache = QPushButton("清除Cursor缓存") - btn_extract_token = QPushButton("Token提取") - for btn in ( - btn_download, - btn_clear_cache, - btn_extract_token, - ): - btn.setMinimumWidth(180) - btn.setMaximumWidth(260) - btn.setSizePolicy(btn.sizePolicy().horizontalPolicy(), btn.sizePolicy().verticalPolicy()) - actions_layout.addWidget(btn, 0, Qt.AlignLeft) - - layout.addWidget(actions_widget) - layout.addStretch() - - btn_download.clicked.connect(self.download_db_tool) - btn_clear_cache.clicked.connect(self.clear_cursor_cache) - btn_extract_token.clicked.connect(self.on_token_extract_clicked) - self._emergency_dialog = dialog + + # 保存原父级和布局,以便关闭时归还 + original_parent = self.groupHelpSilentDetect.parentWidget() + original_layout = None + if original_parent: + original_layout = original_parent.layout() + + dialog_layout = QVBoxLayout(dialog) + dialog_layout.addWidget(self.groupHelpSilentDetect) + + # 添加关闭按钮 + btn_close = QPushButton("关闭", dialog) + btn_close.setMinimumHeight(35) + btn_close.clicked.connect(dialog.close) + dialog_layout.addWidget(btn_close) + + # 在关闭时将组件还原回原布局的顶部(在 spacer 之前) + def restore_widget(): + if original_layout: + original_layout.insertWidget(0, self.groupHelpSilentDetect) + elif original_parent: + self.groupHelpSilentDetect.setParent(original_parent) + + dialog.finished.connect(restore_widget) dialog.show() + def on_reset_windsurf_clicked(self): + """重置 Windsurf 机器码。""" + # 检测 Windsurf 是否正在运行 + if is_windsurf_running(): + msg_box = QMessageBox(self) + msg_box.setWindowTitle("Windsurf正在运行") + msg_box.setText("Windsurf正在运行中!\n请先保存代码并手动关闭Windsurf。") + msg_box.setIcon(QMessageBox.Warning) + btn_close = msg_box.addButton("💀 强制关闭", QMessageBox.ActionRole) + btn_cancel = msg_box.addButton("取消", QMessageBox.RejectRole) + msg_box.setDefaultButton(btn_cancel) + msg_box.exec_() + + if msg_box.clickedButton() == btn_close: + self.log("💀 正在强制关闭Windsurf...") + if kill_windsurf(): + self.log("✅ Windsurf已关闭") + else: + self.log("⚠️ 未找到运行中的Windsurf进程") + QMessageBox.warning(self, "警告", "未找到运行中的Windsurf进程") + return + else: + return + + # 弹窗询问确认 + reply = QMessageBox.question( + self, + "确认重置", + "确定要重置 Windsurf 的机器码吗?\n该操作会清除当前账户的登录状态,重置后您需要重新登录账户。", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + if reply != QMessageBox.Yes: + return + + self.log("🔄 开始重置 Windsurf 机器码...") + + # 禁用重置按钮 + if self.btnResetWindsurf: + self.btnResetWindsurf.setEnabled(False) + self.btnResetWindsurf.setText("🔄 重置中...") + + success = reset_windsurf_machine_id(self.log) + + if success: + QMessageBox.information( + self, + "重置成功", + "Windsurf 机器码已成功重置!\n您现在可以重新打开 Windsurf 并登录新的免费账户了。" + ) + else: + QMessageBox.critical( + self, + "重置失败", + "重置 Windsurf 机器码失败,请查看日志了解详细原因。\n可能原因:Windsurf 正在运行,请先手动关闭它。" + ) + + if self.btnResetWindsurf: + self.btnResetWindsurf.setEnabled(True) + self.btnResetWindsurf.setText("重置机器码") + + def on_clear_windsurf_clicked(self): + """清除 Windsurf 缓存(直接删除文件夹)""" + # 检测 Windsurf 是否正在运行 + if is_windsurf_running(): + msg_box = QMessageBox(self) + msg_box.setWindowTitle("Windsurf正在运行") + msg_box.setText("检测到 Windsurf 正在运行!\n清除缓存前需要先关闭 Windsurf。") + msg_box.setIcon(QMessageBox.Warning) + btn_close = msg_box.addButton("💀 强制关闭", QMessageBox.ActionRole) + btn_cancel = msg_box.addButton("取消", QMessageBox.RejectRole) + msg_box.setDefaultButton(btn_cancel) + msg_box.exec_() + + if msg_box.clickedButton() == btn_close: + self.log("💀 正在强制关闭Windsurf...") + if kill_windsurf(): + self.log("✅ Windsurf已关闭") + else: + self.log("⚠️ 未找到运行中的Windsurf进程") + QMessageBox.warning(self, "警告", "未找到运行中的Windsurf进程") + return + else: + return + + # 再次弹窗确认 + reply = QMessageBox.question( + self, + "确认清除缓存", + "确定要直接删除 %APPDATA%\\Windsurf 数据文件夹吗?\n该操作会彻底清除所有配置、扩展与缓存数据,且不可恢复!", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + if reply != QMessageBox.Yes: + return + + self.log("🔄 开始清除 Windsurf 缓存文件夹...") + if self.btnClearWindsurf: + self.btnClearWindsurf.setEnabled(False) + self.btnClearWindsurf.setText("🔄 清理中...") + + success = clear_windsurf_cache(self.log) + + if success: + QMessageBox.information( + self, + "清理成功", + "Windsurf 数据文件夹已成功删除清除!" + ) + else: + QMessageBox.critical( + self, + "清理失败", + "清除 Windsurf 数据文件夹失败,请查看日志了解详细原因。" + ) + + if self.btnClearWindsurf: + self.btnClearWindsurf.setEnabled(True) + self.btnClearWindsurf.setText("清除缓存") + def on_token_extract_clicked(self): """密码通过后打开 Token 提取窗口。""" pwd, ok = QInputDialog.getText( @@ -2636,16 +3529,29 @@ class MainWindow(QMainWindow): if self.lblCurrentDetectId: self.lblCurrentDetectId.setText(f"当前检测 ID:{self.current_detect_id}") if self.lblCurrentDetectToken: - self.lblCurrentDetectToken.setText(f"当前 Token:{self.current_detect_token}") + self.lblCurrentDetectToken.setText(self.current_detect_token) self.log(f"🚀 换号成功 (ID={self.current_detect_id})") self.log(f"🔑 提取的 Token: {self.current_detect_token}") self.log("🚀 正在为您启动 Cursor...") self.on_open_cursor_clicked() - QMessageBox.information(self, "成功", "无感检测换号成功!\n已自动为您启动 Cursor。") else: QMessageBox.critical(self, "错误", message) + def _adjust_silent_id(self, delta: int): + """调整无感检测输入框中的 ID。""" + if not self.txtSilentToken: + return + + raw_id = self.txtSilentToken.text().strip() + if raw_id and not raw_id.isdigit(): + QMessageBox.warning(self, "警告", "ID 必须为纯数字,无法自动增减!") + return + + current_id = int(raw_id) if raw_id else 0 + new_id = max(0, current_id + delta) + self.txtSilentToken.setText(str(new_id)) + def on_silent_detect_clicked(self): """执行无感检测换号。""" id_str = self.txtSilentToken.text().strip() if self.txtSilentToken else "" @@ -2657,24 +3563,12 @@ class MainWindow(QMainWindow): return if is_cursor_running(): - msg_box = QMessageBox(self) - msg_box.setWindowTitle("Cursor正在运行") - msg_box.setText("检测到 Cursor 正在运行!\n由于更新数据库需要独占锁,请先关闭 Cursor。") - msg_box.setIcon(QMessageBox.Warning) - btn_close = msg_box.addButton("💀 强制关闭并继续", QMessageBox.ActionRole) - btn_cancel = msg_box.addButton("取消", QMessageBox.RejectRole) - msg_box.setDefaultButton(btn_cancel) - msg_box.exec_() - - if msg_box.clickedButton() == btn_close: - self.log("💀 正在强制关闭Cursor...") - if kill_cursor(): - self.log("✅ Cursor已关闭") - else: - self.log("⚠️ 未找到运行中的Cursor进程") - QMessageBox.warning(self, "警告", "未找到运行中的Cursor进程") - return + self.log("💀 检测到 Cursor 正在运行,正在自动关闭后继续无感换号...") + if kill_cursor(): + self.log("✅ Cursor已自动关闭") else: + self.log("⚠️ 自动关闭 Cursor 失败,请手动关闭后重试") + QMessageBox.warning(self, "警告", "自动关闭 Cursor 失败,请手动关闭后重试。") return if self.btnSilentChange: @@ -2686,23 +3580,87 @@ class MainWindow(QMainWindow): self.detect_thread.finished_signal.connect(self.on_silent_detect_finished) self.detect_thread.start() - @Slot() - def on_silent_unavailable_clicked(self): - """用户反馈当前 Token 不可用占位逻辑。""" + def _format_token_mark_result(self, data: dict) -> str: + """格式化服务端标记可用/不可用接口返回数据。""" + token_id = data.get("id", self.current_detect_id) + is_used = data.get("is_used", "-") + is_available = data.get("is_available", "-") + state_changed = data.get("state_changed", "-") + update_time = data.get("update_time", "-") + return ( + f"ID: {token_id}\n" + f"is_used: {is_used}\n" + f"is_available: {is_available}\n" + f"state_changed: {state_changed}\n" + f"update_time: {update_time}" + ) + + def _start_mark_current_token_usability(self, available: bool): + """调用服务端接口标记当前检测 ID 的 Token 可用/不可用。""" if not getattr(self, "current_detect_id", None): QMessageBox.warning(self, "警告", "请先成功换号/检测一个 ID!") return - + + action_text = "可用" if available else "不可用" reply = QMessageBox.question( self, - "反馈 Token 不可用", - f"确定要反馈当前 ID: {self.current_detect_id} 对应的 Token 为不可用吗?", + f"反馈 Token {action_text}", + f"确定要反馈当前 ID: {self.current_detect_id} 对应的 Token 为{action_text}吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No ) - if reply == QMessageBox.Yes: - self.log(f"⚠️ 用户已点击不可用按钮反馈 ID: {self.current_detect_id}。") - QMessageBox.information(self, "提示", "已收到反馈,不可用接口待后端实现。") + if reply != QMessageBox.Yes: + return + + self.log(f"🔄 正在调用服务端接口标记 ID={self.current_detect_id} 为{action_text}...") + + if self.btnSilentAvailable: + self.btnSilentAvailable.setEnabled(False) + if self.btnSilentUnavailable: + self.btnSilentUnavailable.setEnabled(False) + + self._mark_token_thread = MarkTokenUsabilityThread(self.current_detect_id, available) + self._mark_token_thread.finished_signal.connect( + lambda success, data, error_msg, available=available: self.on_mark_token_usability_finished( + success, data, error_msg, available + ) + ) + self._mark_token_thread.start() + + @Slot(bool, dict, str, bool) + def on_mark_token_usability_finished(self, success, data, error_msg, available): + """服务端标记 Token 可用/不可用完成。""" + if self.btnSilentAvailable: + self.btnSilentAvailable.setEnabled(True) + if self.btnSilentUnavailable: + self.btnSilentUnavailable.setEnabled(True) + + action_text = "可用" if available else "不可用" + if success: + result_text = self._format_token_mark_result(data) + self.log(f"✅ 已标记 ID={data.get('id', self.current_detect_id)} 为{action_text}") + self.log( + "📡 服务端返回:" + f"id={data.get('id', self.current_detect_id)}, " + f"is_used={data.get('is_used', '-')}, " + f"is_available={data.get('is_available', '-')}, " + f"state_changed={data.get('state_changed', '-')}, " + f"update_time={data.get('update_time', '-')}" + ) + QMessageBox.information(self, "提交成功", f"Token 已标记为{action_text}。\n\n{result_text}") + else: + self.log(f"❌ 标记 ID={self.current_detect_id} 为{action_text}失败: {error_msg}") + QMessageBox.critical(self, "提交失败", f"Token 标记为{action_text}失败:\n{error_msg}") + + @Slot() + def on_silent_available_clicked(self): + """用户反馈当前 Token 可用。""" + self._start_mark_current_token_usability(True) + + @Slot() + def on_silent_unavailable_clicked(self): + """用户反馈当前 Token 不可用。""" + self._start_mark_current_token_usability(False) @Slot() def on_silent_copy_token_clicked(self): diff --git a/main_backup.py b/main_backup.py index e9e65c4..06215d9 100644 --- a/main_backup.py +++ b/main_backup.py @@ -1120,7 +1120,7 @@ class MainWindow(QMainWindow): self.statusBar().addPermanentWidget(QLabel(f"Version: {__VERSION__}")) self.log("🚀 程序启动成功") - self.log("📋 请先粘贴Token,然后点击换号") + # self.log("📋 请先粘贴Token,然后点击换号") if self._splash: self._splash.finish(self)