From c8ad2a2283bc1e5716a855a8a74734ebd25d5209 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 23 May 2026 14:37:31 +0800 Subject: [PATCH 01/71] 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 --- .github/workflows/release.yml | 40 +++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index df1693e..ff9753f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -126,13 +126,29 @@ jobs: # 💡 核心自愈补丁:显式将 .output 下打包出的真实生产绝对路径文件, # 稳固地上存至 GitHub 的常驻产物箱中进行安全物理隔离,防范后期发布网络崩溃导致产物蒸发! - - name: Upload Extension Artifacts + - name: Upload Chrome Extension uses: actions/upload-artifact@v4 with: - name: extension-zips - path: | - .output/*.zip + name: chrome-extension + path: .output/testing-tools-*.zip retention-days: 7 + if-no-files-found: error + + - name: Upload Firefox Extension + uses: actions/upload-artifact@v4 + with: + name: firefox-extension + path: .output/testing-tools-*-firefox.zip + retention-days: 7 + if-no-files-found: error + + - name: Upload Source Code + uses: actions/upload-artifact@v4 + with: + name: source-code + path: .output/testing-tools-*-sources.zip + retention-days: 7 + if-no-files-found: error # ── Phase 4: 独立中央签发发布(Atomic Release Publisher) ─────────────────── release: @@ -144,10 +160,22 @@ jobs: uses: actions/checkout@v4 # 💡 独立下载打包完好的绝对产物包 - - name: Download Extension Artifacts + - name: Download Chrome Extension uses: actions/download-artifact@v4 with: - name: extension-zips + name: chrome-extension + path: release-artifacts + + - name: Download Firefox Extension + uses: actions/download-artifact@v4 + with: + name: firefox-extension + path: release-artifacts + + - name: Download Source Code + uses: actions/download-artifact@v4 + with: + name: source-code path: release-artifacts - name: Extract version from tag From 61e741f1981ccc18bb1202e1ac7555664adf7c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= Date: Mon, 25 May 2026 19:15:31 +0800 Subject: [PATCH 02/71] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20AGENTS.md?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E6=B5=8B=E8=AF=95=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E5=92=8C=E7=BF=BB=E8=AF=91=E6=96=87=E4=BB=B6=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index d2f240d..289a697 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,7 +54,7 @@ i18n/locales/{zh,en}/ # 国际化资源 (common.json, features.json 及各功 ## 关键架构决策 **路由**: 不使用 React Router。通过 `config/features.tsx` 的 `FEATURES` 数组管理,`RouterProvider` 根据 `PageType` -渲染对应组件。支持三种渲染模式: popup / sidepanel / tab。 +渲染对应组件。支持三种渲染模式: popup / sidepanel / tab。每种模式有独立的路由和可见页面配置。 **存储**: 所有 Chrome Storage 键必须在 `types/storage.d.ts` 的 `StorageSchema` 中定义。使用 `utils/chromeStorage.ts` 及其 Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照以消除首屏闪烁。 @@ -75,13 +75,20 @@ Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照 - `@/utils/useLazyTranslation` (返回 `ns:key` 格式翻译) - `window.matchMedia` - 测试文件命名: `__tests__/*.test.{ts,tsx}` 或 `*.test.{ts,tsx}` +- Mock 模式: 使用 `vi.mock()` 进行模块级 mock,避免在测试文件中重复 mock 代码 +- 测试工具: `@testing-library/react` + `@testing-library/user-event` 进行组件测试 ## i18n - 命名空间: `common` (默认), `features` - 翻译键格式: `namespace:key` (如 `features:timestamp.title`) - 语言: `zh` (默认), `en` +- 翻译文件结构: + - `i18n/locales/{zh,en}/common.json` - 全局通用翻译 + - `i18n/locales/{zh,en}/features.json` - 功能模块标题和描述 + - `i18n/locales/{zh,en}/{功能名}.json` - 各功能独立翻译(如 timestamp.json, storageCleaner.json 等) - 添加新翻译: 编辑 `i18n/locales/{zh,en}/{common,features}.json` 及对应功能独立 JSON +- 使用 `useLazyTranslation` hook 加载功能独立翻译,返回 `ns:key` 格式 ## 新功能开发清单 @@ -97,6 +104,8 @@ Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照 - 禁止使用 `any` (测试文件除外) - 未使用变量/参数: 使用 `_` 前缀 (如 `_unused`) - 样式: 使用 Tailwind CSS + shadcn/ui (通过 `className` 和 `cn()` 工具) +- UI 组件: 优先使用 `components/ui/` 下的 shadcn/ui 组件 (button, dialog, select 等) +- 图标: 使用 `lucide-react` 图标库 - 格式: Prettier (`.prettierrc`: 100 字符宽, 单引号, 尾逗号 all, LF 换行) - ESLint 使用 `typescript-eslint` 的 `projectService: true`(无需手动维护 project 路径) @@ -109,3 +118,6 @@ Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照 - TypeScript: ^5.9.3 - Vitest: ^4.1.7 - i18next: ^26.2.0 +- @dnd-kit (拖拽排序) +- marked (Markdown 解析) +- qrious + qr-scanner (二维码生成与解析) From dec43f89da52ed4f666ff6fa964e37e2b9662c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= Date: Mon, 25 May 2026 19:21:22 +0800 Subject: [PATCH 03/71] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=20AGENTS.md?= =?UTF-8?q?=EF=BC=8C=E5=AE=8C=E5=96=84=20CI=20=E6=AD=A5=E9=AA=A4=E5=92=8C?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E9=94=AE=E5=90=8D=E6=A0=BC=E5=BC=8F=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 289a697..f28e8bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md -WXT 浏览器扩展项目 (React 19 + TypeScript)。 +WXT 浏览器扩展项目 (React 19 + TypeScript)。提供时间戳转换、存储清理、JWT 解析、JSON 工具、二维码、Base64、Markdown 等测试效率工具。 ## 核心命令 @@ -22,14 +22,17 @@ npm run test:coverage # 带覆盖率的测试 ## 验证流程 -CI 执行顺序: `setup → lint/typecheck/test(并行) → build` (build 依赖前三者)。 +CI 步骤(严格顺序,任一步骤失败则停止并标记 CI 失败): -Pre-commit hook (`.husky/pre-commit` 调用 `lint-staged`): +1. `setup`(安装依赖、`wxt prepare`) +2. 并行运行 `lint`、`typecheck`、`test`(三者全部通过才继续) +3. `build`(仅当步骤 2 全部成功时执行) -- 代码文件 (`*.{ts,tsx,js,jsx,mjs}`): `eslint --fix --max-warnings=0 --no-warn-ignored` → `prettier --write` -- 其他文件 (`*.{json,css,scss,md}`): `prettier --write` +Pre-commit hook(`.husky/pre-commit` 调用 `lint-staged`,任一步骤返回非零则终止提交): -提交前确保 `lint` 和 `typecheck` 通过。 +1. 代码文件 (`*.{ts,tsx,js,jsx,mjs}`):运行 `eslint --fix --max-warnings=0 --no-warn-ignored`;若失败则终止并报告错误 +2. 同一代码文件:运行 `prettier --write` +3. 其他文件 (`*.{json,css,scss,md}`):运行 `prettier --write` ## WXT 生成文件 @@ -54,10 +57,12 @@ i18n/locales/{zh,en}/ # 国际化资源 (common.json, features.json 及各功 ## 关键架构决策 **路由**: 不使用 React Router。通过 `config/features.tsx` 的 `FEATURES` 数组管理,`RouterProvider` 根据 `PageType` -渲染对应组件。支持三种渲染模式: popup / sidepanel / tab。每种模式有独立的路由和可见页面配置。 +渲染对应组件。支持三种渲染模式:popup(弹窗)、sidepanel(侧边栏)和 browser-tab(浏览器新标签页,通过 `open_in_tab` 打开)。 +每种模式有独立的路由和可见页面配置。 -**存储**: 所有 Chrome Storage 键必须在 `types/storage.d.ts` 的 `StorageSchema` 中定义。使用 `utils/chromeStorage.ts` 及其 -Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照以消除首屏闪烁。 +**存储**: 所有 Chrome Storage 键必须在 `types/storage.d.ts` 的 `StorageSchema` 中定义,键名使用 kebab-case 格式(如 `app/currentRoute`)。 +使用 `utils/chromeStorage.ts` 及其 Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照以消除首屏闪烁。 +修改 StorageSchema 时,必须在 `utils/chromeStorage.ts` 添加版本迁移函数,并在测试中覆盖迁移场景。 **通信**: 使用 `@webext-core/messaging`,协议定义在 `utils/messages.ts`。 @@ -89,6 +94,7 @@ Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照 - `i18n/locales/{zh,en}/{功能名}.json` - 各功能独立翻译(如 timestamp.json, storageCleaner.json 等) - 添加新翻译: 编辑 `i18n/locales/{zh,en}/{common,features}.json` 及对应功能独立 JSON - 使用 `useLazyTranslation` hook 加载功能独立翻译,返回 `ns:key` 格式 +- 回退策略: 当翻译 key 在目标语言缺失时,回退到默认语言 `zh`;若默认语言也缺失,返回占位格式 `namespace:key` 并在开发模式下记录 warning ## 新功能开发清单 From e5ae802ab21054abb6bfac230d1b3951db7bc10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= Date: Mon, 25 May 2026 20:11:39 +0800 Subject: [PATCH 04/71] 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 --- pages/StorageCleaner/AutoRefreshToggle.tsx | 2 +- pages/StorageCleaner/CleaningResult.tsx | 5 +---- pages/StorageCleaner/ErrorDisplay.tsx | 4 ++-- pages/StorageCleaner/OptionItem.tsx | 4 ++-- pages/StorageCleaner/StorageCleanerConfirm.tsx | 3 +-- pages/StorageCleaner/StorageOptionsGrid.tsx | 4 ++-- pages/StorageCleaner/index.tsx | 8 ++++---- 7 files changed, 13 insertions(+), 17 deletions(-) diff --git a/pages/StorageCleaner/AutoRefreshToggle.tsx b/pages/StorageCleaner/AutoRefreshToggle.tsx index a062723..dec8af4 100644 --- a/pages/StorageCleaner/AutoRefreshToggle.tsx +++ b/pages/StorageCleaner/AutoRefreshToggle.tsx @@ -48,7 +48,7 @@ export default function AutoRefreshToggle({ return (
+
{/* 2. 彻底重构容器类名结构: - 成功状态:采用 Tailwind 官方推荐的 emerald 体系,利用 /10 (10% 透明度) 和 /20 (边框)。 - 失败状态:完全放权给标准的 border-destructive/20 和 bg-destructive/5。 diff --git a/pages/StorageCleaner/ErrorDisplay.tsx b/pages/StorageCleaner/ErrorDisplay.tsx index 4bf067d..e653ada 100644 --- a/pages/StorageCleaner/ErrorDisplay.tsx +++ b/pages/StorageCleaner/ErrorDisplay.tsx @@ -13,7 +13,7 @@ export default function ErrorDisplay({ error, className, ...props }: ErrorDispla // 1. 精简层级:单层外壳直接搞定居中、响应式高度与外部类名扩展
{/* 3. 图标与主要错误信息全面对接 text-destructive 语义色 */} -
+
diff --git a/pages/StorageCleaner/OptionItem.tsx b/pages/StorageCleaner/OptionItem.tsx index ba82ff5..db4b12d 100644 --- a/pages/StorageCleaner/OptionItem.tsx +++ b/pages/StorageCleaner/OptionItem.tsx @@ -29,7 +29,7 @@ export default function OptionItem({ // 3. 跨越级交互升级:将外部容器升级为一个高度敏感的可点击 Tab 热区 onClick={onChange} className={cn( - 'flex justify-between items-center py-2.5 px-3.5 rounded-xl border cursor-pointer select-none transition-all duration-200', + 'flex justify-between items-center py-2.5 px-3.5 rounded-xl border cursor-pointer select-none', // 4. 彻底抛弃硬编码黄底: // - 选中时:使用 bg-primary/5 (系统主色超淡叠加) 配合标准 border-primary/30。 // - 未选中时:保持透明 border-transparent,悬停呈现 bg-muted。 @@ -45,7 +45,7 @@ export default function OptionItem({
diff --git a/pages/StorageCleaner/StorageCleanerConfirm.tsx b/pages/StorageCleaner/StorageCleanerConfirm.tsx index 1d736a4..190b405 100644 --- a/pages/StorageCleaner/StorageCleanerConfirm.tsx +++ b/pages/StorageCleaner/StorageCleanerConfirm.tsx @@ -42,7 +42,6 @@ export function StorageCleanerConfirm({ {/* 头部标题区域 */} @@ -72,7 +71,7 @@ export function StorageCleanerConfirm({
{/* 风险警告横幅 */} -
+
{t('storageCleaner:irreversible')} diff --git a/pages/StorageCleaner/StorageOptionsGrid.tsx b/pages/StorageCleaner/StorageOptionsGrid.tsx index 4e84d6f..699913d 100644 --- a/pages/StorageCleaner/StorageOptionsGrid.tsx +++ b/pages/StorageCleaner/StorageOptionsGrid.tsx @@ -46,7 +46,7 @@ export default function StorageOptionsGrid({ return (