diff --git a/docs/superpowers/plans/2026-03-20-preview-panel-option-a-plan.md b/docs/superpowers/plans/2026-03-20-preview-panel-option-a-plan.md new file mode 100644 index 0000000..f7268ff --- /dev/null +++ b/docs/superpowers/plans/2026-03-20-preview-panel-option-a-plan.md @@ -0,0 +1,342 @@ +# Preview Panel Option A Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 在不改变三栏布局和执行流程的前提下,把右侧预览列表升级为“旧名 -> 新名”的双行映射视图,提升执行前确认效率。 + +**Architecture:** 改动集中在 `virtual-preview-list` 展示层,继续复用现有 `PreviewItem` 结构,不修改 `file-selector-panel` 的预览数据协议。状态、失败原因和冲突提示由列表组件在渲染时根据 `item.file.name`、`item.newName`、`item.conflict`、`item.done`、`item.error` 推导生成;`preview-panel` 仍只负责统计区和外层容器。 + +**Tech Stack:** TypeScript、Lit Web Components、Vitest、现有 i18n 资源 + +--- + +## 文件结构 + +### 需要修改 +- `src/content/components/virtual-preview-list.ts` + - 将单行新文件名渲染改成双行映射结构。 + - 把状态标签从单独徽标调整为与旧文件名同层展示。 + - 为冲突项/失败项补充可读的辅助说明。 + - 保持虚拟列表切换逻辑与长列表性能不变。 +- `src/locales/zh_CN.json` +- `src/locales/zh_TW.json` +- `src/locales/en.json` + - 仅在实现时确认现有文案不足的情况下补充预览项辅助说明文案。 + +### 需要新增 +- `tests/unit/virtual-preview-list.test.ts` + - 覆盖双行映射结构、状态标签、冲突/失败说明、长文件名 title 合约。 + +### 需要验证但通常不必修改 +- `src/content/components/preview-panel.ts` + - 确认统计区和列表容器不需要跟随变更。 +- `src/types/file-selector.ts` + - 确认 `PreviewItem` 结构已足够承载新视图。 + +--- + +### Task 1: 先为双行映射视图补失败测试 + +**Files:** +- Create: `tests/unit/virtual-preview-list.test.ts` +- Modify: `src/content/components/virtual-preview-list.ts` + +- [ ] **Step 1: 写失败测试,锁定“旧名 -> 新名”最小契约** + +```ts +it('renders original name and new name for a pending preview item', async () => { + const element = document.createElement('virtual-preview-list') as VirtualPreviewListElement; + element.items = [ + { + file: { id: '1', name: 'old-name.mkv', ext: 'mkv', isDir: false, size: 1, mtime: Date.now() }, + newName: 'new-name.mkv', + conflict: false, + }, + ]; + + document.body.appendChild(element); + await element.updateComplete; + + expect(element.shadowRoot?.textContent).toContain('old-name.mkv'); + expect(element.shadowRoot?.textContent).toContain('new-name.mkv'); +}); +``` + +- [ ] **Step 2: 运行测试,确认当前实现先失败** + +Run: +```bash +npx vitest run tests/unit/virtual-preview-list.test.ts +``` + +Expected: +- FAIL,因为当前组件只渲染新文件名,看不到旧文件名。 + +- [ ] **Step 3: 最小实现双行渲染结构** + +Implementation notes: +- 在 `renderPreviewItem()` 中新增“旧文件名行”和“新文件名行”。 +- 旧文件名使用 `item.file.name`。 +- 新文件名继续使用 `item.newName`。 +- 保留现有 `title` 以支持完整名称悬浮查看。 + +- [ ] **Step 4: 再跑测试,确认最小映射能力已成立** + +Run: +```bash +npx vitest run tests/unit/virtual-preview-list.test.ts +``` + +Expected: +- PASS。 + +- [ ] **Step 5: 提交本任务** + +```bash +git add tests/unit/virtual-preview-list.test.ts src/content/components/virtual-preview-list.ts +git commit -m "test: cover preview list old-to-new mapping" +``` + +--- + +### Task 2: 为状态项和辅助说明补测试,再完善展示逻辑 + +**Files:** +- Modify: `tests/unit/virtual-preview-list.test.ts` +- Modify: `src/content/components/virtual-preview-list.ts` +- Modify: `src/locales/zh_CN.json` +- Modify: `src/locales/zh_TW.json` +- Modify: `src/locales/en.json` + +- [ ] **Step 1: 写失败测试,描述冲突项和失败项的展示预期** + +```ts +it('renders conflict summary next to the original name row', async () => { + // conflict: true + // 断言冲突徽标存在,且旧文件名和新文件名同时可见 +}); + +it('renders a user-friendly failure message for extract errors', async () => { + // error: 'extract_episode_not_found' + // 断言显示本地化错误文案,而不是仅有原始错误码 +}); + +it('renders pending badge only when showStatus is true', async () => { + // showStatus false/true 分别断言 pending 徽标行为 +}); +``` + +- [ ] **Step 2: 运行测试,确认现有实现不满足新行为** + +Run: +```bash +npx vitest run tests/unit/virtual-preview-list.test.ts +``` + +Expected: +- FAIL,原因可能包括: + - 状态标签位置和结构不符合预期 + - 辅助说明未渲染 + - 原始错误码未转译成用户可读文案 + +- [ ] **Step 3: 实现状态重排和说明文案** + +Implementation notes: +- 将状态标签放在旧文件名同行右侧。 +- `conflict` 项显示固定说明,如“与现有文件名重复”或等效文案。 +- `error` 项优先尝试 `I18nService.t('error_' + item.error)`,找不到再回退到原始错误文本。 +- 若现有 locale 缺少冲突说明文案,再补充最少的新 key。 +- 保持 `showStatus` 逻辑不变,只改变展示位置与结构。 + +- [ ] **Step 4: 运行测试,确认状态与说明文案通过** + +Run: +```bash +npx vitest run tests/unit/virtual-preview-list.test.ts +``` + +Expected: +- PASS。 + +- [ ] **Step 5: 提交本任务** + +```bash +git add tests/unit/virtual-preview-list.test.ts src/content/components/virtual-preview-list.ts src/locales/zh_CN.json src/locales/zh_TW.json src/locales/en.json +git commit -m "feat: show original-to-new mapping in preview items" +``` + +--- + +### Task 3: 锁定样式合约,避免新结构破坏列表可读性 + +**Files:** +- Modify: `tests/unit/virtual-preview-list.test.ts` +- Modify: `src/content/components/virtual-preview-list.ts` + +- [ ] **Step 1: 写样式/结构测试,锁定层级和截断约束** + +```ts +it('keeps original name visually secondary and new name visually prominent', () => { + const cssText = VirtualPreviewList.styles.cssText; + expect(cssText).toContain('.file-name.old'); + expect(cssText).toContain('.file-name.new'); + expect(cssText).toContain('text-overflow: ellipsis;'); +}); + +it('keeps preview items compatible with virtualized long lists', () => { + const cssText = VirtualPreviewList.styles.cssText; + expect(cssText).toContain('.preview-item'); + expect(cssText).toContain('.preview-content'); +}); +``` + +- [ ] **Step 2: 运行测试,确认样式契约尚未全部满足** + +Run: +```bash +npx vitest run tests/unit/virtual-preview-list.test.ts +``` + +Expected: +- FAIL,因为新类名或截断样式尚未完整落地。 + +- [ ] **Step 3: 完成样式收尾** + +Implementation notes: +- 为旧名/新名分别设置类名和视觉层级。 +- 为两行文件名保持单行截断。 +- 保持冲突/失败/成功背景与边框系统延续现有主题变量。 +- 不新增会影响虚拟列表布局计算的绝对定位复杂结构。 + +- [ ] **Step 4: 再跑测试,确认样式合约通过** + +Run: +```bash +npx vitest run tests/unit/virtual-preview-list.test.ts +``` + +Expected: +- PASS。 + +- [ ] **Step 5: 提交本任务** + +```bash +git add tests/unit/virtual-preview-list.test.ts src/content/components/virtual-preview-list.ts +git commit -m "style: refine preview item hierarchy for option a" +``` + +--- + +### Task 4: 做集成验证,确认预览面板主链路未回归 + +**Files:** +- Modify: `tests/unit/virtual-preview-list.test.ts` +- Verify: `src/content/components/preview-panel.ts` +- Verify: `src/content/components/file-selector-panel.ts` + +- [ ] **Step 1: 补一条轻量集成测试,验证 `preview-panel` 仍能透传数据到列表** + +```ts +it('renders preview items through preview-panel without changing counters', async () => { + // 挂载 preview-panel,传入含 old/new/conflict/error 的 items + // 断言统计区仍正确,列表中可见旧名与新名 +}); +``` + +- [ ] **Step 2: 运行聚焦测试,确认先看到真实集成结果** + +Run: +```bash +npx vitest run tests/unit/virtual-preview-list.test.ts +``` + +Expected: +- 若 `preview-panel` 不需要改动,则测试应直接 PASS; +- 若透传或 DOM 结构假设有误,则在这里修正测试或最小代码。 + +- [ ] **Step 3: 只在必要时做最小集成修复** + +Implementation notes: +- 优先不改 `preview-panel.ts`。 +- 若测试显示统计区布局受影响,再做最小 CSS 或 DOM 修正。 + +- [ ] **Step 4: 运行本轮相关测试和基础校验** + +Run: +```bash +npx vitest run tests/unit/virtual-preview-list.test.ts tests/unit/file-selector-panel-conflict.test.ts tests/unit/file-selector-panel-diagnostic-feedback.test.ts tests/unit/file-selector-panel-undo.test.ts +npx tsc --noEmit +``` + +Expected: +- 所有测试 PASS。 +- TypeScript 检查通过。 + +- [ ] **Step 5: 提交本任务** + +```bash +git add tests/unit/virtual-preview-list.test.ts src/content/components/virtual-preview-list.ts src/content/components/preview-panel.ts +git commit -m "test: verify preview panel option a integration" +``` + +--- + +### Task 5: 做最终回归验证并整理交付 + +**Files:** +- Verify only: `src/content/components/virtual-preview-list.ts` +- Verify only: `src/content/components/preview-panel.ts` +- Verify only: `docs/superpowers/specs/2026-03-20-preview-panel-option-a-design.md` + +- [ ] **Step 1: 运行完整单测回归** + +Run: +```bash +npx vitest run +``` + +Expected: +- 全部单测 PASS。 + +- [ ] **Step 2: 运行 lint 和类型检查** + +Run: +```bash +npm run lint +npm run typecheck +``` + +Expected: +- 无 lint 错误。 +- 无 type error。 + +- [ ] **Step 3: 如有必要,运行一次聚焦 E2E 验证预览未破坏主流程** + +Run: +```bash +npx playwright test tests/e2e/batch-rename.spec.ts +``` + +Expected: +- 预览面板主流程可打开、可展示、执行前无明显回归。 + +- [ ] **Step 4: 更新进度文件并整理交付说明** + +Implementation notes: +- 更新 `task_plan.md`、`findings.md`、`progress.md` 中的实施状态。 +- 汇总本次改动点、测试结果和剩余风险。 + +- [ ] **Step 5: 提交最终任务** + +```bash +git add src tests task_plan.md findings.md progress.md +git commit -m "feat: upgrade preview panel to option a mapping view" +``` + +--- + +## 备注 + +- 本计划默认优先走最小可用版本,不额外引入“剧集提取摘要标签”这类增强信息,除非实现时确认不会增加明显噪声。 +- 若 Task 2 中发现现有 locale 已足够支撑失败原因转译,则不必机械新增 i18n key;保持 DRY。 +- 如果后续用户希望在预览项上增加“从该项填充剧名/锚点”的交互,应单独立 spec 和 plan,不应在本计划中顺手扩 scope。