Files
testing-tool/.github/copilot-instructions.md
T
LingandRX d4c29a1bf4 Develop (#54)
* fix(ci): fix release workflow artifact upload issue

- Split artifact upload into 3 separate steps (Chrome, Firefox, Source)
- Update download steps to match new artifact names
- Add if-no-files-found: error for better error handling
- Fix Node.js 20 compatibility issue with upload-artifact@v4 glob pattern

* docs: 更新 AGENTS.md,添加测试工具和翻译文件结构说明

* docs: 更新 AGENTS.md,完善 CI 步骤和存储键名格式说明

* fix: remove unnecessary animations from StorageCleaner page

- Remove entrance animations (animate-in, fade-in, zoom-in) from all components
- Remove scale effect (active:scale-[0.99]) from clean button
- Remove transition effects (transition-all, transition-colors) from OptionItem, StorageOptionsGrid, AutoRefreshToggle
- Remove bouncing animation from error icon
- Remove pulsing animation from warning banner
- Keep animate-spin on button loading spinner as functional indicator

* fix: remove unnecessary animations from RightClickRestorer page

* fix: remove all unnecessary animations from all pages

- Remove dead code animations (animate-in, fade-in, slide-in, zoom-in, shake) from tailwindcss-animate plugin (not installed)
- Remove decorative transition effects (transition-all, transition-colors) from all page components
- Remove scale effects (active:scale-95) from buttons
- Remove bounce animations (animate-bounce) from icons
- Keep functional animate-spin on loading spinners as they provide essential loading feedback

Affected pages: Dashboard, Timestamp, Jwt, JsonTools, Base64Converter, HtmlToMarkdown, MarkdownToHtml, QrCode, TextStatistics

* docs: 添加项目文档(copilot-instructions、编码规范、各目录 README)

- 新增 .github/copilot-instructions.md:Copilot 指令文件,涵盖构建命令、CI 流水线、项目架构、关键规范
- 新增 .github/CODING_STANDARDS.md:完整的代码编写规范文档(TypeScript、React、样式、错误处理、命名、测试、Hook、i18n、存储等 12 个章节)
- 新增 11 个目录的 README.md:components、components/ui、config、entrypoints、i18n、lib、pages、providers、public、src、types、utils
- 更新 AGENTS.md:补充页面组件模式说明和 components/ui 目录

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(CopyButton): use shadcn buttonVariants instead of duplicated variant classes

Remove hand-written variantClasses/sizeClasses that duplicated shadcn's
buttonVariants. Now extends ButtonProps and imports buttonVariants from
components/ui/button for consistent styling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ErrorBoundary): use shadcn destructive tokens instead of hardcoded red colors

Replace hardcoded red-200/red-50/red-600 with border-destructive,
bg-destructive/5, text-destructive. Use variant='destructive' on
Button. Align error log styling with PageErrorBoundary.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(TextInputArea): replace hardcoded Chinese strings with i18n t() calls

Use existing translation keys for clear success, copy success, and
copy error messages instead of hardcoded Chinese text.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(QrCodePreview): use shadcn Button instead of manual button styles

Replace hand-written button class strings with shadcn Button component
using outline and default variants. Removes ~15 lines of duplicated
Tailwind classes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(GlobalSnackbar): clarify relationship with sonner toast

Add note explaining when to use GlobalSnackbar (provider context,
severity levels, custom positioning) vs sonner toast (simple one-off
messages).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ImageUploader): wrap handleClearFile with useCallback

Prevents unnecessary re-renders by memoizing the callback, consistent
with handleFileChange which already uses useCallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(SwitchButtonGroup): remove redundant containerPadding conditional

Both branches of the ternary returned 'p-1'. Inline the constant
directly into the className string.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(TopBar): add missing openExtensionPage import

The function was called in handleOpenInTab but never imported from
utils/chromeTabs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(TopBar): add selectedIndex boundary protection in keyboard nav

Guard against out-of-bounds access when search results change during
keyboard navigation. Check selectedIndex < totalItems before accessing
arrays.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(RouterContainer): remove unnecessary empty-dep useMemo

getEntryPointType() is a simple config getter. Replace useMemo(() => ..., [])
with a direct call — the empty dep array made the memo pointless.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(i18n): add missing translation keys for error boundaries and copy messages

Add keys for errorBoundary, pageErrorBoundary, router, and
messages.copyEmpty in both zh and en locales.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(CopyButton): replace hardcoded Chinese strings with i18n t() calls

Use useTranslation('common') for tooltip, copy empty/success/error
toast messages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ErrorBoundary): replace hardcoded Chinese with i18n via withTranslation HOC

Use react-i18next withTranslation HOC for class component to translate
title, description, and refresh button text.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(PageErrorBoundary): replace hardcoded Chinese with i18n via withTranslation HOC

Use react-i18next withTranslation HOC for class component to translate
title, description, and retry button text.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(RouterContainer): replace hardcoded Chinese with i18n t() calls

Use useTranslation('common') for 404 page title and description with
entryPointType interpolation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(pages): 修复 TypeScript 错误、硬编码字符串,移除死代码

- 修复 LiveClock.tsx CopyButton size 属性类型错误 (small → sm)
- 修复 ToolCard.tsx 紫色 RGB 值拼写错误 (147,51,2 purple → 147,51,232)
- 替换 4 处硬编码中文为 i18n 调用 (StorageCleaner, JsonTools)
- MarkdownToHtml 预览链接色改为 CSS 变量以支持暗黑模式
- 移除 Base64Converter 中已被 Base64ConverterSection 替代的死代码
  (FileMode.tsx, ImageMode.tsx 及其测试文件)
- 更新 README.md 移除对已删除文件的引用

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(CopyButton, LiveClock): 修改按钮大小为 'icon',移除硬编码颜色配置

* refactor(TextInputArea): 复用 CopyButton 组件替换内联复制逻辑

* fix(test): 修复 CopyButton mock 未捕获 clipboard writeText 异常

---------

Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-28 00:40:54 +08:00

6.5 KiB
Raw Blame History

Copilot 指令

基于 WXT 框架的浏览器扩展项目(React 19 + TypeScript),为开发者和测试人员提供效率工具:时间戳转换、存储清理、JWT 解析、JSON 工具、二维码、Base64、Markdown 等。

核心命令

npm run dev              # Chrome 开发模式(支持 HMR
npm run dev:firefox      # Firefox 开发模式
npm run build            # Chrome 生产构建
npm run build:firefox    # Firefox 生产构建
npm run zip              # 打包 Chrome 扩展(.output/*.zip
npm run zip:firefox      # 打包 Firefox 扩展
npm run lint             # ESLint 检查(--max-warnings=0
npm run typecheck        # TypeScript 类型检查(tsc --noEmit
npm run test             # 运行全部单元测试(vitest run)
npm run test:watch       # Vitest 监视模式
npm run test:coverage    # 带覆盖率的测试

运行单个测试文件:npx vitest run path/to/file.test.ts

修改 package.json 后需运行 npm install(会自动触发 postinstallwxt prepare 重新生成 .wxt/ 类型声明)。

CI 流水线(GitHub Actions

严格顺序门控,任一步骤失败则终止:

  1. setup — 安装依赖,缓存 node_modules
  2. linttypechecktest — 三者并行运行,全部通过才继续
  3. build — Chrome + Firefox 矩阵构建(仅当步骤 2 全部通过时执行)

Pre-commit 钩子(.husky/pre-commitlint-staged):

  1. 代码文件(*.{ts,tsx,js,jsx,mjs}):运行 eslint --fix --max-warnings=0
  2. 同一代码文件:运行 prettier --write
  3. 其他文件(*.{json,css,scss,md}):运行 prettier --write

项目架构

路由(不使用 React Router

路由完全通过 config/features.tsx 中的 FEATURES 数组管理。每个功能定义一个 key(类型为 types/storage.d.ts 中的 PageType)和三个懒加载组件,分别对应 popupsidepaneltab 三种渲染模式。providers/RouterProvider.tsx 中的 RouterProvider 根据存储状态渲染当前页面。

存在三套独立的路由作用域:app/popupRouteapp/sidepanelRouteapp/tabRoute,各自维护独立的可见页面列表和页面排序。

存储

所有 Chrome Storage 键必须在 types/storage.d.tsStorageSchema 中声明,键名使用 kebab-case 格式(如 app/currentRoute)。使用 utils/chromeStorage.ts 中的类型安全封装(storageUtil.get/set/remove)。

Router 同时使用 chrome.storage.local 持久化和 localStorage 快照来消除首屏闪烁。

扩展通信

使用 @webext-core/messaging。通信协议在 utils/messages.ts 中通过 ProtocolMap 定义。使用该模块导出的 sendMessage / onMessage,不要直接使用原生 chrome.runtime.sendMessage

页面组件模式

功能页面遵循 UI + Hook 分离 模式:

pages/FeatureName/
├── index.tsx              # UI 组件(纯展示,使用 shadcn/ui 组件)
├── useFeatureName.ts      # 业务逻辑 Hook(状态管理 + 转换逻辑)
└── constants.ts           # 常量定义(可选)
  • 页面组件调用 useLazyTranslation('featureName') 获取翻译函数
  • Hook 负责所有状态管理,通过返回值暴露给页面
  • 子组件可进一步拆分(如 LiveClock.tsxResultView.tsx

新功能开发清单

  1. types/storage.d.tsPageType 联合类型中添加新成员
  2. config/features.tsxFEATURES 数组中添加配置(key、翻译键、图标、三种渲染模式组件)
  3. pages/ 目录创建页面组件(懒加载):
    • index.tsx — 使用 useLazyTranslation 的 UI 组件
    • useFeatureName.ts — 业务逻辑 Hook
    • constants.ts — 常量(可选)
  4. i18n/locales/{zh,en}/features.json 添加翻译(复杂功能可新建独立 JSON 文件)
  5. 如需新权限,更新 wxt.config.tsmanifest.permissions
  6. 添加对应的单元测试

关键规范

完整的代码编写规范详见 CODING_STANDARDS.md

浏览器 API

始终使用 wxt/browser 导出的 browser 对象,而非原生 chrome API,以确保跨浏览器兼容性。

路径别名

@/ 映射到项目根目录(已在 tsconfig 和 vitest.config 中配置)。跨目录导入使用 @/ 绝对别名,同目录导入使用 ./ 相对路径。

UI 组件

  • 使用 components/ui/ 下的 shadcn/ui 组件(button、dialog、select、input 等)
  • 图标:lucide-react
  • 样式:Tailwind CSS + @/lib/utils 中的 cn() 工具函数(clsx + tailwind-merge
  • 主题:使用 shadcn/ui 语义化 tokenbg-backgroundtext-foregroundborder-border 等),禁止硬编码颜色

代码分割

wxt.config.ts 通过 manualChunksForHtmlOnly() 自动分组 vendor 依赖(vendor-react、vendor-i18n、vendor-qr、vendor-dnd、vendor-markdown),无需手动配置。

代码风格

  • 禁止使用 any(测试文件除外)
  • 未使用的变量/参数:使用 _ 前缀(如 _unused
  • Prettier100 字符宽、单引号、尾逗号 all、LF 换行
  • ESLint 使用 typescript-eslintprojectService: true
  • 导出模式:页面组件 default export,工具函数/Hook 命名导出,UI 组件 forwardRef + 命名导出

测试

  • 环境:jsdom
  • 全局变量:vitest/globalsdescribe、it、expect 等无需导入)
  • Setup 文件:vitest.setup.ts 自动 mock 以下内容:
    • chrome.* / browser.* APIstorage、tabs、runtime、cookies 等)
    • react-i18next(返回 key 作为翻译)
    • @/utils/useLazyTranslation(返回 ns:key 格式)
    • window.matchMedia
  • 测试文件命名:__tests__/*.test.{ts,tsx}*.test.{ts,tsx}
  • 使用 vi.mock() 进行模块级 mock;避免重复 mock vitest.setup.ts 中已有的内容
  • 测试工具:@testing-library/react + @testing-library/user-event

国际化(i18n

  • 命名空间:common(默认)、features
  • 翻译键格式:namespace:key(如 features:timestamp.title
  • 语言:zh(默认)、en
  • 翻译文件:i18n/locales/{zh,en}/{common,features}.json + 各功能独立 JSON 文件
  • 使用 useLazyTranslation Hook 加载功能专属翻译
  • 回退策略:缺失的翻译键回退到 zh,若仍缺失则返回占位格式 namespace:key

WXT 生成文件

  • .wxt/ 目录由 postinstallwxt prepare)自动生成,包含 TypeScript 类型声明和扩展 tsconfig
  • 生产构建输出到 .output/ 目录
  • tsconfig.json 继承自 ./.wxt/tsconfig.json