diff --git a/i18n/README.md b/i18n/README.md new file mode 100644 index 0000000..dcaa52b --- /dev/null +++ b/i18n/README.md @@ -0,0 +1,61 @@ +# i18n/ + +国际化资源目录,管理多语言翻译和 i18next 初始化配置。 + +## 目录结构 + +``` +i18n/ +├── index.ts # i18next 初始化配置 +└── locales/ + ├── zh/ # 中文翻译(默认语言) + │ ├── common.json # 通用文案 + │ ├── features.json # 功能模块标题和描述 + │ ├── timestamp.json # 时间戳工具翻译 + │ ├── storageCleaner.json # 存储清理工具翻译 + │ ├── qrCode.json # 二维码工具翻译 + │ ├── textStatistics.json # 文本统计工具翻译 + │ ├── jwt.json # JWT 工具翻译 + │ ├── jsonDiff.json # JSON 差异工具翻译 + │ ├── jsonFormat.json # JSON 格式化工具翻译 + │ ├── base64Converter.json + │ ├── markdownToHtml.json + │ ├── htmlToMarkdown.json + │ └── rightClickRestorer.json + └── en/ # 英文翻译(结构同上) + └── ... +``` + +## index.ts + +i18next 初始化配置: + +- 同步加载 `common` 和 `features` 核心命名空间 +- 自定义 `chromeStorage` 语言检测器,从 Chrome Storage 读取语言偏好 +- `normalizeLanguage()` 将任意语言标识归一化为 `zh` 或 `en` +- 语言变更时同步更新 Day.js 本地化和 localStorage 快照 + +## 翻译键格式 + +- 命名空间:`common`(默认)、`features`、各功能独立命名空间 +- 键格式:`namespace:key`(如 `features:timestamp.title`、`timestamp:unitMs`) + +## 使用方式 + +```tsx +// 页面组件 — 懒加载翻译 +import { useLazyTranslation } from '@/utils/useLazyTranslation'; +const { t } = useLazyTranslation('timestamp'); +t('timestamp:title'); + +// 全局组件 — 直接使用 +import { useTranslation } from 'react-i18next'; +const { t } = useTranslation(['common', 'features']); +t('common:settings'); +``` + +## 添加新翻译 + +1. 在 `locales/{zh,en}/features.json` 添加功能标题和描述 +2. 创建 `locales/{zh,en}/{功能名}.json` 添加功能专属翻译 +3. 在 `utils/useLazyTranslation.ts` 的 `localeModules` 中注册新命名空间 diff --git a/package-lock.json b/package-lock.json index 82265b3..af81474 100644 --- a/package-lock.json +++ b/package-lock.json @@ -211,7 +211,7 @@ "version": "5.1.11", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -228,7 +228,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", @@ -245,7 +245,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -255,7 +255,7 @@ "version": "2.3.9", "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/@babel/code-frame": { @@ -542,7 +542,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "css-tree": "^3.0.0" @@ -555,7 +555,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -575,7 +575,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -599,7 +599,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz", "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -627,7 +627,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -650,7 +650,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz", "integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -675,7 +675,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -818,6 +818,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -829,6 +830,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -839,6 +841,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -852,6 +855,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -868,6 +872,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -884,6 +889,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -900,6 +906,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -916,6 +923,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -932,6 +940,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -948,6 +957,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -964,6 +974,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -980,6 +991,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -996,6 +1008,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1012,6 +1025,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1028,6 +1042,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1044,6 +1059,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1060,6 +1076,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1076,6 +1093,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1092,6 +1110,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1108,6 +1127,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1124,6 +1144,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1140,6 +1161,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1156,6 +1178,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1172,6 +1195,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1188,6 +1212,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1204,6 +1229,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1220,6 +1246,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1236,6 +1263,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1252,6 +1280,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1481,7 +1510,7 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" @@ -1601,7 +1630,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1632,7 +1661,7 @@ "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -1659,6 +1688,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1715,6 +1745,7 @@ "version": "0.132.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -2503,6 +2534,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2519,6 +2551,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2535,6 +2568,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2551,6 +2585,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2567,6 +2602,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2583,6 +2619,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2599,6 +2636,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2615,6 +2653,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2631,6 +2670,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2647,6 +2687,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2663,6 +2704,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2679,6 +2721,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2695,6 +2738,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2713,6 +2757,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2729,6 +2774,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2742,12 +2788,14 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, "license": "MIT" }, "node_modules/@testing-library/dom": { @@ -2842,6 +2890,7 @@ "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2858,6 +2907,7 @@ "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, "license": "MIT", "dependencies": { "@types/deep-eql": "*", @@ -2879,6 +2929,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, "license": "MIT" }, "node_modules/@types/estree": { @@ -2929,7 +2980,7 @@ "version": "25.9.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" @@ -2945,7 +2996,7 @@ "version": "19.2.15", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2955,7 +3006,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -3261,6 +3312,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.7.tgz", "integrity": "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==", + "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", @@ -3278,6 +3330,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.7.tgz", "integrity": "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/spy": "4.1.7", @@ -3316,6 +3369,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.7.tgz", "integrity": "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/utils": "4.1.7", @@ -3329,6 +3383,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.7.tgz", "integrity": "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.7", @@ -3344,6 +3399,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.7.tgz", "integrity": "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==", + "dev": true, "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" @@ -3461,7 +3517,7 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3833,6 +3889,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -3983,7 +4040,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "require-from-string": "^2.0.2" @@ -4179,7 +4236,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/bundle-name": { @@ -4345,6 +4402,7 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -4616,7 +4674,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/concat-map": { @@ -4739,7 +4797,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "mdn-data": "2.27.1", @@ -4793,14 +4851,14 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/data-urls": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "whatwg-mimetype": "^5.0.0", @@ -4899,7 +4957,7 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/deep-extend": { @@ -5025,6 +5083,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -5242,7 +5301,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=20.19.0" @@ -5395,6 +5454,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { @@ -5468,7 +5528,7 @@ "version": "0.27.7", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -5901,6 +5961,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" @@ -5988,6 +6049,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -6164,6 +6226,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -6624,7 +6687,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@exodus/bytes": "^1.6.0" @@ -7205,7 +7268,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/is-primitive": { @@ -7470,7 +7533,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -7499,7 +7562,7 @@ "version": "29.1.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^5.1.11", @@ -7540,7 +7603,7 @@ "version": "11.5.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.0.tgz", "integrity": "sha512-5YgH9UJd7wVb9hIouI2adWpgqrrICkt070Dnj8EUY1+B4B2P9eRLPAkAAo6NICA7CEhOIeBHl46u9zSNpNu7zA==", - "devOptional": true, + "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -7769,6 +7832,7 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -7801,6 +7865,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7821,6 +7886,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7841,6 +7907,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7861,6 +7928,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7881,6 +7949,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7901,6 +7970,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7921,6 +7991,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7941,6 +8012,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7961,6 +8033,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -7981,6 +8054,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8001,6 +8075,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8336,6 +8411,7 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -8408,7 +8484,7 @@ "version": "2.27.1", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", - "devOptional": true, + "dev": true, "license": "CC0-1.0" }, "node_modules/merge2": { @@ -8615,6 +8691,7 @@ "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, "funding": [ { "type": "github", @@ -9101,7 +9178,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "entities": "^8.0.0" @@ -9141,6 +9218,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, "license": "MIT" }, "node_modules/perfect-debounce": { @@ -9160,6 +9238,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -9254,6 +9333,7 @@ "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -9598,7 +9678,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9979,7 +10059,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10056,6 +10136,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.132.0", @@ -10222,7 +10303,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "devOptional": true, + "dev": true, "license": "ISC", "dependencies": { "xmlchars": "^2.2.0" @@ -10448,6 +10529,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, "license": "ISC" }, "node_modules/signal-exit": { @@ -10533,7 +10615,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -10544,7 +10626,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -10589,6 +10671,7 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, "license": "MIT" }, "node_modules/std-env": { @@ -10920,7 +11003,7 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/tailwind-merge": { @@ -11049,7 +11132,7 @@ "version": "5.47.1", "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -11108,12 +11191,14 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -11123,6 +11208,7 @@ "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -11148,7 +11234,7 @@ "version": "7.0.30", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "tldts-core": "^7.0.30" @@ -11161,7 +11247,7 @@ "version": "7.0.30", "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/tmp": { @@ -11191,7 +11277,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" @@ -11204,7 +11290,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "punycode": "^2.3.1" @@ -11425,7 +11511,7 @@ "version": "7.25.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=20.18.1" @@ -11435,7 +11521,7 @@ "version": "7.24.6", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/unimport": { @@ -11675,6 +11761,7 @@ "version": "8.0.14", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", + "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", @@ -11775,6 +11862,7 @@ "version": "4.1.7", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.7.tgz", "integrity": "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==", + "dev": true, "license": "MIT", "dependencies": { "@vitest/expect": "4.1.7", @@ -11864,7 +11952,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "xml-name-validator": "^5.0.0" @@ -12006,7 +12094,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=20" @@ -12023,7 +12111,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=20" @@ -12033,7 +12121,7 @@ "version": "16.0.1", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@exodus/bytes": "^1.11.0", @@ -12167,6 +12255,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, "license": "MIT", "dependencies": { "siginfo": "^2.0.0", @@ -12372,7 +12461,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18" @@ -12406,7 +12495,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/y18n": { @@ -12430,6 +12519,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, "license": "ISC", "optional": true, "bin": { diff --git a/public/README.md b/public/README.md new file mode 100644 index 0000000..b7b3f2f --- /dev/null +++ b/public/README.md @@ -0,0 +1,20 @@ +# public/ + +静态资源目录,存放无需构建处理的文件,会被直接复制到输出目录。 + +## 文件说明 + +| 文件/目录 | 用途 | +| -------------- | -------------------------------- | +| `icon/` | 扩展图标,提供多种尺寸 | +| `icon/16.png` | 16×16 图标(工具栏) | +| `icon/32.png` | 32×32 图标 | +| `icon/48.png` | 48×48 图标(扩展管理页) | +| `icon/96.png` | 96×96 图标 | +| `icon/128.png` | 128×128 图标(Chrome Web Store) | +| `wxt.svg` | WXT 框架标志 SVG 图标 | + +## 注意事项 + +- 修改图标后需同步更新 `wxt.config.ts` 中的 manifest 配置 +- 图标格式推荐使用 PNG,确保透明背景 diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..5bb0402 --- /dev/null +++ b/src/README.md @@ -0,0 +1,24 @@ +# src/ + +源码目录,存放全局样式定义。 + +## 文件说明 + +| 文件 | 用途 | +| ----------- | ----------------- | +| `index.css` | 全局 CSS 入口文件 | + +## index.css + +全局样式入口,包含: + +- **Tailwind 指令**:`@tailwind base/components/utilities` +- **shadcn/ui CSS 变量**:定义 `--background`、`--primary`、`--destructive`、`--card`、`--muted`、`--accent`、`--border`、`--ring` 等语义化颜色变量 +- **主题色值**:`:root`(亮色)和 `.dark`(暗色)两套完整的颜色定义 +- **圆角变量**:`--radius` 定义全局圆角大小 + +## 修改注意事项 + +- 修改 CSS 变量会影响所有使用 shadcn/ui 语义化 token 的组件 +- 新增颜色变量需同时在 `:root` 和 `.dark` 中定义 +- 避免在组件中硬编码颜色值,应使用 CSS 变量或 Tailwind 的语义化类名 diff --git a/src/components/README.md b/src/components/README.md new file mode 100644 index 0000000..abd1286 --- /dev/null +++ b/src/components/README.md @@ -0,0 +1,27 @@ +# components/ + +通用业务组件目录,存放跨页面复用的 UI 组件,与具体工具页面解耦。 + +## 组件列表 + +| 组件 | 用途 | +| ----------------------- | ------------------------------------------------------------------------------ | +| `TopBar.tsx` | 顶部导航栏,集成搜索(含历史记录)、主题切换、语言切换、返回导航 | +| `RouterContainer.tsx` | 路由容器,根据当前路由动态渲染对应页面组件,集成错误边界和骨架屏 | +| `SwitchButtonGroup.tsx` | 通用切换按钮组,支持 `small/medium/large` 三种尺寸,用于页面子模式切换 | +| `TextInputArea.tsx` | 增强文本输入区域,支持校验规则、工具栏操作、字符计数、清空 | +| `CopyButton.tsx` | 一键复制按钮,支持复制成功状态动画,封装 `copyTextToClipboard` 和 `toast` 反馈 | +| `ImageUploader.tsx` | 图片上传组件,支持拖拽上传、文件选择和预览 | +| `QrCodePreview.tsx` | 二维码预览组件,展示生成的二维码图片,提供复制和下载操作 | +| `DecodeResultPaper.tsx` | Base64 解码结果展示面板,显示 MIME 类型、文件大小、文件名输入和下载按钮 | +| `GlobalSnackbar.tsx` | 全局消息提示组件 + Context Provider,支持受控/Hook/全局单例三种使用方式 | +| `ErrorBoundary.tsx` | 全局错误边界(类组件),捕获子组件树 JS 错误并展示友好错误页面 | +| `PageErrorBoundary.tsx` | 页面级错误边界,适配 shadcn 暗黑模式,支持 `resetKey` 自动恢复 | +| `PageSkeleton.tsx` | 页面骨架屏,提供 `dashboard` 和 `tool` 两种变体,用于 Suspense fallback | + +## 使用约定 + +- 优先使用 `components/ui/` 下的 shadcn/ui 基础组件 +- 组件使用 `cn()` 合并 Tailwind 类名,支持 `className` 透传 +- 需要 memo 优化的组件使用 `React.memo` + `displayName` +- 需要 ref 转发的组件使用 `React.forwardRef` + `displayName` diff --git a/src/components/ui/README.md b/src/components/ui/README.md new file mode 100644 index 0000000..72420ba --- /dev/null +++ b/src/components/ui/README.md @@ -0,0 +1,40 @@ +# components/ui/ + +shadcn/ui 基础原子组件目录,基于 Radix UI 原语 + Tailwind CSS 实现。 + +## 组件列表 + +| 组件 | 用途 | +| -------------- | -------------------------------------------------------------------------------------------------- | +| `button.tsx` | 按钮组件,支持 `default/destructive/outline/secondary/ghost/link` 变体和 `default/sm/lg/icon` 尺寸 | +| `input.tsx` | 标准输入框,统一的 ring/focus 样式 | +| `select.tsx` | 下拉选择组件,包含 Trigger、Content、Item 等子组件 | +| `dialog.tsx` | 对话框组件,包含 Overlay、Content、Header、Footer、Title、Description | +| `checkbox.tsx` | 复选框组件 | +| `label.tsx` | 标签组件 | +| `switch.tsx` | 开关组件 | +| `badge.tsx` | 徽章组件,支持 `default/secondary/destructive/outline` 变体 | + +## 使用方式 + +```tsx +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +``` + +## 添加新组件 + +使用 shadcn/ui CLI 添加新组件: + +```bash +npx shadcn-ui@latest add +``` + +组件配置在项目根目录的 `components.json` 中定义。 diff --git a/src/config/README.md b/src/config/README.md new file mode 100644 index 0000000..ced4dcb --- /dev/null +++ b/src/config/README.md @@ -0,0 +1,44 @@ +# config/ + +应用级配置目录,存放功能特性的注册中心。 + +## 文件说明 + +| 文件 | 用途 | +| -------------- | ---------------------------------------- | +| `features.tsx` | 核心配置文件,定义所有工具功能的注册信息 | + +## features.tsx + +`FEATURES` 数组是路由和功能元数据的**单一事实来源**,每个功能定义包含: + +- `key`:页面类型标识(`PageType`) +- `labelKey` / `descriptionKey`:i18n 翻译键 +- `themeColorKey`:主题色(`primary/success/warning/error/secondary/info`) +- `icon`:lucide-react 图标组件 +- `defaultVisible`:默认是否可见 +- `components`:三种渲染模式的懒加载组件(`popup`、`sidepanel`、`tab`) + +## 已注册功能(11 个) + +| key | 图标 | 说明 | +| -------------------- | ----------------- | ----------------- | +| `dashboard` | — | 仪表盘首页 | +| `timestamp` | Clock | 时间戳转换工具 | +| `storageCleaner` | Database | 存储清理工具 | +| `qrCode` | QrCode | 二维码工具 | +| `textStatistics` | FileText | 文本统计工具 | +| `jwt` | Key | JWT 解析工具 | +| `jsonDiff` | GitCompareArrows | JSON 差异比较工具 | +| `base64Converter` | ArrowLeftRight | Base64 转换器 | +| `markdownToHtml` | Code | Markdown 转 HTML | +| `htmlToMarkdown` | File | HTML 转 Markdown | +| `rightClickRestorer` | MousePointerClick | 右键菜单恢复工具 | + +## 导出函数 + +- `getFeatureByKey(key)` — 根据 key 获取功能配置 +- `getDefaultVisibleFeatureKeys()` — 获取默认可见的功能 key 列表 +- `getAllFeatureKeys()` — 获取所有功能 key 列表 +- `getDefaultPageOrder()` — 获取默认页面排序(不含 dashboard) +- `getEntryPointType()` — 判断当前入口类型(popup/sidepanel/tab) diff --git a/src/entrypoints/README.md b/src/entrypoints/README.md new file mode 100644 index 0000000..47982f0 --- /dev/null +++ b/src/entrypoints/README.md @@ -0,0 +1,51 @@ +# entrypoints/ + +WXT 框架要求的扩展生命周期入口点,对应 Chrome Extension 的各个上下文。 + +## 入口文件 + +| 文件 | 用途 | +| ------------------------------- | --------------------------------------------------------------------------------------------- | +| `background.ts` | Service Worker 入口:注册右键菜单、监听菜单点击、处理消息通信、管理侧边栏状态、注入主环境脚本 | +| `content.ts` | Content Script 入口:注入所有页面(``),在 `document_end` 时初始化消息处理器 | +| `rightClickRestorer.content.ts` | 专用 Content Script:处理右键菜单恢复功能,注入浮动状态徽章 | + +## 子目录 + +### popup/ + +Popup 弹窗页面(点击扩展图标弹出)。 + +| 文件 | 用途 | +| ------------ | ------------------------------------------------------------------------------ | +| `index.html` | HTML 入口 | +| `main.tsx` | React 挂载点 | +| `App.tsx` | 根组件,组装 `RouterProvider` + `TopBar` + `ErrorBoundary` + `RouterContainer` | + +### sidepanel/ + +侧边栏页面,结构与 popup 类似,额外通知 background 侧边栏开启/关闭状态。 + +### options/ + +设置页面,支持: + +- 拖拽排序功能顺序(`@dnd-kit`) +- 功能可见性管理(显示/隐藏) +- Popup/Sidepanel/Tab 三种模式独立配置 + +### content/ + +Content Script 内部分模块: + +| 文件 | 用途 | +| ----------------------- | ------------------------------------------------------------------- | +| `messageHandler.ts` | 消息处理器初始化入口 | +| `contextMenuHandler.ts` | 右键菜单点击事件处理,执行时间戳转换/文本统计并通过 UI Popover 展示 | +| `uiPopover.ts` | 在页面中注入浮层 Popover UI,展示右键菜单操作结果 | + +## 架构说明 + +- `background.ts` 是扩展的核心协调者,处理跨上下文通信 +- `content.ts` 注入到所有页面,负责接收和处理来自 background 的消息 +- `popup/`、`sidepanel/`、`options/` 共享同一套页面组件(来自 `pages/`),通过 `RouterProvider` 的不同配置实现独立路由 diff --git a/src/lib/README.md b/src/lib/README.md new file mode 100644 index 0000000..4616805 --- /dev/null +++ b/src/lib/README.md @@ -0,0 +1,26 @@ +# lib/ + +通用库工具目录,存放与业务无关的底层工具函数。 + +## 文件说明 + +| 文件 | 用途 | +| ---------- | ------------------------------------ | +| `utils.ts` | `cn()` 函数 — shadcn/ui 标准工具函数 | + +## cn() + +组合 `clsx` + `tailwind-merge`,用于合并和去重 Tailwind CSS 类名: + +```typescript +import { cn } from '@/lib/utils'; + +// 条件类名 + 合并外部 className +
+``` + +所有需要动态合并 Tailwind 类名的场景都应使用 `cn()`,而非手动拼接字符串。 diff --git a/src/pages/README.md b/src/pages/README.md new file mode 100644 index 0000000..c3a15a2 --- /dev/null +++ b/src/pages/README.md @@ -0,0 +1,140 @@ +# pages/ + +功能页面组件目录,每个子目录对应一个工具页面。 + +## 目录结构约定 + +典型页面遵循 **UI + Hook 分离** 模式: + +``` +pages/FeatureName/ +├── index.tsx # 页面 UI(纯展示,使用 shadcn/ui 组件) +├── useFeatureName.ts # 业务逻辑 Hook(状态管理 + 转换逻辑) +├── constants.ts # 常量定义(可选) +├── SubComponent.tsx # 子组件(可选) +└── __tests__/ # 测试文件 + └── index.test.tsx +``` + +## 页面列表 + +### Dashboard/ + +仪表盘首页,以卡片网格展示所有可见工具,支持点击导航。 + +| 文件 | 用途 | +| -------------- | -------------------------------------------- | +| `index.tsx` | 页面组件,渲染工具卡片网格 | +| `ToolCard.tsx` | 工具卡片组件,展示图标、标题、描述和实时数据 | + +### Timestamp/ + +时间戳转换工具,支持秒/毫秒级互转、多时区选择、实时时钟。 + +| 文件 | 用途 | +| -------------------------- | ----------------------------------------------------------------- | +| `index.tsx` | 页面 UI | +| `useTimestampConverter.ts` | 业务逻辑 Hook,包含转换模式、输入、单位、时区状态和响应式计算管线 | +| `LiveClock.tsx` | 实时时钟子组件,`React.memo` 优化 | +| `ResultView.tsx` | 转换结果展示子组件 | +| `constants.ts` | 时区列表等常量 | + +### StorageCleaner/ + +浏览器存储清理工具,支持 Cookie/LocalStorage/SessionStorage/IndexedDB/Cache/SW 清理。 + +| 文件 | 用途 | +| --------------------------- | -------------- | +| `index.tsx` | 页面 UI | +| `useStorageCleaner.ts` | 业务逻辑 Hook | +| `StorageOptionsGrid.tsx` | 清理选项网格 | +| `StorageCleanerConfirm.tsx` | 清理确认对话框 | +| `AutoRefreshToggle.tsx` | 自动刷新开关 | +| `ErrorDisplay.tsx` | 错误展示组件 | +| `CleaningResult.tsx` | 清理结果展示 | +| `OptionItem.tsx` | 单个选项组件 | + +### QrCode/ + +二维码工具,支持生成(URL→QR)和解析(QR→URL)。 + +| 文件/目录 | 用途 | +| ------------- | ---------------- | +| `index.tsx` | 页面 UI | +| `types.ts` | 类型定义 | +| `contexts/` | Context Provider | +| `hooks/` | 业务逻辑 Hooks | +| `components/` | 子组件 | + +### TextStatistics/ + +文本统计工具,实时计算字符数、单词数、行数、字节大小。 + +| 文件 | 用途 | +| ----------- | --------------------------------------------- | +| `index.tsx` | 页面组件,集成 `TextInputArea` 和统计结果展示 | + +### Jwt/ + +JWT 解析工具,解码 Header/Payload/Signature。 + +| 文件/目录 | 用途 | +| ------------- | ---------------- | +| `index.tsx` | 页面 UI | +| `types.ts` | 类型定义 | +| `contexts/` | Context Provider | +| `hooks/` | 业务逻辑 Hooks | +| `components/` | 子组件 | + +### JsonTools/ + +JSON 工具集:差异比较、格式化、YAML/TOML/Minify 转换。 + +| 文件 | 用途 | +| ------------------------ | ---------------------------- | +| `index.tsx` | 页面入口,子模式切换 | +| `types.ts` | 类型定义 | +| `diffEngine.ts` | 差异比较引擎 | +| `JsonDiffInput.tsx` | JSON 输入组件 | +| `DiffResult.tsx` | 差异结果展示 | +| `DiffNavigator.tsx` | 差异导航器 | +| `JsonFormatSection.tsx` | 格式化区域 | +| `JsonConvertSection.tsx` | 转换区域(YAML/TOML/Minify) | +| `JsonTree.tsx` | JSON 树形展示 | + +### Base64Converter/ + +Base64 编解码工具,支持文本/文件/图片三种模式。 + +| 文件 | 用途 | +| ---------------------------- | --------------------- | +| `index.tsx` | 页面入口,子模式切换 | +| `useBase64Converter.ts` | 业务逻辑 Hook | +| `TextMode.tsx` | 文本模式 | +| `Base64ConverterSection.tsx` | 文件/图片通用转换区域 | + +### MarkdownToHtml/ + +Markdown 转 HTML 工具,支持分栏/预览/源码三种视图模式。 + +### HtmlToMarkdown/ + +HTML 转 Markdown 工具,支持分栏/预览/Markdown 三种视图模式。 + +### RightClickRestorer/ + +右键菜单恢复工具,解除网站对右键的限制。 + +| 文件 | 用途 | +| -------------------------- | ------------- | +| `index.tsx` | 页面 UI | +| `useRightClickRestorer.ts` | 业务逻辑 Hook | + +## 新增页面 + +1. 在 `types/storage.d.ts` 添加 `PageType` 联合类型成员 +2. 在 `config/features.tsx` 的 `FEATURES` 数组添加配置 +3. 在 `pages/` 创建页面目录(遵循上述结构) +4. 在 `i18n/locales/{zh,en}/features.json` 添加翻译 +5. 如需新权限,更新 `wxt.config.ts` +6. 添加对应的单元测试 diff --git a/src/providers/README.md b/src/providers/README.md new file mode 100644 index 0000000..22d2209 --- /dev/null +++ b/src/providers/README.md @@ -0,0 +1,63 @@ +# providers/ + +React Context Provider 目录,为整个应用提供全局共享状态。 + +## 文件说明 + +| 文件 | 用途 | +| ----------------------- | ---------------------- | +| `AppRoot.tsx` | 应用根 Provider 组合器 | +| `RouterProvider.tsx` | 路由 Context Provider | +| `ThemeModeProvider.tsx` | 主题模式 Provider | + +## AppRoot.tsx + +应用根 Provider 组合器,按顺序包裹: + +``` +React.StrictMode + └── ThemeModeProvider + └── RouterProvider + └── children +``` + +## RouterProvider.tsx + +路由 Context Provider,管理: + +- **当前页面**:`currentPage`(`PageType`) +- **可见页面列表**:`visiblePages` +- **页面排序**:`pageOrder` +- **加载状态**:`isLoaded` + +核心特性: + +- 通过 `chrome.storage` 持久化路由状态 +- 使用 `localStorage` 快照实现首屏 0 闪烁 +- 支持 popup/sidepanel/tab 三种入口的独立路由同步(通过 `syncKey`、`visiblePagesKey`、`pageOrderKey` 配置) +- 处理右键菜单待处理数据的路由跳转 +- 监听 `chrome.storage.onChanged` 实现跨端同步 + +导出: + +- `RouterProvider` 组件 +- `useRouter()` Hook — 获取 `currentPage`、`visiblePages`、`pageOrder`、`navigateTo`、`goBack` 等 + +## ThemeModeProvider.tsx + +主题模式 Provider,管理: + +- **主题模式**:`light` / `dark` / `system` +- **解析后的主题**:`resolvedTheme`(`light` / `dark`) + +核心特性: + +- 使用 `localStorage` 快照实现首屏 0 闪烁 +- 监听系统级暗色模式变化(`matchMedia`) +- 通过 `chrome.storage` 跨端同步主题偏好 +- 自动在 `document.documentElement` 上切换 `dark` class + +导出: + +- `ThemeModeProvider` 组件 +- `useThemeMode()` Hook — 获取 `themeMode`、`resolvedTheme`、`setThemeMode` diff --git a/src/types/README.md b/src/types/README.md new file mode 100644 index 0000000..67fbff4 --- /dev/null +++ b/src/types/README.md @@ -0,0 +1,42 @@ +# types/ + +全局共享的 TypeScript 类型声明文件目录。 + +## 文件说明 + +### storage.d.ts + +核心类型定义文件,包含: + +**页面类型:** + +- `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:** + +- `StorageSchema` — Chrome Storage 完整数据结构定义,所有存储键必须在此声明 + - 键名使用 kebab-case 格式(如 `app/currentRoute`) + - 包含路由、主题、工具偏好、搜索历史等所有持久化数据 + +**其他类型:** + +- `FormMapEntry` — 表单映射条目定义 +- `ContextMenuPendingData` — 右键菜单待处理数据 +- `StorageCleanerPreferences` / `StorageCleanerOptions` — 存储清理偏好 +- `CleaningResult` / `StorageCleanResult` — 清理结果类型 + +### qrious.d.ts + +`qrious` 库的类型声明,定义 QR 码生成选项和 `QRious` 类。 + +## 修改 StorageSchema 的注意事项 + +修改 `StorageSchema` 时,必须: + +1. 在 `utils/chromeStorage.ts` 添加版本迁移函数 +2. 在测试中覆盖迁移场景 diff --git a/src/utils/README.md b/src/utils/README.md new file mode 100644 index 0000000..a3f8d04 --- /dev/null +++ b/src/utils/README.md @@ -0,0 +1,42 @@ +# utils/ + +通用工具函数和 React 自定义 Hooks 目录,与具体页面解耦。 + +## 工具函数 + +| 文件 | 用途 | +| -------------------- | --------------------------------------------------------------------------------------------------- | +| `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()`) +- 工具函数**不抛异常**,返回包含 `hasError` 和 `error` 字段的结果对象 +- Hook 使用 `use` 前缀命名,定义返回值接口类型 +- 存储操作使用 `chromeStorage.ts` 的 `storageUtil` 封装,不要直接调用 `chrome.storage` +- 消息通信使用 `messages.ts` 的 `sendMessage`/`onMessage`,不要使用原生 `chrome.runtime.sendMessage` diff --git a/vitest.setup.ts b/vitest.setup.ts index abe63e7..97bef60 100644 --- a/vitest.setup.ts +++ b/vitest.setup.ts @@ -68,6 +68,36 @@ vi.mock('@/components/CopyButton', () => ({ ), })); +vi.mock('@/components/CopyButton', () => ({ + CopyButton: ({ + text, + tooltip, + onClick, + }: { + text: string; + tooltip?: string; + onClick?: (e: React.MouseEvent) => void; + }) => + React.createElement( + 'button', + { + 'aria-label': tooltip || 'copy', + type: 'button', + onClick: async (e: React.MouseEvent) => { + if (text) { + try { + await navigator.clipboard.writeText(text); + } catch { + // 与真实 CopyButton 行为一致:失败时静默处理 + } + } + onClick?.(e); + }, + }, + 'Copy', + ), +})); + const storageMock = { local: { get: vi.fn().mockImplementation(() => Promise.resolve({})),