Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a0c8ba850 | |||
| 6c7b7294b4 | |||
| a2e07a9115 | |||
| 2464a5fea6 | |||
| 78f5292b00 | |||
| 5a2589cc57 | |||
| 64db60d75e | |||
| 7090ba3251 | |||
| 31b6edf06b | |||
| a374bf456d | |||
| ea10b27c14 | |||
| 40443c8fc5 | |||
| ee4a45630e | |||
| 4a423889d9 | |||
| ee78e314d3 | |||
| ee77c97b56 | |||
| aa8b84af50 | |||
| 707f17ac84 | |||
| af0b2d8ed1 | |||
| 49e7f1ea55 | |||
| 1a92e88934 | |||
| 2b84eda24f | |||
| 7f22b95aa2 | |||
| 9990d86270 | |||
| ea0552e8aa | |||
| c40b66b703 | |||
| f55fd4338d | |||
| 27c1cf37e3 | |||
| 2520d72eb1 | |||
| 4d038203a7 | |||
| 2c45ddada9 | |||
| a0b1ade662 | |||
| d964e27c9d | |||
| 10644fae95 | |||
| 77f722b575 |
+38
-42
@@ -285,7 +285,8 @@ import { useThemeMode } from '@/providers/ThemeModeProvider';
|
||||
import { FeatureConfig, FEATURES } from '@/config/features';
|
||||
import { storageUtil } from '@/utils/chromeStorage';
|
||||
// 5. i18n
|
||||
import { useI18n } from '@/utils/chromeI18n';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { normalizeLanguage, SUPPORTED_LANGUAGES } from '@/i18n';
|
||||
// 6. 本地组件
|
||||
import TextMode from './TextMode';
|
||||
import { ZONES } from './constants';
|
||||
@@ -294,7 +295,7 @@ import SwitchButtonGroup from '@/components/SwitchButtonGroup';
|
||||
import { Button } from '@/components/ui/button';
|
||||
// 8. 工具函数 / Hook
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useStorageState } from '@/utils/useStorageState';
|
||||
import { useLazyTranslation } from '@/utils/useLazyTranslation';
|
||||
// 9. 类型
|
||||
import type { PageType, StorageSchema } from '@/types/storage';
|
||||
```
|
||||
@@ -400,22 +401,22 @@ className={cn(
|
||||
|
||||
## 5. 错误处理
|
||||
|
||||
### 5.1 工具函数:可恢复错误返回可判断结果
|
||||
### 5.1 工具函数:结果对象模式
|
||||
|
||||
可恢复的解析/校验错误应返回可判断的结果,避免工具层直接弹 Toast。确需保留底层异常的函数
|
||||
(如 `formatJson` / `minifyJson`)必须在页面 Hook 或 UI 层捕获并转换为用户提示:
|
||||
工具函数**不抛异常**,返回包含 `hasError` 和 `error` 字段的结果对象:
|
||||
|
||||
```typescript
|
||||
// ✅ 可恢复校验返回错误消息,调用方据此展示 UI
|
||||
export function validateJson(text: string): string | null {
|
||||
if (!text.trim()) {
|
||||
return null;
|
||||
}
|
||||
// ✅ 结果对象模式
|
||||
export function markdownToHtml(markdown: string): MarkdownToHtmlResult {
|
||||
try {
|
||||
JSON.parse(text.trim());
|
||||
return null;
|
||||
} catch (e) {
|
||||
return e instanceof SyntaxError ? e.message : 'Invalid JSON';
|
||||
...
|
||||
return { html, originalLength, htmlLength, hasError: false };
|
||||
} catch (error) {
|
||||
return {
|
||||
html: '', ...
|
||||
hasError: true,
|
||||
error: error instanceof Error ? error.message : 'Markdown 解析失败',
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -657,7 +658,7 @@ export function useTimestampConverter(): UseTimestampConverterReturn { ... }
|
||||
|
||||
```
|
||||
src/utils/useStorageState.ts — Chrome Storage 状态持久化
|
||||
src/utils/chromeI18n.ts — chrome.i18n wrapper 与 useI18n
|
||||
src/utils/useLazyTranslation.ts — i18n 懒加载
|
||||
src/utils/useContextMenuData.ts — 右键菜单数据
|
||||
src/utils/useDebounce.ts — 防抖
|
||||
src/pages/Timestamp/useTimestampConverter.ts — 页面级 Hook
|
||||
@@ -670,48 +671,43 @@ src/pages/StorageCleaner/useStorageCleaner.ts — 页面级 Hook
|
||||
|
||||
### 9.1 翻译键格式
|
||||
|
||||
- 使用 Chrome 扩展标准的 `chrome.i18n`,通过 `src/utils/chromeI18n.ts` 暴露 `useI18n`
|
||||
- 翻译 key 存放在 `public/_locales/zh_CN/messages.json`
|
||||
- 直接 key:`t('dashboard_title')` → 查找 `dashboard_title`
|
||||
- 命名空间兼容写法:`t('common:buttons.search')` → 查找 `common_buttons_search`
|
||||
- 命名空间参数:`useI18n(['common', 'features'])` 会尝试 `common_key`、`features_key`
|
||||
- 命名空间:`common`(默认)、`features`
|
||||
- 翻译键格式:`namespace:key`(如 `features:timestamp.title`)
|
||||
- 语言:`zh`(默认)、`en`
|
||||
|
||||
### 9.2 翻译文件结构
|
||||
|
||||
```
|
||||
public/_locales/zh_CN/messages.json — Chrome 扩展默认语言包
|
||||
wxt.config.ts — manifest.default_locale = 'zh_CN'
|
||||
i18n/locales/{zh,en}/common.json — 全局通用翻译
|
||||
i18n/locales/{zh,en}/features.json — 功能模块标题和描述
|
||||
i18n/locales/{zh,en}/{功能名}.json — 各功能独立翻译
|
||||
```
|
||||
|
||||
### 9.3 使用方式
|
||||
|
||||
```typescript
|
||||
// ✅ 页面组件 / 子组件 — 使用 useI18n
|
||||
import { useI18n } from '@/utils/chromeI18n';
|
||||
// ✅ 页面组件 — 使用 useLazyTranslation
|
||||
import { useLazyTranslation } from '@/utils/useLazyTranslation';
|
||||
|
||||
export default function Index() {
|
||||
const { t } = useI18n('timestamp');
|
||||
return <h1>{t('timestamp_title')}</h1>;
|
||||
const { t } = useLazyTranslation('timestamp');
|
||||
return <h1>{t('timestamp:title')}</h1>;
|
||||
}
|
||||
|
||||
// ✅ 带占位符
|
||||
export function NotFoundMessage() {
|
||||
const { t } = useI18n('router');
|
||||
return <p>{t('router_notFoundDescription', { entryPointType: 'popup' })}</p>;
|
||||
// ✅ 全局组件 — 使用 useTranslation
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export function TopBar() {
|
||||
const { t } = useTranslation(['common', 'features']);
|
||||
return <span>{t('common:settings')}</span>;
|
||||
}
|
||||
```
|
||||
|
||||
### 9.4 添加新翻译
|
||||
|
||||
1. 在 `public/_locales/zh_CN/messages.json` 添加 Chrome 扩展格式的消息:
|
||||
```json
|
||||
{
|
||||
"feature_title": { "message": "功能标题" },
|
||||
"feature_description": { "message": "功能描述" }
|
||||
}
|
||||
```
|
||||
2. key 使用下划线分隔,避免点号;`useI18n` 会把 `namespace:key.path` 兼容转换为下划线
|
||||
3. `chrome.i18n` 不支持运行时动态切换语言,浏览器语言变化后需要刷新扩展页面
|
||||
1. 在 `i18n/locales/{zh,en}/features.json` 添加功能标题和描述
|
||||
2. 创建 `i18n/locales/{zh,en}/{功能名}.json` 添加功能专属翻译
|
||||
3. 在 `utils/useLazyTranslation.ts` 的 `localeModules` 中注册新命名空间
|
||||
|
||||
---
|
||||
|
||||
@@ -1087,7 +1083,7 @@ export default function Index() {
|
||||
4. ✅ 业务逻辑提取到 `useXxx.ts` Hook(index.tsx 不超过 150 行)
|
||||
5. ✅ 需要持久化的 UI 状态使用 `useStorageState`
|
||||
6. ✅ 常量 ≥3 个时提取到 `constants.ts`
|
||||
7. ✅ 在 `public/_locales/zh_CN/messages.json` 添加翻译
|
||||
7. ✅ 在 `i18n/locales/{zh,en}/` 添加翻译
|
||||
8. ✅ 创建 `__tests__/index.test.tsx` 测试文件
|
||||
9. ✅ 如需新权限,更新 `wxt.config.ts` 的 `manifest.permissions`
|
||||
10. ✅ 运行 `npm run lint && npm run typecheck && npm run test` 全部通过
|
||||
@@ -1107,8 +1103,8 @@ export default function Index() {
|
||||
| `src/hooks/` | 自定义 React Hooks |
|
||||
| `src/utils/` | 工具函数与服务抽象 |
|
||||
| `src/types/` | TypeScript 类型声明 |
|
||||
| `src/lib/` | 通用工具函数与生成器库(cn、utils、generators) |
|
||||
| `public/` | 静态资源与 Chrome `_locales` 语言包 |
|
||||
| `src/lib/` | 通用工具函数(cn、utils) |
|
||||
| `public/` | 静态资源 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copilot 指令
|
||||
|
||||
基于 WXT 框架的浏览器扩展项目(React 19 + TypeScript),为开发者和测试人员提供效率工具:时间戳转换、存储清理、JWT 解析、JSON 工具、二维码、Base64、测试数据生成器等。
|
||||
基于 WXT 框架的浏览器扩展项目(React 19 + TypeScript),为开发者和测试人员提供效率工具:时间戳转换、存储清理、JWT 解析、JSON 工具、二维码、Base64、Markdown 等。
|
||||
|
||||
## 核心命令
|
||||
|
||||
@@ -74,10 +74,10 @@ pages/FeatureName/
|
||||
1. 在 `types/storage.d.ts` 的 `PageType` 联合类型中添加新成员
|
||||
2. 在 `config/features.tsx` 的 `FEATURES` 数组中添加配置(key、翻译键、图标、三种渲染模式组件)
|
||||
3. 在 `pages/` 目录创建页面组件(懒加载):
|
||||
- `index.tsx` — 使用 `useI18n` 的 UI 组件
|
||||
- `index.tsx` — 使用 `useLazyTranslation` 的 UI 组件
|
||||
- `useFeatureName.ts` — 业务逻辑 Hook
|
||||
- `constants.ts` — 常量(可选)
|
||||
4. 在 `public/_locales/zh_CN/messages.json` 添加 Chrome i18n 翻译
|
||||
4. 在 `i18n/locales/{zh,en}/features.json` 添加翻译(复杂功能可新建独立 JSON 文件)
|
||||
5. 如需新权限,更新 `wxt.config.ts` 的 `manifest.permissions`
|
||||
6. 添加对应的单元测试
|
||||
|
||||
@@ -102,7 +102,7 @@ pages/FeatureName/
|
||||
|
||||
### 代码分割
|
||||
|
||||
`wxt.config.ts` 通过 `manualChunksForHtmlOnly()` 自动分组 vendor 依赖(vendor-react、vendor-qr、vendor-dnd),无需手动配置。
|
||||
`wxt.config.ts` 通过 `manualChunksForHtmlOnly()` 自动分组 vendor 依赖(vendor-react、vendor-i18n、vendor-qr、vendor-dnd),无需手动配置。
|
||||
|
||||
### 代码风格
|
||||
|
||||
@@ -118,7 +118,8 @@ pages/FeatureName/
|
||||
- 全局变量:`vitest/globals`(describe、it、expect 等无需导入)
|
||||
- Setup 文件:`vitest.setup.ts` 自动 mock 以下内容:
|
||||
- `chrome.*` / `browser.*` API(storage、tabs、runtime、cookies 等)
|
||||
- `@/utils/chromeI18n`(从 `public/_locales/zh_CN/messages.json` 加载真实翻译)
|
||||
- `react-i18next`(返回 key 作为翻译)
|
||||
- `@/utils/useLazyTranslation`(返回 `ns:key` 格式)
|
||||
- `window.matchMedia`
|
||||
- 测试文件命名:`__tests__/*.test.{ts,tsx}` 或 `*.test.{ts,tsx}`
|
||||
- 使用 `vi.mock()` 进行模块级 mock;避免重复 mock `vitest.setup.ts` 中已有的内容
|
||||
@@ -126,12 +127,12 @@ pages/FeatureName/
|
||||
|
||||
### 国际化(i18n)
|
||||
|
||||
- 使用 Chrome 扩展标准 `chrome.i18n`
|
||||
- 默认语言目录:`public/_locales/zh_CN/messages.json`
|
||||
- 使用方式:`import { useI18n } from '@/utils/chromeI18n'`
|
||||
- 翻译键格式:直接 key(如 `timestamp_title`);兼容 `namespace:key.path` 并转换为下划线
|
||||
- 回退策略:缺失翻译返回 key 本身,并在开发模式下记录 warning
|
||||
- 限制:`chrome.i18n` 跟随浏览器语言,不能在运行时动态切换语言
|
||||
- 命名空间:`common`(默认)、`features`
|
||||
- 翻译键格式:`namespace:key`(如 `features:timestamp.title`)
|
||||
- 语言:`zh`(默认)、`en`
|
||||
- 翻译文件:`i18n/locales/{zh,en}/{common,features}.json` + 各功能独立 JSON 文件
|
||||
- 使用 `useLazyTranslation` Hook 加载功能专属翻译
|
||||
- 回退策略:缺失的翻译键回退到 `zh`,若仍缺失则返回占位格式 `namespace:key`
|
||||
|
||||
### WXT 生成文件
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# AGENTS.md
|
||||
|
||||
WXT 浏览器扩展项目 (React 19 + TypeScript)。提供时间戳转换、存储清理、JWT 解析、JSON 工具、二维码、Base64、测试数据生成器等测试效率工具。
|
||||
WXT 浏览器扩展项目 (React 19 + TypeScript)。提供时间戳转换、存储清理、JWT 解析、JSON 工具、二维码、Base64、Markdown 等测试效率工具。
|
||||
|
||||
## 核心命令
|
||||
|
||||
@@ -54,7 +54,6 @@ src/ # 源代码根目录
|
||||
utils/ # 工具函数与服务抽象
|
||||
types/ # TypeScript 类型声明
|
||||
lib/ # 通用工具函数(cn、utils)
|
||||
workers/ # Web Worker(数据生成等耗时任务)
|
||||
public/ # 静态资源(图标、_locales 等)
|
||||
```
|
||||
|
||||
@@ -77,38 +76,6 @@ src/pages/FeatureName/
|
||||
- 当 `index.tsx` 超过 150 行时,必须拆分为 UI + Hook 模式
|
||||
- 复杂页面可增加 `contexts/`、`hooks/`、`components/` 子目录
|
||||
|
||||
### 测试数据生成器模块
|
||||
|
||||
```
|
||||
src/pages/TestDataGenerator/
|
||||
├── index.tsx # 主页面(字段配置 + 标签页切换)
|
||||
├── hooks/useGenerator.ts # Web Worker 管理 Hook(创建、复用、通信、销毁)
|
||||
└── components/
|
||||
├── FieldList.tsx # 字段列表(虚拟滚动 + @dnd-kit 拖拽排序 + 规则保存)
|
||||
├── FieldItem.tsx # 字段卡片展示
|
||||
├── FieldEditor.tsx # 字段编辑器(名称校验、生成器选择、参数配置)
|
||||
├── GeneratorSelector.tsx # 生成器选择器(分类 + 搜索)
|
||||
├── GeneratorConfig.tsx # 生成器参数表单(动态渲染 string/number/boolean/select/array)
|
||||
├── GenerateOptions.tsx # 生成选项(数量、格式)
|
||||
├── GenerateButton.tsx # 生成按钮 + 进度条
|
||||
├── DataPreview.tsx # 示例数据预览(JSON 语法高亮)
|
||||
├── ResultPanel.tsx # 生成结果状态面板
|
||||
├── ExportPanel.tsx # 导出面板(复制/下载 JSON/CSV)
|
||||
└── RuleManager.tsx # 规则管理(CRUD、搜索、导入/导出)
|
||||
|
||||
src/utils/
|
||||
├── ruleStorage.ts # 规则持久化存储(localStorage)
|
||||
└── dataExporter.ts # 数据导出工具(JSON/CSV 转换、下载、剪贴板)
|
||||
|
||||
src/lib/generators/ # 内置生成器定义(个人信息、企业、技术、基础类型)
|
||||
|
||||
src/workers/
|
||||
└── generator.worker.ts # 数据生成 Web Worker
|
||||
|
||||
src/types/
|
||||
└── testDataGenerator.ts # 类型定义(FieldConfig, DataRule, GeneratorDefinition 等)
|
||||
```
|
||||
|
||||
## 关键架构决策
|
||||
|
||||
**路由**: 不使用 React Router。通过 `src/config/features.tsx` 的 `FEATURES` 数组管理,`RouterProvider` 根据 `PageType`
|
||||
@@ -133,7 +100,7 @@ src/types/
|
||||
- 全局变量: `vitest/globals` (describe, it, expect 等无需导入)
|
||||
- Setup 文件: `vitest.setup.ts` 自动 mock:
|
||||
- `chrome.*` / `browser.*` API (storage, tabs, runtime, cookies 等)
|
||||
- `@/utils/chromeI18n` (从 `public/_locales/zh_CN/messages.json` 加载真实翻译)
|
||||
- `@/utils/chromeI18n` (从 `public/_locales/zh/messages.json` 加载真实翻译)
|
||||
- `window.matchMedia`
|
||||
- 测试文件命名: `__tests__/*.test.{ts,tsx}` 或 `*.test.{ts,tsx}`
|
||||
- Mock 模式: 使用 `vi.mock()` 进行模块级 mock,避免在测试文件中重复 mock 代码
|
||||
@@ -143,8 +110,8 @@ src/types/
|
||||
|
||||
项目使用 Chrome 扩展标准的 `chrome.i18n` API 进行本地化,通过 `src/utils/chromeI18n.ts` 提供类型安全的 React Hook 包装。
|
||||
|
||||
- **翻译文件**: `public/_locales/zh_CN/messages.json`(Chrome 扩展标准格式)
|
||||
- **默认语言**: `zh_CN`(在 `wxt.config.ts` 的 `manifest.default_locale` 中配置)
|
||||
- **翻译文件**: `public/_locales/{zh,en}/messages.json`(Chrome 扩展标准格式)
|
||||
- **默认语言**: `zh`(在 `wxt.config.ts` 的 `manifest.default_locale` 中配置)
|
||||
- **使用方式**: `import { useI18n } from '@/utils/chromeI18n'`
|
||||
- **翻译键格式**:
|
||||
- 直接 key: `t('dashboard_title')` → 查找 `dashboard_title`
|
||||
@@ -163,7 +130,7 @@ src/types/
|
||||
- `index.tsx` — UI 组件,使用 `useI18n` 获取翻译
|
||||
- `useFeatureName.ts` — 业务逻辑 Hook
|
||||
- `constants.ts` — 常量(可选)
|
||||
4. 在 `public/_locales/zh_CN/messages.json` 添加翻译
|
||||
4. 在 `public/_locales/zh/messages.json`(及 `en/messages.json`)添加翻译
|
||||
5. 如需新权限,更新 `wxt.config.ts` 的 `manifest.permissions`
|
||||
6. 添加对应的单元测试
|
||||
|
||||
@@ -181,8 +148,7 @@ src/types/
|
||||
## 关键外部库(非显而易见的)
|
||||
|
||||
- `@webext-core/messaging` — 扩展消息通信
|
||||
- `@dnd-kit` — 拖拽排序(用于页面顺序管理和字段列表排序)
|
||||
- `@dnd-kit` — 拖拽排序(用于页面顺序管理)
|
||||
- `qrious` + `qr-scanner` — 二维码生成与解析
|
||||
- `dayjs` — 日期处理(时间戳转换)
|
||||
- `sonner` — Toast 通知(替代传统 snackbar)
|
||||
- Web Worker — 批量数据生成(`src/workers/generator.worker.ts`),避免阻塞 UI 线程
|
||||
|
||||
@@ -8,72 +8,61 @@
|
||||
|
||||
## 功能特性
|
||||
|
||||
### Dashboard 首页
|
||||
### 🚀 Dashboard 首页
|
||||
|
||||
- **工具导航**: 快速访问所有可用工具.
|
||||
- **个性化定制**: 支持自定义工具的排序和可见性.
|
||||
- **实时预览**: 在卡片上直接查看实时数据(如当前时间戳).
|
||||
|
||||
### 时间戳转换工具
|
||||
### ⏰ 时间戳转换工具
|
||||
|
||||
- **实时显示**: 毫秒级精度显示当前系统时间.
|
||||
- **双向转换**: 日期字符串与 Unix 时间戳(秒/毫秒)之间的无缝转换.
|
||||
- **多时区支持**: 预设常用时区(亚洲/上海、美洲/纽约、欧洲/伦敦),支持快速切换.
|
||||
- **快捷操作**: 一键复制转换结果,支持多种格式.
|
||||
|
||||
### 存储清理工具
|
||||
### 🧹 存储清理工具
|
||||
|
||||
- **智能识别**: 自动检测并显示当前活动标签页的域名.
|
||||
- **全面清理**: 支持一键清理 localStorage、sessionStorage、IndexedDB、Cookies、Cache Storage 和 Service Workers.
|
||||
- **细粒度控制**: 可根据需要选择特定的清理项.
|
||||
- **自动刷新**: 提供清理后自动刷新页面的选项,确保状态同步.
|
||||
|
||||
### 文本统计工具
|
||||
### 📝 文本统计工具
|
||||
|
||||
- **实时分析**: 键入即统计,无需额外操作.
|
||||
- **多维指标**: 统计字符数、单词数、行数以及精确的字节大小.
|
||||
- **性能优化**: 采用高性能分词算法,支持大文本处理.
|
||||
|
||||
### JWT 解析工具
|
||||
### 🔑 JWT 解析工具
|
||||
|
||||
- **快速解码**: 自动解析 JSON Web Token 的 Header 和 Payload.
|
||||
- **格式化显示**: 以着色和格式化的 JSON 视图展示数据,方便阅读.
|
||||
- **安全检查**: 自动去除 `Bearer` 前缀,处理异常输入并提供友好提示.
|
||||
- **签名查看**: 展示 JWT 签名部分,辅助验证令牌完整性.
|
||||
|
||||
### 右键恢复工具
|
||||
### 🖱️ 右键恢复工具
|
||||
|
||||
- **当前页面检测**: 自动识别当前活动标签页的域名.
|
||||
- **一键恢复**: 解除网站对右键菜单的限制,恢复复制、粘贴等基础操作.
|
||||
- **状态可视化**: 通过 Badge 组件直观展示当前页面的锁定/解锁状态.
|
||||
|
||||
### 二维码工具
|
||||
### 🖼️ 二维码工具
|
||||
|
||||
- **生成器**: 将当前 URL 或自定义文本快速转换为二维码,支持下载.
|
||||
- **解析器**: 支持通过上传图片或粘贴图片来解析二维码内容.
|
||||
|
||||
### JSON 工具
|
||||
### 🔄 JSON 工具
|
||||
|
||||
- **差异比较**: 对比两段 JSON 数据,高亮展示差异.
|
||||
- **格式化**: 支持 JSON 美化、压缩、转 YAML / TOML.
|
||||
|
||||
### Base64 转换器
|
||||
### 🧰 Base64 转换器
|
||||
|
||||
- **文本编解码**: 支持文本内容的 Base64 编码与解码.
|
||||
- **文件转换**: 支持文件与 Base64 字符串互转.
|
||||
- **图像预览**: 支持图片 Base64 编码与实时预览.
|
||||
|
||||
### 测试数据生成器
|
||||
|
||||
- **可视化字段配置**: 通过 UI 界面定义数据字段,支持拖拽排序、最多 40 个字段.
|
||||
- **丰富的内置生成器**: 涵盖个人信息(姓名、手机、邮箱)、企业数据(公司名、职位)、技术数据(IP、MAC 地址、UUID)、基础类型(数字、日期、枚举)等多个分类.
|
||||
- **灵活的参数配置**: 每个生成器支持自定义参数(如数字范围、日期格式、枚举值列表等).
|
||||
- **空值率与唯一性**: 可为非必填字段设置空值率,支持字段唯一性约束.
|
||||
- **规则管理**: 保存、加载、编辑、复制、导入/导出字段配置规则,方便复用.
|
||||
- **批量生成**: 支持 1 ~ 100,000 条数据生成,通过 Web Worker 异步处理避免阻塞 UI.
|
||||
- **实时预览**: 配置字段后即时预览示例数据结构.
|
||||
- **多格式导出**: 支持 JSON 和 CSV 格式,提供复制到剪贴板和下载文件两种导出方式.
|
||||
|
||||
## 技术栈
|
||||
|
||||
- **框架**: [WXT (Web Extension Toolkit)](https://wxt.dev/)
|
||||
@@ -81,13 +70,11 @@
|
||||
- **UI 组件**: shadcn/ui (基于 Radix UI 的无头组件库)
|
||||
- **样式**: Tailwind CSS + class-variance-authority + cn() 工具函数
|
||||
- **日期处理**: dayjs (集成 UTC 和 Timezone 插件)
|
||||
- **国际化**: Chrome `chrome.i18n` + `public/_locales/zh_CN/messages.json`
|
||||
- **国际化**: i18next + react-i18next
|
||||
- **通信**: @webext-core/messaging
|
||||
- **存储**: Chrome Storage API (类型安全封装)
|
||||
- **解析引擎**: qr-scanner (二维码解析), qrious (二维码生成)
|
||||
- **测试**: Vitest + Testing Library
|
||||
- **拖拽排序**: @dnd-kit/core + @dnd-kit/sortable
|
||||
- **异步生成**: Web Worker (批量数据生成)
|
||||
|
||||
## 项目结构
|
||||
|
||||
@@ -102,13 +89,12 @@
|
||||
│ │ ├── background.ts # 后台 Service Worker
|
||||
│ │ └── content.ts # 网页注入脚本
|
||||
│ ├── pages/ # 各功能模块的页面组件
|
||||
│ ├── workers/ # Web Worker (数据生成等耗时任务)
|
||||
│ ├── providers/ # 全局状态提供者 (Router, Theme 等)
|
||||
│ ├── hooks/ # 自定义 React Hooks
|
||||
│ ├── utils/ # 工具函数与服务抽象
|
||||
│ ├── types/ # TypeScript 类型声明
|
||||
│ └── lib/ # 通用工具函数与生成器库 (cn, utils, generators 等)
|
||||
├── public/ # 静态资源 (图标、_locales 本地化资源等)
|
||||
│ └── lib/ # 通用工具函数 (cn, utils 等)
|
||||
├── public/ # 静态资源 (图标、manifest 资源等)
|
||||
├── wxt.config.ts # WXT 框架核心配置
|
||||
└── package.json # 项目元数据与依赖管理
|
||||
```
|
||||
@@ -155,6 +141,7 @@
|
||||
- `sidePanel`: 支持在浏览器侧边栏中运行.
|
||||
- `clipboardWrite`: 提供一键复制功能.
|
||||
- `contextMenus`: 注册右键菜单,支持快捷操作.
|
||||
- `alarms`: 管理后台定时任务(如清理后自动刷新).
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 版本: v1.0
|
||||
> 创建时间: 2024-01-20
|
||||
> 状态: 已实现(见 `src/pages/TestDataGenerator/`、`src/lib/generators/`、`src/workers/generator.worker.ts`)
|
||||
> 状态: 待 Review
|
||||
|
||||
## 产品定位
|
||||
|
||||
@@ -19,13 +19,3 @@
|
||||
- [规则管理](./rule-management.md)
|
||||
- [界面设计](./ui-design.md)
|
||||
- [技术实现](./technical-implementation.md)
|
||||
|
||||
## 当前实现入口
|
||||
|
||||
- 页面入口:`src/pages/TestDataGenerator/index.tsx`
|
||||
- Worker:`src/workers/generator.worker.ts`
|
||||
- 生成器库:`src/lib/generators/`
|
||||
- 规则存储:`src/utils/ruleStorage.ts`
|
||||
- 导出工具:`src/utils/dataExporter.ts`
|
||||
|
||||
实现约束与任务完成状态记录在 [TASKS.md](./TASKS.md)。
|
||||
|
||||
@@ -12,24 +12,8 @@
|
||||
| `icon/48.png` | 48×48 图标(扩展管理页) |
|
||||
| `icon/96.png` | 96×96 图标 |
|
||||
| `icon/128.png` | 128×128 图标(Chrome Web Store) |
|
||||
| `_locales/` | Chrome 扩展本地化资源 |
|
||||
|
||||
## 本地化资源
|
||||
|
||||
项目使用 Chrome 扩展标准 `chrome.i18n`,默认语言在 `wxt.config.ts` 中配置为 `zh_CN`。
|
||||
|
||||
| 文件/目录 | 用途 |
|
||||
| ------------------------------ | -------------------------------------------------- |
|
||||
| `_locales/zh_CN/messages.json` | 默认中文语言包,包含功能名、描述、按钮、提示等翻译 |
|
||||
|
||||
添加或修改文案时:
|
||||
|
||||
- 使用 Chrome 扩展消息格式:`"key": { "message": "文本" }`
|
||||
- key 使用下划线分隔,例如 `testDataGenerator_title`
|
||||
- 代码中通过 `useI18n` 或 `getMessage` 读取,不要新增 `i18n/locales` 目录
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 修改图标后需同步更新 `wxt.config.ts` 中的 manifest 配置
|
||||
- 图标格式推荐使用 PNG,确保透明背景
|
||||
- 修改 `_locales` 后需确认 `manifest.default_locale` 与语言目录名一致
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { act, render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
// unmock the globally-mocked component so we test the real implementation
|
||||
vi.unmock('@/components/CopyButton');
|
||||
|
||||
vi.mock('@/utils/clipboard', () => ({
|
||||
copyTextToClipboard: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('sonner', () => ({
|
||||
toast: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
import { CopyButton } from '@/components/CopyButton';
|
||||
import { copyTextToClipboard } from '@/utils/clipboard';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
const mockedCopy = vi.mocked(copyTextToClipboard);
|
||||
const mockedToast = vi.mocked(toast);
|
||||
|
||||
describe('CopyButton', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('复制成功时调用 copyTextToClipboard 并传入正确 text', async () => {
|
||||
mockedCopy.mockResolvedValue(true);
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
|
||||
render(<CopyButton text="hello world" />);
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
expect(mockedCopy).toHaveBeenCalledWith('hello world');
|
||||
expect(mockedToast.success).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('复制成功后图标切换为 Check,1.5 秒后恢复', async () => {
|
||||
mockedCopy.mockResolvedValue(true);
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
|
||||
render(<CopyButton text="test" />);
|
||||
|
||||
// 点击后复制成功,按钮获得 emerald 样式(说明切到了 Check 状态)
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button').className).toContain('text-emerald');
|
||||
});
|
||||
|
||||
// 1.5 秒后样式恢复
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(1500);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button').className).not.toContain('text-emerald');
|
||||
});
|
||||
});
|
||||
|
||||
it('复制空文本时弹出 error toast', async () => {
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
|
||||
render(<CopyButton text="" />);
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
expect(mockedCopy).not.toHaveBeenCalled();
|
||||
expect(mockedToast.error).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('复制失败时弹出 error toast', async () => {
|
||||
mockedCopy.mockResolvedValue(false);
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
|
||||
render(<CopyButton text="something" />);
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
expect(mockedCopy).toHaveBeenCalledWith('something');
|
||||
expect(mockedToast.error).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// ==================== 新增测试 ====================
|
||||
|
||||
it('初始渲染时显示 Copy 图标且无 emerald 样式', () => {
|
||||
render(<CopyButton text="initial" />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button.className).not.toContain('text-emerald');
|
||||
// 通过 aria-label 确认按钮存在,图标由 lucide 渲染为 svg
|
||||
expect(button).toHaveAttribute('aria-label');
|
||||
});
|
||||
|
||||
it('自定义 tooltip 会覆盖默认 title 和 aria-label', () => {
|
||||
render(<CopyButton text="tooltip-test" tooltip="自定义提示" />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toHaveAttribute('title', '自定义提示');
|
||||
expect(button).toHaveAttribute('aria-label', '自定义提示');
|
||||
});
|
||||
|
||||
it('className 被正确透传到按钮', () => {
|
||||
render(<CopyButton text="class-test" className="my-custom-class" />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button.className).toContain('my-custom-class');
|
||||
});
|
||||
|
||||
it('点击事件阻止冒泡', async () => {
|
||||
mockedCopy.mockResolvedValue(true);
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
const parentClick = vi.fn();
|
||||
|
||||
render(
|
||||
<div onClick={parentClick}>
|
||||
<CopyButton text="stop-propagation" />
|
||||
</div>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
expect(mockedCopy).toHaveBeenCalled();
|
||||
expect(parentClick).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('组件卸载时清除定时器,不触发状态更新警告', async () => {
|
||||
mockedCopy.mockResolvedValue(true);
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
|
||||
const { unmount } = render(<CopyButton text="unmount-test" />);
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
// 在 1.5 秒超时到期前卸载组件
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(500);
|
||||
});
|
||||
|
||||
// 卸载不应抛出 "Can't perform a React state update on an unmounted component" 警告
|
||||
expect(() => unmount()).not.toThrow();
|
||||
|
||||
// 前进剩余时间,确认没有异常
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(2000);
|
||||
});
|
||||
});
|
||||
|
||||
it('快速连续点击不会创建多个重叠定时器', async () => {
|
||||
mockedCopy.mockResolvedValue(true);
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
|
||||
render(<CopyButton text="rapid-click" />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
|
||||
// 快速点击 3 次
|
||||
await user.click(button);
|
||||
await user.click(button);
|
||||
await user.click(button);
|
||||
|
||||
// copyTextToClipboard 应该被调用 3 次(每次点击都执行)
|
||||
expect(mockedCopy).toHaveBeenCalledTimes(3);
|
||||
|
||||
// 但 setTimeout 相关的 clearTimeout + setTimeout 组合应正常工作
|
||||
// advance 1.5 秒后,copied 状态应恢复为 false
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(1500);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(button.className).not.toContain('text-emerald');
|
||||
});
|
||||
});
|
||||
|
||||
it('其他 button props 通过 ...props 透传', () => {
|
||||
render(<CopyButton text="props-test" data-testid="copy-btn" disabled id="copy-button-id" />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
expect(button).toHaveAttribute('data-testid', 'copy-btn');
|
||||
expect(button).toBeDisabled();
|
||||
expect(button).toHaveAttribute('id', 'copy-button-id');
|
||||
});
|
||||
});
|
||||
@@ -19,7 +19,7 @@
|
||||
- `defaultVisible`:默认是否可见
|
||||
- `components`:三种渲染模式的懒加载组件(`popup`、`sidepanel`、`tab`)
|
||||
|
||||
## 已注册功能(10 个)
|
||||
## 已注册功能(11 个)
|
||||
|
||||
| key | 图标 | 说明 |
|
||||
| -------------------- | ----------------- | ----------------- |
|
||||
@@ -31,8 +31,9 @@
|
||||
| `jwt` | Key | JWT 解析工具 |
|
||||
| `jsonDiff` | GitCompareArrows | JSON 差异比较工具 |
|
||||
| `base64Converter` | ArrowLeftRight | Base64 转换器 |
|
||||
| `markdownToHtml` | Code | Markdown 转 HTML |
|
||||
| `htmlToMarkdown` | File | HTML 转 Markdown |
|
||||
| `rightClickRestorer` | MousePointerClick | 右键菜单恢复工具 |
|
||||
| `testDataGenerator` | FileSpreadsheet | 测试数据生成器 |
|
||||
|
||||
## 导出函数
|
||||
|
||||
|
||||
+3
-33
@@ -4,10 +4,9 @@
|
||||
|
||||
## 文件说明
|
||||
|
||||
| 文件/目录 | 用途 |
|
||||
| ------------- | -------------------------------------- |
|
||||
| `utils.ts` | `cn()` 函数 — shadcn/ui 标准工具函数 |
|
||||
| `generators/` | 测试数据生成器内置生成器库和分类注册表 |
|
||||
| 文件 | 用途 |
|
||||
| ---------- | ------------------------------------ |
|
||||
| `utils.ts` | `cn()` 函数 — shadcn/ui 标准工具函数 |
|
||||
|
||||
## cn()
|
||||
|
||||
@@ -25,32 +24,3 @@ import { cn } from '@/lib/utils';
|
||||
```
|
||||
|
||||
所有需要动态合并 Tailwind 类名的场景都应使用 `cn()`,而非手动拼接字符串。
|
||||
|
||||
## generators/
|
||||
|
||||
测试数据生成器的核心生成器库,供 `src/workers/generator.worker.ts` 在后台线程中按字段配置生成数据。
|
||||
|
||||
| 文件 | 用途 |
|
||||
| -------------- | ---------------------------------------------- |
|
||||
| `index.ts` | 聚合所有生成器,导出分类、查找和搜索函数 |
|
||||
| `personal.ts` | 个人信息生成器(姓名、邮箱、手机号、身份证等) |
|
||||
| `business.ts` | 业务数据生成器(订单号、价格、日期、状态等) |
|
||||
| `technical.ts` | 技术数据生成器(UUID、IPv4、URL) |
|
||||
| `basic.ts` | 基础类型生成器(整数、浮点数、布尔值、字符串) |
|
||||
| `types.ts` | 生成器库内部类型与通用选项 |
|
||||
|
||||
使用示例:
|
||||
|
||||
```typescript
|
||||
import { getGeneratorById, searchGenerators } from '@/lib/generators';
|
||||
|
||||
const generator = getGeneratorById('chineseName');
|
||||
const value = generator?.generate({});
|
||||
const matched = searchGenerators('uuid');
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- 新增生成器必须实现 `GeneratorDefinition`,并加入所属分类文件的导出列表。
|
||||
- 需要唯一值或大批量稳定输出时,优先实现 `generateAtIndex(params, index)`。
|
||||
- 新增分类时同步更新 `generatorCategories`,并确保 `GeneratorSelector` 能展示该分类。
|
||||
|
||||
+9
-22
@@ -113,6 +113,14 @@ Base64 编解码工具,支持文本/文件/图片三种模式。
|
||||
| `TextMode.tsx` | 文本模式 |
|
||||
| `Base64ConverterSection.tsx` | 文件/图片通用转换区域 |
|
||||
|
||||
### MarkdownToHtml/
|
||||
|
||||
Markdown 转 HTML 工具,支持分栏/预览/源码三种视图模式。
|
||||
|
||||
### HtmlToMarkdown/
|
||||
|
||||
HTML 转 Markdown 工具,支持分栏/预览/Markdown 三种视图模式。
|
||||
|
||||
### RightClickRestorer/
|
||||
|
||||
右键菜单恢复工具,解除网站对右键的限制。
|
||||
@@ -122,32 +130,11 @@ Base64 编解码工具,支持文本/文件/图片三种模式。
|
||||
| `index.tsx` | 页面 UI |
|
||||
| `useRightClickRestorer.ts` | 业务逻辑 Hook |
|
||||
|
||||
### TestDataGenerator/
|
||||
|
||||
测试数据生成器,支持可视化字段配置、规则复用、Web Worker 批量生成和 JSON/CSV 导出。
|
||||
|
||||
| 文件/目录 | 用途 |
|
||||
| ---------------------------- | ------------------------------------------------- |
|
||||
| `index.tsx` | 主页面,管理字段配置、规则标签页和生成结果 |
|
||||
| `hooks/useGenerator.ts` | Worker 生命周期与消息通信,暴露生成/取消/清理操作 |
|
||||
| `components/FieldList.tsx` | 字段列表、拖拽排序、虚拟滚动和规则保存入口 |
|
||||
| `components/FieldEditor.tsx` | 字段名称、生成器、参数、必填/空值率/唯一性配置 |
|
||||
| `components/RuleManager.tsx` | 规则搜索、加载、编辑、复制、删除、导入和导出 |
|
||||
| `components/ExportPanel.tsx` | 生成结果复制和 JSON/CSV 下载 |
|
||||
|
||||
约束与注意事项:
|
||||
|
||||
- 字段数量最多 40 个(`FieldList.MAX_FIELDS`)。
|
||||
- 生成数量为 1 ~ 100,000,预设值包含 50、100、1,000、5,000、10,000。
|
||||
- 大批量生成通过 `src/workers/generator.worker.ts` 执行,避免阻塞 UI;Worker 每 1,000 条或结束时回传进度。
|
||||
- 规则通过 `src/utils/ruleStorage.ts` 存入 `localStorage`,最多保存 20 条规则。
|
||||
- 内置生成器位于 `src/lib/generators/`,按个人信息、业务数据、技术数据、基础类型分类。
|
||||
|
||||
## 新增页面
|
||||
|
||||
1. 在 `types/storage.d.ts` 添加 `PageType` 联合类型成员
|
||||
2. 在 `config/features.tsx` 的 `FEATURES` 数组添加配置
|
||||
3. 在 `pages/` 创建页面目录(遵循上述结构)
|
||||
4. 在 `public/_locales/zh_CN/messages.json` 添加 Chrome i18n 翻译
|
||||
4. 在 `i18n/locales/{zh,en}/features.json` 添加翻译
|
||||
5. 如需新权限,更新 `wxt.config.ts`
|
||||
6. 添加对应的单元测试
|
||||
|
||||
+3
-11
@@ -10,10 +10,12 @@
|
||||
|
||||
**页面类型:**
|
||||
|
||||
- `PageType` — 所有页面类型的联合类型(含 `dashboard`、`timestamp`、`storageCleaner`、`testDataGenerator` 等)
|
||||
- `PageType` — 所有页面类型的联合类型(`dashboard` | `timestamp` | `storageCleaner` | ...)
|
||||
- `JsonToolsPageMode` — JSON 工具子模式(`diff` | `format` | `yaml` | `toml` | `minify`)
|
||||
- `Base64ConverterPageMode` — Base64 子模式(`text` | `file` | `image`)
|
||||
- `Base64ConvertDirection` — 编解码方向(`encode` | `decode`)
|
||||
- `MarkdownToHtmlPreviewMode` — Markdown 预览模式(`split` | `preview` | `html`)
|
||||
- `HtmlToMarkdownPreviewMode` — HTML 预览模式(`split` | `preview` | `markdown`)
|
||||
|
||||
**存储 Schema:**
|
||||
|
||||
@@ -32,16 +34,6 @@
|
||||
|
||||
`qrious` 库的类型声明,定义 QR 码生成选项和 `QRious` 类。
|
||||
|
||||
### testDataGenerator.ts
|
||||
|
||||
测试数据生成器共享类型,包含:
|
||||
|
||||
- `FieldConfig` — 字段配置(字段名、生成器、参数、必填、空值率、唯一性)
|
||||
- `DataRule` — 可保存/导入/导出的字段规则
|
||||
- `GeneratorDefinition` / `GeneratorParam` — 内置生成器定义和参数 Schema
|
||||
- `GenerateResult` / `GenerateProgress` / `WorkerMessage` — Worker 生成结果、进度和消息协议
|
||||
- `ExportFile` — JSON/CSV 导出文件描述
|
||||
|
||||
## 修改 StorageSchema 的注意事项
|
||||
|
||||
修改 `StorageSchema` 时,必须:
|
||||
|
||||
+21
-22
@@ -4,40 +4,39 @@
|
||||
|
||||
## 工具函数
|
||||
|
||||
| 文件 | 用途 |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------- |
|
||||
| `chromeStorage.ts` | Chrome Storage API 封装:类型安全的 `StorageUtils` 类,提供 `get/set/remove` 方法 |
|
||||
| `chromeTabs.ts` | Chrome Tabs API 封装:获取活动标签页、获取域名、在新标签页打开扩展页面 |
|
||||
| `clipboard.ts` | 剪贴板操作:`copyTextToClipboard`(文本)、`copyImageToClipboard`(图片) |
|
||||
| `messages.ts` | 扩展消息通信:基于 `@webext-core/messaging`,定义 `MessageAction` 枚举和 `ProtocolMap` 类型安全映射 |
|
||||
| `contextMenu.ts` | 右键菜单配置与操作:定义菜单项、创建菜单、解析点击事件、ID→PageType 映射 |
|
||||
| `base64Converter.ts` | Base64 编解码:文本↔Base64、文件↔Base64、图片预览,定义文件大小限制和图像 MIME 类型 |
|
||||
| `jwt.ts` | JWT 解析:Base64URL 解码、解析 Header/Payload/Signature、JSON 格式化输出 |
|
||||
| `jsonFormatter.ts` | JSON 格式化/压缩:支持缩进、按键排序、minify |
|
||||
| `jsonToYaml.ts` | JSON→YAML 转换 |
|
||||
| `jsonToToml.ts` | JSON→TOML 转换 |
|
||||
| `qrCodeParser.ts` | 二维码解析:基于 `qr-scanner` 库从文件中解析二维码 |
|
||||
| `storageCleaner.ts` | 存储清理:获取当前标签页、检测受限 URL、计算 Cookie/Storage 大小、清理操作 |
|
||||
| `textStatistics.ts` | 文本统计:使用 `Intl.Segmenter` 计算字符数/单词数/行数/字节大小 |
|
||||
| `format.ts` | 通用格式化:`formatBytes` 将字节转为可读字符串(B/KB/MB/GB/TB) |
|
||||
| `dayjs.ts` | Day.js 初始化:扩展 UTC、Timezone、RelativeTime 插件,加载中文本地化 |
|
||||
| `chromeI18n.ts` | Chrome `chrome.i18n` 包装:提供 `getMessage` 和兼容 React 使用的 `useI18n` Hook |
|
||||
| `ruleStorage.ts` | 测试数据生成器规则存储:基于 `localStorage` 的 CRUD、搜索、导入/导出和数量限制 |
|
||||
| `dataExporter.ts` | 测试数据导出:JSON/CSV 转换、文件下载和复制到剪贴板 |
|
||||
| `rightClickInjection.ts` | 右键恢复注入脚本:在页面上下文恢复 contextmenu/copy/paste 等事件默认行为 |
|
||||
| 文件 | 用途 |
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| `chromeStorage.ts` | Chrome Storage API 封装:类型安全的 `StorageUtils` 类,提供 `get/set/remove` 方法 |
|
||||
| `chromeTabs.ts` | Chrome Tabs API 封装:获取活动标签页、获取域名、在新标签页打开扩展页面 |
|
||||
| `clipboard.ts` | 剪贴板操作:`copyTextToClipboard`(文本)、`copyImageToClipboard`(图片) |
|
||||
| `messages.ts` | 扩展消息通信:基于 `@webext-core/messaging`,定义 `MessageAction` 枚举和 `ProtocolMap` 类型安全映射 |
|
||||
| `contextMenu.ts` | 右键菜单配置与操作:定义菜单项、创建菜单、解析点击事件、ID→PageType 映射 |
|
||||
| `base64Converter.ts` | Base64 编解码:文本↔Base64、文件↔Base64、图片预览,定义文件大小限制和图像 MIME 类型 |
|
||||
| `jwt.ts` | JWT 解析:Base64URL 解码、解析 Header/Payload/Signature、JSON 格式化输出 |
|
||||
| `jsonFormatter.ts` | JSON 格式化/压缩:支持缩进、按键排序、minify |
|
||||
| `jsonToYaml.ts` | JSON→YAML 转换 |
|
||||
| `jsonToToml.ts` | JSON→TOML 转换 |
|
||||
| `markdownToHtml.ts` | Markdown→HTML 转换:基于 `marked` 库,支持 GFM 和换行转换 |
|
||||
| `htmlToMarkdown.ts` | HTML→Markdown 转换:基于 DOMParser 解析 |
|
||||
| `qrCodeParser.ts` | 二维码解析:基于 `qr-scanner` 库从文件中解析二维码 |
|
||||
| `storageCleaner.ts` | 存储清理:获取当前标签页、检测受限 URL、计算 Cookie/Storage 大小、清理操作 |
|
||||
| `textStatistics.ts` | 文本统计:使用 `Intl.Segmenter` 计算字符数/单词数/行数/字节大小 |
|
||||
| `format.ts` | 通用格式化:`formatBytes` 将字节转为可读字符串(B/KB/MB/GB/TB) |
|
||||
| `dayjs.ts` | Day.js 初始化:扩展 UTC、Timezone、RelativeTime 插件,加载中文本地化 |
|
||||
|
||||
## 自定义 Hooks
|
||||
|
||||
| 文件 | 用途 |
|
||||
| ----------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| `useStorageState.ts` | Chrome Storage 状态 Hook:类似 `useState`,值自动同步到 `chrome.storage`,使用 `localStorage` 快照消除首屏闪烁 |
|
||||
| `useLazyTranslation.ts` | 懒加载翻译 Hook:按需动态导入 i18n 命名空间,支持预加载和缓存 |
|
||||
| `useContextMenuData.ts` | 右键菜单数据 Hook:从 storage 读取待处理数据,匹配 featureKey 后消费并触发回调 |
|
||||
| `useDebounce.ts` | 防抖 Hook:对值进行延迟更新,避免频繁触发 |
|
||||
|
||||
## 使用约定
|
||||
|
||||
- 工具函数使用**命名导出**(`export function xxx()`)
|
||||
- 工具层不直接展示 Toast;可恢复错误返回可判断结果,需要抛出的解析/转换错误由页面 Hook 或 UI 层捕获
|
||||
- 工具函数**不抛异常**,返回包含 `hasError` 和 `error` 字段的结果对象
|
||||
- Hook 使用 `use` 前缀命名,定义返回值接口类型
|
||||
- 存储操作使用 `chromeStorage.ts` 的 `storageUtil` 封装,不要直接调用 `chrome.storage`
|
||||
- 消息通信使用 `messages.ts` 的 `sendMessage`/`onMessage`,不要使用原生 `chrome.runtime.sendMessage`
|
||||
|
||||
Reference in New Issue
Block a user