Files
cmshoppe/docs/tasks/T-585.md
T

71 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-585
title: Chrome 路径自动检测(注册表+标准目录,Chrome-only,仅空/失效才填)+ ⑤「自动检测」按钮
phase: 7
deps: [T-557]
status: TODO
created: 2026-07-10
---
## 问题 / 背景
`chrome_path` 默认硬编码 `C:\Program Files\Google\Chrome\Application\chrome.exe`(`appconfig.py:74`),对**用户级安装**(`%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe`,免管理员安装,很常见)、`Program Files (x86)` 会失效。非标准盘的**正常安装版**通常可由 Windows 注册表定位;任意目录的便携版不应扫描整盘,继续由 T-557「选择...」手动指定。小白遇到路径不存在只能手动翻 `chrome.exe`,是"开箱即用"要消灭的摩擦。
产品原则对齐(运营日常零配置 + 逃生口 + 反馈):**自动检测填路径(小白无感)+ 保留手动「选择...」(逃生口)+ 检测结果给反馈(不做看不见的决定)**。
## 方案(改哪个文件、改成什么)
### 检测与有效性函数(`app/chrome.py`,可 mock、跨平台不崩)
- 新增 `detect_chrome_path() -> str`,按可靠性顺序返回第一个**归一化后确实存在**的 Chrome 可执行路径;无结果返回 `""`:
1. Windows App Paths:优先 `HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe`(当前用户安装),再 `HKLM` 同键;每个 hive 都尝试 64 位、32 位注册表视图,读取默认值。`winreg` 仅 Windows 导入/调用,平台不支持、键不存在或读取失败时静默跳过。
2. 标准目录兜底:`%ProgramFiles%\Google\Chrome\Application\chrome.exe`、`%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe`、`%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe`。
3. 都没有则返回空字符串。
- 所有候选路径先去除成对引号、展开 `%环境变量%` / `~`、转绝对路径,再 `os.path.isfile` 验证;不返回带启动参数、不存在的注册表值或目录路径。
- 新增内部可复用的“已配置路径是否有效”判断:绝对/相对文件路径按上述归一化校验;仅文件名 `chrome.exe` 时也允许 `shutil.which()` 从 PATH 解析成功,避免把历史上实际可启动的配置误覆盖。
- **Chrome-only**:只从上述 `chrome.exe` App Paths/目录/PATH 候选取值,不检测、不返回 Edge、Chromium 等浏览器。
- 注册表读取和文件探测需可 mock;Linux/WSL 不导入或依赖真实 `winreg`,仍可测试目录/PATH 兜底。
### 启动自动填(仅空/失效才填,不覆盖有效设置)
- `appconfig.DEFAULT_CONFIG["chrome_path"]` 改为空字符串,不再把不存在的 Program Files 路径写入新用户配置。
- 在 `prepare_data_dir()` 与强制升级检查之后、创建 `MainWindow` 之前加载配置:若 `chrome_path` 为空或“已配置路径是否有效”返回否,则调 `detect_chrome_path()`;命中后写入**归一化绝对路径**并通过既有 `appconfig.save_config()` 持久化。
- 自动填服务须返回“是否改动、最终配置、用户可见消息”,让 GUI 入口将同一份配置传给 `MainWindow`;不把检测逻辑塞进 `load_config()`,避免任意配置读取都隐式访问注册表或写文件。
- **绝不覆盖有效的已设路径**(便携版/自定义绝对路径或可从 PATH 启动的 `chrome.exe`);检测不到时不写配置、不阻断启动。
### ⑤设置「自动检测」按钮 + 反馈
- `app/gui/tabs/settings.py`:Chrome 路径行在「选择...」旁加「自动检测」按钮,点击调 `detect_chrome_path()`:
- 命中 → 填入 `chrome_path_edit` 并提示「已自动定位 Chrome:<路径>」;通过既有文本变更机制标记“未保存更改”,**不直接写 `config.json`**,由用户点击「保存设置」后持久化。
- 未命中 → 提示「未找到 Chrome,请点『选择...』手动指定,或先安装 Chrome」(指路,不是死胡同)。
- 未命中时不清空用户当前输入值;既有 T-557「选择...」不改。
- 启动自动填命中时,在 `MainWindow` 构造完成后写状态栏「已自动定位 Chrome:<路径>」,确保不会被初始“就绪”覆盖;启动不弹窗、不因未检测到 Chrome 而阻断。
### 文档同步
- `docs/04-architecture.md`(Chrome 启动/路径来源、启动自动填边界)、`docs/troubleshooting.md`(Chrome 找不到的排查改为先点“自动检测”再手动选择)。
## 验收要点
- 新配置默认 `chrome_path=""`,不会写入不存在的硬编码路径。
- `detect_chrome_path()`:mock HKCU/HKLM(含注册表视图)返回某路径且文件存在 → 返回归一化绝对路径;注册表无 → 落到标准目录,命中 `%LOCALAPPDATA%` fixture → 返回该路径;带引号/环境变量的注册表值能识别;全无 → 返回 `""`。
- 非 Windows/`winreg` 不可用 → 不抛异常,仍可走目录/PATH 兜底;不真读注册表于测试。
- 只返回 Chrome,不返回 Edge/Chromium(构造 Edge 存在但 Chrome 不存在的场景,断言返回 `""` 或不误取 Edge)。
- 启动自动填:`chrome_path` 为空 → 检测命中后写入;指向不存在文件 → 覆盖为检测结果;已设且文件存在、或 `chrome.exe` 可由 PATH 解析 → 不改(断言不覆盖);未检测到时不写配置、不阻断启动。
- 启动命中后的状态栏保留“已自动定位 Chrome”;初始“就绪”不覆盖该消息。
- ⑤「自动检测」按钮:命中填入、标记未保存并给成功提示;未命中给指路提示、不清空原值,也不写配置。
- 既有 T-557「选择...」手动选择不回归。
- 验证命令(unittest,不引入 pytest):
- `py -3.10 -m unittest tests.test_chrome tests.test_appconfig tests.test_gui`
- `python -m ruff check app tests main.py`
- `py -3.10 -m compileall app main.py`
- `py -3.10 -m unittest discover -s tests`
- `git diff --check`
## 边界(不改什么)
- 不扫描其它盘或用户目录来猜测便携版;便携版继续由用户点「选择...」指定。
- 不覆盖用户已设且有效的 `chrome_path`(便携版/自定义路径/PATH 逃生口)。
- 不检测/不自动使用 Edge、Chromium 等非 Chrome 浏览器。
- 不改 `chrome.py` 启动/CDP 逻辑本身、`chrome_user_data_dir`、端口分配。
- 不真读注册表于测试(mock);不引入新第三方依赖;不把注册表读取或配置写入放进通用 `load_config()`。
- 不改 ①采集/②生成/③更新/DB/AI/cmhub。
## 执行记录
(做完在这里写:改了什么文件、跑了什么验证命令及结果、遇到的阻塞、关键决策。)