docs: README 增加截图预览并停止跟踪内部文档

- README 新增截图预览:插件设置界面与后台深色模式效果
- docs 下内部审查/计划文档从仓库移除,本地保留并加入 gitignore
- 新增 docs/screenshots 4 张截图
This commit is contained in:
2026-08-08 21:36:59 +08:00
parent c4d99ec041
commit 3e9630fc91
12 changed files with 16 additions and 628 deletions
+4
View File
@@ -35,3 +35,7 @@ third-party/darkreader/*
!third-party/darkreader/SHA256SUMS !third-party/darkreader/SHA256SUMS
__pycache__/ __pycache__/
*.pyc *.pyc
# 内部审查/计划文档不入库,仅保留本地
docs/*.md
docs/**/*.md
+12
View File
@@ -28,6 +28,18 @@
| 主题模式 | `dark` | 始终使用深色模式 | | 主题模式 | `dark` | 始终使用深色模式 |
| 主题模式 | `auto` | 跟随系统外观自动切换 | | 主题模式 | `auto` | 跟随系统外观自动切换 |
## 截图预览
以下截图展示插件在 Halo 后台的实际效果。
![插件设置界面](docs/screenshots/插件界面.png)
![后台深色模式效果 1](docs/screenshots/photo1.png)
![后台深色模式效果 2](docs/screenshots/photo2.png)
![后台深色模式效果 3](docs/screenshots/photo3.png)
## 技术原理 ## 技术原理
- 插件通过 `useDarkMode()` 管理 `light` / `dark` / `auto` 三种状态。 - 插件通过 `useDarkMode()` 管理 `light` / `dark` / `auto` 三种状态。
-151
View File
@@ -1,151 +0,0 @@
# 修复计划 — halo-dark-mode-plugin2026-08-08
> 依据:`docs/review-2026-08-08.md`
> 状态:待用户审批
> 审批通过后按 P2 → P3 顺序执行
## 一、修复总览
| ID | 优先级 | 问题 | 修复方式 | 主要涉及文件 |
| --- | --- | --- | --- | --- |
| P2-1 | P2 | 深色用户刷新 console 会浅色闪烁 | 同步设置 `color-scheme`,在 `enable()` 前减少闪烁 | `ui/src/composables/useDarkMode.ts``ui/src/darkreader-engine.ts` |
| P2-2 | P2 | 本地项目文档仍描述旧的手工 CSS / Monaco 架构 | 同步 AGENTS.md、CLAUDE.md、scripts/README.md 到纯 Dark Reader 现状 | `AGENTS.md``CLAUDE.md``scripts/README.md` |
| P2-3 | P2 | `scripts/``workplace/` 脚本双份 | 删除重复脚本;专用分析脚本保留在 `workplace/` 或迁移,待确认 | `workplace/login_wait.py``workplace/scan_dark.py` 等 |
| P2-4 | P2 | 前端无单元测试 | 为 `useDarkMode` 补测试,覆盖 light/dark/auto、持久化与非法回退 | `ui/src/composables/__tests__/useDarkMode.spec.ts` |
| P2-5 | P2 | 多标签页主题不同步 | 监听 `storage` 事件同步主题 | `ui/src/composables/useDarkMode.ts` |
| P2-6 | P2 | 切换器/设置选项不可键盘操作 | 改为原生 `button` + ARIA,或原生 radio 语义 | `ui/src/components/ThemeToggle.vue``ui/src/views/SettingsView.vue` |
| P3-1 | P3 | 后端使用 `System.out.println` | 使用 Lombok `@Slf4j` 输出日志 | `src/main/java/run/halo/darkmode/DarkModePlugin.java` |
| P3-2 | P3 | vendored package.json 保留完整 devDependencies | 裁剪为最小字段,只保留构建所需元数据 | `third-party/darkreader/package.json` |
| P3-3 | P3 | 无 Dark Reader 升级/完整性机制 | 增加 `SHA256SUMS` 并写入 README 校验说明 | `third-party/darkreader/SHA256SUMS``README.md``.gitignore` |
| P3-4 | P3 | `data-halo-theme` 属性为历史遗留 | 保留属性,但在代码注释中说明兼容性遗留 | `ui/src/composables/useDarkMode.ts``ui/src/styles/variables.css` |
| P3-6 | P3 | `plugin.yaml``spec.enabled: true` | 建议改为 `false`,安装后由用户启用 | `src/main/resources/plugin.yaml` |
## 二、详细修复说明
### P2-1 FOUC 闪烁
-`useDarkMode.ts``applyHtmlAttribute()` 中同步设置:
`document.documentElement.style.colorScheme = isDark ? 'dark' : 'light'`
- `darkreader-engine.ts` 保持现有 `enable()/disable()` 逻辑。
- `color-scheme` 只能缓解浏览器控件/滚动条闪烁,无法完全消除 DR 异步注入间隙;
该限制会写进注释和 README。
### P2-2 本地项目文档
- `AGENTS.md` / `CLAUDE.md`
- 删除 `overrides/``halo-core.css`、Monaco 同步、FOUC 同步脚本等旧描述
- 统一为“纯 Dark Reader 策略”
- `scripts/README.md`
- 把“Monaco 日志查看器主题同步”改为检查 `data-darkreader-mode`
- 补充 `verify-toggle.py` 的新三向断言说明
### P2-3 脚本收口
- 明确 `scripts/` 为正式工具目录。
- 计划删除的重复文件(需审批):
- `workplace/login_wait.py`(重复 `scripts/login-wait.py`
- `workplace/scan_dark.py`(重复 `scripts/scan-dark.py`
- `workplace/fetch_bundle.py``probe_conflict.py``aggregate.py` 属于专用分析工具,
默认保留在 `workplace/`;如需一并迁移到 `scripts/`,请在审批时注明。
### P2-4 单元测试
新增 `ui/src/composables/__tests__/useDarkMode.spec.ts`,覆盖:
1. 默认 `auto` + 系统深色 → `isDark = true`
2. `auto` 下点击切换 → 变成显式深色/浅色
3. `dark ↔ light` 往返切换
4. `setTheme()` 持久化到 localStorage
5. localStorage 非法值回退到 `auto`
6. `storage` 事件跨标签页同步
同时移除 `pnpm test:unit``--passWithNoTests`,防止测试静默通过。
### P2-5 多标签页同步
- `useDarkMode.ts` 增加:
`window.addEventListener('storage', handler)`
- 仅当 `newValue` 是合法 `light/dark/auto` 时更新 `theme`
### P2-6 可访问性
- `ThemeToggle.vue`
- `div` 改为 `<button type="button">`
- 增加 `:aria-pressed="isDark"`
- 保留现有样式并补按钮 reset
- `SettingsView.vue`
- 选项容器加 `role="radiogroup"`
- 每个选项改为 `<button type="button" role="radio" :aria-checked="...">`
- 键盘 Tab / Enter / Space 原生可用
### P3-1 后端日志
- `DarkModePlugin.java` 增加 `@Slf4j`
- `System.out.println` 改为 `log.info(...)`
- 保留 start/stop 语义不变
### P3-2 vendored package.json
- 裁剪为:
`name / version / description / main / module / types / license`
- 删除 scripts、devDependencies、optionalDependencies 等构建无关字段
- 更新后执行 `pnpm install` 同步 lockfile
### P3-3 完整性校验
- 新增 `third-party/darkreader/SHA256SUMS`,记录:
`darkreader.js``darkreader.mjs``package.json``index.d.ts``LICENSE`
- `.gitignore` 白名单增加 `SHA256SUMS`
- README 增加“升级 Dark Reader 后校验 SHA256SUMS”说明
### P3-4 历史遗留属性
- 保留 `data-halo-theme`verify-toggle.py 仍使用)
-`useDarkMode.ts``variables.css` 增加注释:
该属性当前无 CSS 消费方,仅为兼容性遗留标记
### P3-6 plugin.yaml
- 建议将 `spec.enabled` 改为 `false`
- 理由:官方 manifest 文档建议生产环境由用户安装后手动启用
## 三、暂缓项(本计划不做)
- P3-5 i18n:目标用户为中文,暂不引入国际化
- Dark Reader 完整源码保留在本地但不入库:维持现状
- 不新增界面截图:README 已可用,后续上架前再补
## 四、回归验证
```bash
cd ui
pnpm install
pnpm type-check
pnpm lint
pnpm test:unit
pnpm build
cd ..
./gradlew test
./gradlew build
```
运行时验证:
```bash
D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\verify-toggle.py
```
验收标准:
- 单测至少覆盖 P2-4 中列出的 6 个场景且全部通过
- `pnpm test:unit` 不再 `--passWithNoTests`
- 深色刷新时 `color-scheme` 已同步
- 键盘可操作侧边栏按钮与设置选项
- `SHA256SUMS` 可校验,README 有说明
- 后端日志不再输出到 stdout
## 五、版本与提交
- 修复内容涉及运行时代码,后续打 JAR 时按项目约定递增版本(1.0.4 → 1.0.5
- 本次审批通过后,先提交代码修复,再按需打包
-81
View File
@@ -1,81 +0,0 @@
# 问题交接单:深色模式入口去重 + 菜单分组调整(2026-08-08
> 审查窗口产出,**交付开发窗口执行**;审查窗口不修改代码。
> 用户需求(已确认):① 深色模式设置入口从"偏好设置"移到 Halo 官方"外观"分组;② 移除侧边栏底部注入的切换按钮(与菜单入口重复,用户判定多余)。
> 前置说明:真实环境取证于 `blog.liuhangyv.top/console`(已登录),见"问题描述"。
## 一、问题描述
### 问题 1:深色模式存在两个入口,功能重复
当前 Halo 后台对"深色模式"有两个入口:
- **入口 A(侧边栏底部切换按钮)**:插件注入的 `ThemeToggle` 按钮(显示"浅色模式 / 深色模式",点击一键切换)。
- **入口 B(侧边栏菜单项)**:「偏好设置 → 深色模式」设置页(浅色 / 深色 / 跟随系统三选一)。
用户观察到侧边栏底部有两个"切换"类入口,底部注入的按钮闲置不用,判定没有必要,要求移除。
**真实环境取证结果**(消除歧义):
- 侧边栏底部**只有 1 个**主题切换按钮:插件注入的 `plugin-dark-mode-toggle`(位于 `sidebar__profile` 上方),工作正常。
- 用户信息区(`user-profile__actions`)另外两个图标按钮是 Halo 官方「个人资料(跳 /uc)」和「退出登录」——已核对官方源码 `ui/src/layouts/UserProfileBanner.vue`**不是**主题切换。
- 因此用户所说的"两个切换按钮" = 入口 A(底部注入按钮)+ 入口 B(菜单项),二者功能重复,保留其一即可。
### 问题 2:设置页入口挂在自建分组"偏好设置"下
- 现状:菜单 `group` 为字符串 `'偏好设置'`。这不是 Halo 标准分组 key,Halo 找不到对应 i18n 翻译,直接将该字符串作为分组标题显示,于是在侧边栏多出"偏好设置"分组(其中只有"深色模式"一项)。
- 期望:放入 Halo 官方**「外观」**分组(主题 / 菜单 / 插件所在)。
- 背景:Halo 2.25 官方分组 key`halo-dev/halo` `ui/console-src/router/constant.ts`):`dashboard``content``interface``system``tool`。其中 **`interface` 即"外观"**(实测该分组内已有主题 / 菜单 / 插件)。
## 二、问题在代码中的具体体现
| # | 文件 | 位置 | 说明 |
| --- | --- | --- | --- |
| 1 | `ui/src/index.ts` | L5 | `import { injectThemeToggle } from './injector'` |
| 2 | `ui/src/index.ts` | L7-L8 | 模块加载时执行 `injectThemeToggle()` → 注入入口 A |
| 3 | `ui/src/index.ts` | L26 | `group: '偏好设置'` → 自建字面量分组(入口 B 位置错误) |
| 4 | `ui/src/injector.ts` | 全文 | 入口 A 的注入器:`CONTAINER_CLASS = 'plugin-dark-mode-toggle'`L4)、`TARGET_SELECTOR = '.sidebar__profile'`L5)、`injectThemeToggle()``MutationObserver` 等待侧边栏渲染(L13-L31)、`tryMount()` 将容器插入 profile 上方并 `render(ThemeToggle)`L33-L48 |
| 5 | `ui/src/components/ThemeToggle.vue` | 全文 | 入口 A 的按钮本体(class `theme-toggle`,文案"浅色模式 / 深色模式" |
**连带影响(改动入口 A 时必须同步处理):**
| # | 文件 | 位置 | 影响 |
| --- | --- | --- | --- |
| 6 | `scripts/verify-toggle.py` | L40-L43 | 依赖 `.theme-toggle` 定位点击验证,移除按钮后脚本失效 |
| 7 | `README.md` | L11 / L19 / L23 / L111 / L116 / L130 / L138 | 多处描述"侧边栏一键切换 / UserProfileBanner / 注入器" |
| 8 | `CLAUDE.md` / `AGENTS.md` | L54 / L59 / L86-L88 | 架构树与"ThemeToggle 侧边栏注入"章节(gitignored,本地维护) |
**不涉及**`useDarkMode.ts``darkreader-engine.ts``SettingsView.vue` 均无需改动(入口 B 及其状态管理完全独立)。
## 三、修改建议(供开发窗口执行)
### 推荐方案:移分组 + 移除侧边栏注入按钮
1. **`ui/src/index.ts`**
- L26`group: '偏好设置'``group: 'interface'`(进入"外观"分组)
- L5:删除 `import { injectThemeToggle } from './injector'`
- L7-L8:删除调用与注释
2. **删除文件(需用户书面确认,AGENTS.md 约定)**
- `ui/src/injector.ts`
- `ui/src/components/ThemeToggle.vue`
3. **`scripts/verify-toggle.py`**
- 移除 L40-L43 对 `.theme-toggle` 的点击依赖
- 改为直接驱动状态 + 断言,例如:`localStorage.setItem('halo-dark-mode-theme', 'dark'/'light')` 后断言 `data-halo-theme``data-darkreader-mode``color-scheme` 翻转;保留现有三向 PASS 判定逻辑
4. **文档同步**
- `README.md`:功能特性去掉"侧边栏一键切换"、使用说明改为"进入「外观 → 深色模式」"、项目结构删除 injector/ThemeToggle 两行、验证脚本描述更新
- `CLAUDE.md` / `AGENTS.md`:删除"ThemeToggle 侧边栏注入"章节,更新架构树
5. **构建与回归**
- `cd ui && pnpm build``pnpm type-check``pnpm prettier``pnpm lint``pnpm test:unit`
- 根目录 `gradlew build`
- 部署后跑 `scripts/verify-toggle.py`(改版后)
### 备选方案:仅移分组(保留侧边栏按钮)
- 只改 `ui/src/index.ts` L26 为 `'interface'`;验证脚本与文档均不用动。
- 适合希望保留"一键切换"快捷方式的场景;但无法满足用户"移除多余按钮"的诉求。
## 四、注意事项
- **删除文件必须先获得用户书面同意**(项目 AGENTS.md 明确"未经书面同意不得删除任何文件");用户本意是"没必要保留入口 A",可据此向用户确认后执行。
- 移除入口 A 后,设置页(`SettingsView.vue`)成为唯一入口,属预期结果,保留即可。
- `useDarkMode` 的 localStorage 持久化、storage 跨标签同步、Dark Reader 引擎均与入口 A 无关,不受影响。
- 版本号建议随本次改动递增(如 1.0.6),并同步 `plugin.yaml`(如需)与 `@since`
@@ -1,175 +0,0 @@
# 改进交接单:设置页官方化改造(2026-08-08)
> 审查窗口调研产出,**交付开发窗口执行**;审查窗口不修改代码。
> 调研来源:Halo 官方仓库 `halo-dev/halo`commit `815292f`+ 本地代码 + 线上 `blog.liuhangyv.top/console` 实证(已登录)。
> 本文档为**唯一交付文件**(已合并此前两份调研文档)。
## 〇、需求与结论总览
| # | 需求 | 结论 |
| --- | --- | --- |
| 1 | 侧边栏「深色模式」入口状态反馈 | **不做改动**(依据见第一节) |
| 2 | 设置页顶部缺官方风格标题栏 | **要改**:使用官方 `VPageHeader`(第二节) |
| 3 | 尽量用官方组件实现 | 组件全景与复用方案见第三、四节 |
## 一、侧边栏「深色模式」入口状态反馈(结论:保持现状,不动)
### 1.1 现象
点击「深色模式」后界面切换为深色,但侧边栏「深色模式」入口(菜单项)文案没有随之变化;用户曾疑问是否应显示"白天模式 / 深色模式 / 跟随系统模式"。
### 1.2 调研结论(证据)
1. **Halo 菜单是静态导航**`use-route-menu-generator.ts` 一次性从 `router.getRoutes()``meta.menu` 生成菜单,无业务状态联动。
2. **菜单名是静态字符串**`RoutesMenu.tsx``title={t(item.name, item.name)}`,官方机制不支持动态文案;所有官方/插件菜单项均为固定名称。
3. **激活高亮已生效**`active={route.matched.includes(item.path)}`;线上实证进入 `/console/dark-mode-settings` 后,菜单项 `class="active menu-item-title"`
4. **平台惯例**:窗口内改设置、侧边栏入口不变化,是 Halo 全站一致行为(菜单是入口,不是状态开关)。
### 1.3 结论
- 动态菜单文案不可直接实现(需 DOM hack,不推荐)。
- 保持静态「深色模式」菜单名,激活高亮现状即符合平台标准。
- 当前模式信息继续由设置页内「当前生效」展示(第四节中会优化展示形式)。
## 二、设置页顶部标题栏(需改造)
### 2.1 现象
`/console/dark-mode-settings` 顶部为自定义 `h1 + p`;官方页面(`/console/theme``/console/plugins`)顶部有统一白底标题栏。线上实证:设置页 `document.querySelectorAll('.page-header').length === 0`
### 2.2 官方 `VPageHeader` 组件(`@halo-dev/components` 已导出)
结构(源码 `ui/packages/components/src/components/header/PageHeader.vue`):
```html
<div class="page-header">
<h2 class="page-header__title">
<slot name="icon" />
<span class="page-header__title-text">{{ title }}</span>
</h2>
<div class="page-header__actions"><slot name="actions" /></div>
</div>
```
官方用法(`PluginList.vue`):
```html
<VPageHeader :title="…">
<template #icon><IconPlug /></template>
<template #actions>…按钮…</template>
</VPageHeader>
<div class="m-0 md:m-4"><VCard></VCard></div>
```
## 三、官方组件库全景(21 类)与插件对照
### 3.1 组件清单
| 目录 | 导出名 | 用途 | 设置页可用 |
| --- | --- | --- | --- |
| header | `VPageHeader` | 页面标题栏 | ✅ |
| card | `VCard`title / bodyClassslot header | 内容卡片 | ✅ |
| description | `VDescription` / `VDescriptionItem`label / content / verticalCenter | 键值展示 | ✅ |
| tag | `VTag`theme / roundedslot leftIcon | 标签/徽标 | ✅ |
| status | `VStatusDot`state / text / animate | 状态点 | ✅ |
| button | `VButton`type / size / block / ghost / loading / routeslot icon | 按钮 | ✅ |
| space | `VSpace` | 间距布局 | ✅ |
| alert | `VAlert` | 提示条 | 可选 |
| switch | `VSwitch` | 开关 | 未来扩展 |
| menu | `VMenu` / `VMenuItem` / `VMenuLabel` | 菜单 | — |
| entity | `VEntity` / `VEntityContainer` / `VEntityField` | 列表实体 | — |
| modal / dialog | `VModal` / `VDialog` | 模态/确认 | — |
| dropdown | `VDropdownItem` / `VDropdownDivider` | 下拉 | — |
| tabs | `VTabs` / `VTabItem` / `VTabbar` | 标签页 | — |
| pagination / empty / loading | `VPagination` / `VEmpty` / `VLoading` | 分页/空态/加载 | — |
| avatar | `VAvatar` / `VAvatarGroup` | 头像 | — |
| toast / tooltip | `toast` 函数 / `vTooltip` 指令(非 V 组件) | 提示 | 可选 |
> **注意:官方组件库没有 radio / radio group 单选组件** —— 三个模式选项保持语义化 `<button>` 是合理方案。
### 3.2 插件现状 vs 官方组件
| 插件现状 | 建议 |
| --- | --- |
| 标题:自定义 `.dark-mode-settings__header`h1+p | **`VPageHeader`** |
| 卡片:`.dark-mode-settings__card` | **`VCard`** |
| 「当前生效」:自定义 div + strong | **`VDescription` + `VDescriptionItem`**,模式值用 **`VTag`** / `VStatusDot` |
| 三个模式选项:自定义 `<button>` + `is-active` | 保持 `<button>`(无官方单选组件) |
| 图标:`IconPalette` / ri 图标 | 已是官方体系 ✅ |
## 四、设置页改造方案(用官方组件实现)
`ui/src/views/SettingsView.vue` 重构骨架:
```html
<script setup lang="ts">
import {
IconPalette,
VCard,
VDescription,
VDescriptionItem,
VPageHeader,
VTag,
} from '@halo-dev/components'
// …现有 useDarkMode / currentEffectiveMode / modeOptions 逻辑保留…
</script>
<template>
<div>
<VPageHeader title="深色模式设置">
<template #icon><IconPalette /></template>
<!-- 可选:<template #actions><VButton size="sm" @click="…">…</VButton></template> -->
</VPageHeader>
<div class="m-0 md:m-4">
<VCard :body-class="['!p-0']">
<div class="p-4">
<VDescription>
<VDescriptionItem label="当前生效">
<VTag>{{ currentEffectiveMode }}</VTag>
</VDescriptionItem>
</VDescription>
<div class="dark-mode-settings__options">
<!-- 三个模式按钮保持现有实现(含 is-active 高亮) -->
</div>
</div>
</VCard>
</div>
</div>
</template>
```
要点:
- 外层 `m-0 md:m-4` + `VCard` 是官方列表页通行布局(同 `PluginList.vue`)。
- 顶部标题栏白底在深色模式下由 Dark Reader 自动转换,无需额外适配。
- 三个模式选项维持 `<button>`(官方无单选组件),保留 `is-active` 选中态。
- 样式清理:移除 `.dark-mode-settings__header` / `__title` / `__desc` 相关 CSS;卡片/选项样式按需精简。
## 五、涉及文件与验证
| 文件 | 改动 |
| --- | --- |
| `ui/src/views/SettingsView.vue` | 用 `VPageHeader` / `VCard` / `VDescription` / `VTag` 重构 |
| `ui/src/styles/variables.css` | 可精简(自定义 header/card 样式移除后) |
| `README.md` / `CLAUDE.md` / `AGENTS.md` | 可选:同步设置页结构描述 |
验证:
```bash
cd ui
pnpm build && pnpm type-check && pnpm prettier && pnpm lint && pnpm test:unit
cd .. && gradlew build
```
部署后线上确认:
- `/console/dark-mode-settings` 顶部出现与官方一致的 `.page-header` 标题栏
- 侧边栏「深色模式」菜单项进入页面后保持高亮(现状回归确认,未改动)
- 深色模式下标题栏/卡片与整体协调
## 六、注意事项
- 问题 1(侧边栏入口)**明确不做改动**。
- 官方组件无单选组件,三选项保持 `<button>`,勿强行套用不存在的组件。
- 官方组件样式依赖 Halo Console 的 UnoCSS/主题变量,插件内直接使用即可。
- i18n(可选):插件文案目前硬编码中文,未来可接入 Halo `locales` 国际化,非本次必做。
-70
View File
@@ -1,70 +0,0 @@
# 复查交接单 — halo-dark-mode-plugin2026-08-08 第二版)
> 由审查窗口对开发窗口修复结果(`d95d172 fix: 按审查报告修复 P2/P3 问题并升级 1.0.5`、`b6403b7 docs: 按 Halo 官方插件 README 风格重写`)进行复查。
> 初审交接单见 `docs/review-2026-08-08.md`(本文件为其复查结果,独立文档)。
> 复查基线:`HEAD = d95d172`,工作区有未提交改动(见"遗留问题")。
## 一、总体结论
**P2/P3 绝大部分已修复,构建链全绿,可收尾。** 无新增阻断问题。剩余 3 个遗留项均为"收尾/决策"性质。
## 二、逐项复查结果(对照初审交接单)
| 清单项 | 状态 | 说明 |
| --- | --- | --- |
| P2-1 FOUC | ✅ | `useDarkMode.applyHtmlAttribute` 同步设置 `color-scheme``darkreader-engine.ts` 与 README 均注明 DR 异步注入、闪白无法完全消除 |
| P2-2 文档 | ✅ | `AGENTS.md` / `CLAUDE.md` 已无旧策略残留(halo-core/overrides/Monaco/Tailwind 零命中);`scripts/README.md` 重写;README 按 Halo 官方风格重写 |
| P2-3 工具重复 | ⚠️ 部分 | 已删 `workplace/login_wait.py``workplace/scan_dark.py``fetch_bundle.py``probe_conflict.py``aggregate.py` 仍在(见遗留-3 |
| P2-4 前端单测 | ✅ | 新增 `ui/src/composables/__tests__/useDarkMode.spec.ts` 8 个用例,实测 8/8 通过 |
| P2-5 多标签同步 | ✅ | `storage` 事件监听已实现,并有 2 个测试覆盖(合法值同步 / 非法值忽略) |
| P2-6 可访问性 | ✅ | `ThemeToggle``<button>` + `aria-pressed` + `:focus-visible`;设置页改 `radiogroup` + `role=radio` + `aria-checked` |
| P3-1 后端日志 | ✅ | `@Slf4j` + `log.info`,替换 `System.out.println` |
| P3-2 vendored package.json | ✅ | 裁剪为 name/version/description/main/module/types/license 最小字段 |
| P3-3 完整性校验 | ✅ | `third-party/darkreader/SHA256SUMS` 新增,5 个文件哈希**全部实测匹配** |
| P3-4 遗留属性注释 | ✅ | `variables.css``useDarkMode.ts` 已注明 `data-halo-theme` 为兼容性遗留 |
| P3-5 i18n | ➖ 未处理 | 初审标注低优先级可暂缓,符合预期 |
| P3-6 plugin.yaml | ✅ | `spec.enabled: true → false`(合理默认) |
| 版本号 | ✅ | `gradle.properties` 1.0.5 + `DarkModePlugin @since 1.0.5` 同步 |
## 三、审查窗口实测验证(开发窗口无需重复)
- `vitest run`8/8 通过
- `vue-tsc --build`:通过
- `vite build`:通过(main.js 110.40KB / gzip 37.96KBstyle.css 4.11KB
- `prettier --check src`:通过(审查窗口已代跑 `prettier --write` 修复,见遗留-1
- `gradlew test`BUILD SUCCESSFUL(含 `:ui:assemble` + `:processUiResources` 全链路;沙箱外联网执行)
## 四、遗留问题(待开发窗口收尾)
### 遗留-1:prettier 格式化已修,改动未提交
- `d95d172` 中 5 个文件缺末尾换行、`variables.css` 列对齐不规范
- 审查窗口已执行 `prettier --write src`(纯格式化,无逻辑变化),涉及文件:
- `ui/src/components/ThemeToggle.vue`
- `ui/src/composables/__tests__/useDarkMode.spec.ts`
- `ui/src/composables/useDarkMode.ts`
- `ui/src/styles/index.css`
- `ui/src/styles/variables.css`
- `ui/src/views/SettingsView.vue`
- 当前这些改动在工作区**未提交**,需开发窗口提交
### 遗留-2:logo 改动未提交(需用户决策)
- `src/main/resources/logo.png`:被替换(35KB → 65KB)但未提交
- `src/main/resources/原版.png`:新增未跟踪文件(旧 logo 备份)
- 待决策:新 logo 是否随 1.0.5 入库?`原版.png` 删除 / 保留 / gitignore
### 遗留-3workplace/ 旧工具未清完(需用户确认删除)
- 仍保留:`fetch_bundle.py``probe_conflict.py``aggregate.py`
- 均为旧手工 CSS 工作流配套(抓线上 bundle / 探测覆盖冲突 / 聚合扫描结果),`scan_dark.py` 删除后基本失效
- 建议连同 `deployed-bundle.css``aggregate.txt``scan-results.json` 等产物一并清理收口到 `scripts/`
## 五、可选改进(非必须)
- `SettingsView.vue` radiogroup:各按钮可 Tab 聚焦,但未实现 ArrowUp/ArrowDown 方向键切换焦点(语义上 radiogroup 通常只保留一个 tab stop);不影响当前可用性
## 六、收尾动作清单
1. 提交 prettier 格式化改动(遗留-1
2. 与用户确认 logo / 原版.png 处置(遗留-2
3. 与用户确认 workplace/ 剩余工具删除(遗留-3
4. 可选:radiogroup 方向键导航(五)
5. 可选:推送后跑一次 `scripts/verify-toggle.py` 做真实环境回归(部署 1.0.5 后)
@@ -1,70 +0,0 @@
# 修改交接单:移除设置页键盘操作支持(2026-08-08)
> 审查窗口产出,**交付开发窗口执行**;审查窗口不修改代码。
> 本文档为**唯一交接文件**(已合并 `recheck-entry-2026-08-08.md` 的内容)。
> 用户决定:设置页的键盘操作支持"没必要",要求移除。
## 零、背景:1.0.6 已落实项(复查结论,无需重复处理)
提交 `394613f fix: 深色模式入口移至外观分组并移除侧边栏按钮(1.0.6)` 已复查通过:
- 菜单移入「外观」分组(`ui/src/index.ts` `group: 'interface'`),线上已生效
- 侧边栏注入按钮已移除(`injector.ts``ThemeToggle.vue` 已删除),线上 `.theme-toggle` 数量 = 0
- `verify-toggle.py` 已改为 localStorage + `StorageEvent` 驱动 + color-scheme 断言
- 文档已同步、版本号 1.0.6
- 构建验证:vitest 8/8 · vue-tsc · prettier · vite build · `gradlew build` 全绿
唯一遗留的 R-1(README 过时描述)由本文档第四节第 4 点一并处理。
## 一、需求描述
移除 `SettingsView.vue`(深色模式设置页)中为"键盘操作"特意实现的逻辑:
方向键切换选项、roving tabindex、ARIA 单选组语义、焦点环等。保留最简交互:点击三个选项按钮切换主题模式。
## 二、决策依据(用户补充)
- Halo 后台的官方页面与第三方插件页面均**没有方向键特殊交互的惯例**:按方向键上下键只用于页面滚动/翻页,没有其他特殊功能。
- 设置页单独实现"方向键切换主题选项"radiogroup 键盘导航)与平台整体交互习惯不一致,用户群体也不会预期方向键会改变主题选择,因此判定该键盘操作支持**不必要**,予以移除。
- 保留原生 `<button>` 即可:点击可用,浏览器内置的 Tab 聚焦 + Enter/空格 激活属基础可用性,非刻意实现,无需额外维护。
## 三、现状代码体现(`ui/src/views/SettingsView.vue`
| 行号 | 内容 | 作用 |
| --- | --- | --- |
| L10 | `import { computed, nextTick, ref } from 'vue'` | `nextTick`/`ref` 仅键盘导航使用 |
| L27 | `const optionEls = ref<HTMLButtonElement[]>([])` | 选项按钮 DOM 引用数组 |
| L29-L35 | `activeIndex` computed | 当前选中索引 |
| L37-L39 | `setOptionRef(el, index)` | 收集按钮 ref |
| L41-L45 | `focusOption(index)` | 方向键切换选中并聚焦 |
| L47-L61 | `onKeydown(event)` | ArrowUp/Down/Left/Right/Home/End 处理 |
| L81-L84 | `role="radiogroup"` `aria-label` `@keydown="onKeydown"` | 单选组容器语义 |
| L88-L104 | button 上的 `role="radio"` / `:aria-checked` / `:tabindex`roving/ `:ref` / `@click` | 单选按钮语义与焦点管理 |
| L162-L165 | `.dark-mode-settings__option:focus-visible` | 键盘焦点环(可选移除) |
连带:`README.md` L141 `- 侧边栏切换按钮与设置选项支持键盘操作`(R-1,侧边栏按钮已删,"键盘操作"整体废弃)。
## 四、修改建议
### 方案 A(推荐):移除键盘导航实现,保留简单按钮
`ui/src/views/SettingsView.vue`
1. **script**
- 删除 L27 `optionEls`、L29-L35 `activeIndex`、L37-L39 `setOptionRef`、L41-L45 `focusOption`、L47-L61 `onKeydown`
- L10 import 精简为 `import { computed } from 'vue'``nextTick``ref` 不再使用)
2. **template**
- L81-L84 删除 `role="radiogroup"``aria-label="主题模式"``@keydown="onKeydown"`
- L88-L104 按钮上删除 `role="radio"``:aria-checked``:tabindex``:ref`,仅保留 `type="button"``:class="{ 'is-active': ... }"``@click="setTheme(option.value)"``v-for``index` 不再需要,可去掉)
3. **style**(可选):删除 L162-L165 `:focus-visible` 规则;也可保留(无害,仅鼠标点击时不会触发)
4. **README.md**:删除 L141 整行(R-1 一并处理)
5. 构建回归:`cd ui && pnpm build``pnpm type-check``pnpm prettier``pnpm lint``pnpm test:unit`;根目录 `gradlew build`
### 方案 B(备选,不推荐):彻底无键盘语义
- 三个选项改回 `<div>` + `@click`(删除 button 全部键盘/语义属性)
- 需在样式补 `cursor: pointer`;可访问性退化,仅当明确要求时使用
## 五、注意
- 删除逻辑不涉及 `useDarkMode` / `darkreader-engine`,状态管理与主题切换不受影响。
- vitest 仅覆盖 `useDarkMode`,不受本次改动影响。
- 本次为前端 UI 变更,需重新构建部署后验证。
-81
View File
@@ -1,81 +0,0 @@
# 代码审查交接单 — halo-dark-mode-plugin2026-08-08
> 由审查窗口产出,供开发窗口执行。结论:**可合入,无 P1 阻断问题**。
> 审查基线:`HEAD = 363f30d`(工作区干净,已与 origin/main 同步)。
> 审查范围:Dark Reader 迁移相关 4 个提交(202ab53 / 0bb552b / 88a071f / 363f30d)。
## 一、开发窗口无需重复验证(审查窗口已实测)
- `vite build` 通过:main.js 109.96KBgzip 37.79KB)、style.css 3.76KBDark Reader 已打入 bundle`enable/disable` 具名导出解析正常
- `vue-tsc --build` 通过(exit 0
- `pnpm-lock.yaml``file:../third-party/darkreader` 依赖一致(含 malevic 0.20.2
- 构建产物 style.css 已无 `[data-halo-theme=dark]` 规则(属性属历史遗留)
- DR 产物头 `v4.9.129` 与 README 一致
## 二、待办清单(建议按 P2 → P3 顺序执行)
### P2-1 FOUC:深色用户刷新 console 会浅色闪烁
- 位置:`ui/src/darkreader-engine.ts`watch immediate 块,33-46 行)
- 原因:bundle 在 console 渲染后才执行,DR `enable()` 是异步分析+注入,无同步兜底
- 建议:`enable()` 前同步设 `document.documentElement.style.colorScheme = 'dark'`;或作为已知限制写进文档
### P2-2 本地项目文档滞后(与"纯 DR 策略"不符)
- `AGENTS.md` / `CLAUDE.md`:仍描述 `halo-core.css``overrides/`、Monaco 同步、FOUC 同步脚本——这些已不存在(`ui/src/styles/` 只剩 `index.css` + `variables.css`
- `scripts/README.md` 45-52 行:仍写"Monaco 日志查看器主题同步",实际已改为检查 `data-darkreader-mode`
- 建议:同步到纯 DR 现状
### P2-3 验证工具双份、命名不一致
- `scripts/`login-wait.py、scan-dark.py、verify-toggle.py
- `workplace/`login_wait.py、scan_dark.py、fetch_bundle.py、probe_conflict.py、aggregate.py
- 建议:统一收口到 `scripts/`,旧文件删除需用户书面确认
### P2-4 前端零单元测试
- `pnpm test:unit``vitest --passWithNoTests`,恒绿
- 建议:为 `ui/src/composables/useDarkMode.ts` 补 3-4 个用例,覆盖 `toggle()` 的 light/dark/auto 三种迁移与持久化回退
### P2-5 多标签页主题不同步
- 位置:`ui/src/composables/useDarkMode.ts`
- 建议:监听 `storage` 事件同步主题
### P2-6 可访问性
- `ui/src/components/ThemeToggle.vue` 10-15 行:div+@click,无 role/tabindex/键盘/aria-pressed
- `ui/src/views/SettingsView.vue` 36-41 行:选项 div+@click,同上
- 建议:补键盘与 ARIA(或改原生 radio 语义)
### P3-1 后端日志
- 位置:`src/main/java/run/halo/darkmode/DarkModePlugin.java` 24、29 行 `System.out.println`
- 建议:build.gradle 已有 Lombok,改 `@Slf4j` + `log.info`
### P3-2 vendored package.json 保留完整 devDependencies
- 位置:`third-party/darkreader/package.json`
- 建议:裁剪为 name/version/main/module/types/license 最小字段(`file:` 依赖不会安装 devDeps,但保持干净)
### P3-3 无 DR 升级/完整性机制
- 建议:README 或脚本记录 DR 产物 SHA-256,固定版本
### P3-4 data-halo-theme 属性 + variables.css 已成历史遗留
- 构建产物已无 `[data-halo-theme=dark]` 规则,属性无 CSS 消费方
- 建议:保留可以,但注释说明是兼容性遗留
### P3-5 文案硬编码中文,无 i18n
- 低优先级(目标用户中文),可暂缓
### P3-6 plugin.yaml `spec.enabled: true`
- 确认是否有意(Halo 通常安装后由用户启用)
## 三、完成后回归验证清单
```bash
cd ui
pnpm build # 构建通过
pnpm type-check # 类型检查通过
pnpm lint # 通过
# 真实环境验证(AGENTS.md 规定流程)
python scripts\verify-toggle.py # 三向断言全 PASS(属性/存储/Dark Reader 翻转)
```
## 四、无需改动的亮点(避免开发窗口误改)
- third-party/darkreader 只跟踪 5 个构建必需文件 + gitignore 白名单,LICENSE/版权头保留,合规 ✅
- style.css 从 ~500KB 降到 3.76KB,纯 DR 策略成立 ✅
- verify-toggle.py 的 darkreader 断言与策略一致 ✅
Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB