cloud-drive-renamer/docs/superpowers/plans/2026-03-20-preview-panel-option-a-plan.md

10 KiB

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: 写失败测试,锁定“旧名 -> 新名”最小契约

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:

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:

npx vitest run tests/unit/virtual-preview-list.test.ts

Expected:

  • PASS。

  • Step 5: 提交本任务

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: 写失败测试,描述冲突项和失败项的展示预期

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:

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:

npx vitest run tests/unit/virtual-preview-list.test.ts

Expected:

  • PASS。

  • Step 5: 提交本任务

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: 写样式/结构测试,锁定层级和截断约束

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:

npx vitest run tests/unit/virtual-preview-list.test.ts

Expected:

  • FAIL,因为新类名或截断样式尚未完整落地。

  • Step 3: 完成样式收尾

Implementation notes:

  • 为旧名/新名分别设置类名和视觉层级。

  • 为两行文件名保持单行截断。

  • 保持冲突/失败/成功背景与边框系统延续现有主题变量。

  • 不新增会影响虚拟列表布局计算的绝对定位复杂结构。

  • Step 4: 再跑测试,确认样式合约通过

Run:

npx vitest run tests/unit/virtual-preview-list.test.ts

Expected:

  • PASS。

  • Step 5: 提交本任务

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 仍能透传数据到列表

it('renders preview items through preview-panel without changing counters', async () => {
  // 挂载 preview-panel,传入含 old/new/conflict/error 的 items
  // 断言统计区仍正确,列表中可见旧名与新名
});
  • Step 2: 运行聚焦测试,确认先看到真实集成结果

Run:

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:

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: 提交本任务

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:

npx vitest run

Expected:

  • 全部单测 PASS。

  • Step 2: 运行 lint 和类型检查

Run:

npm run lint
npm run typecheck

Expected:

  • 无 lint 错误。

  • 无 type error。

  • Step 3: 如有必要,运行一次聚焦 E2E 验证预览未破坏主流程

Run:

npx playwright test tests/e2e/batch-rename.spec.ts

Expected:

  • 预览面板主流程可打开、可展示、执行前无明显回归。

  • Step 4: 更新进度文件并整理交付说明

Implementation notes:

  • 更新 task_plan.md、findings.md、progress.md 中的实施状态。

  • 汇总本次改动点、测试结果和剩余风险。

  • Step 5: 提交最终任务

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。