refactor: reorganize directory structure into src/
Move all source code directories into src/ for cleaner project structure: - pages/, components/, utils/, config/, providers/, types/, lib/, assets/, entrypoints/ → src/ - Use WXT srcDir config to resolve @/ alias to src/ - Update tsconfig, vitest, eslint, tailwind configs - Remove scattered README.md files from subdirectories - Update documentation (AGENTS.md, CODING_STANDARDS.md, README.md) - Fix pre-existing lint error in RouterProvider.tsx Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+31
-32
@@ -79,12 +79,12 @@ export function isSupportedImageType(mimeType: string): boolean {
|
|||||||
|
|
||||||
| 场景 | 导出方式 | 示例 |
|
| 场景 | 导出方式 | 示例 |
|
||||||
| ----------- | ------------------------------------------------ | --------------------------------- |
|
| ----------- | ------------------------------------------------ | --------------------------------- |
|
||||||
| 页面组件 | `export default function ComponentName()` | `pages/Timestamp/index.tsx` |
|
| 页面组件 | `export default function ComponentName()` | `src/pages/Timestamp/index.tsx` |
|
||||||
| 业务组件 | `const X = React.memo(...)` + `export default X` | `LiveClock.tsx`, `ResultView.tsx` |
|
| 业务组件 | `const X = React.memo(...)` + `export default X` | `LiveClock.tsx`, `ResultView.tsx` |
|
||||||
| UI 原子组件 | `React.forwardRef(...)` + `export { X }` | `components/ui/button.tsx` |
|
| UI 原子组件 | `React.forwardRef(...)` + `export { X }` | `src/components/ui/button.tsx` |
|
||||||
| 工具函数 | `export function xxx()` | `utils/clipboard.ts` |
|
| 工具函数 | `export function xxx()` | `src/utils/clipboard.ts` |
|
||||||
| 自定义 Hook | `export function useXxx()` | `utils/useStorageState.ts` |
|
| 自定义 Hook | `export function useXxx()` | `src/utils/useStorageState.ts` |
|
||||||
| 类型/接口 | `export interface` / `export type` | `types/storage.d.ts` |
|
| 类型/接口 | `export interface` / `export type` | `src/types/storage.d.ts` |
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// ✅ 页面组件 — default export
|
// ✅ 页面组件 — default export
|
||||||
@@ -473,7 +473,7 @@ import ErrorBoundary from '@/components/ErrorBoundary';
|
|||||||
| 类型 | 命名模式 | 示例 |
|
| 类型 | 命名模式 | 示例 |
|
||||||
| ----------- | -------------------------- | -------------------------------------------------------- |
|
| ----------- | -------------------------- | -------------------------------------------------------- |
|
||||||
| 页面目录 | PascalCase | `Timestamp/`, `Base64Converter/`, `StorageCleaner/` |
|
| 页面目录 | PascalCase | `Timestamp/`, `Base64Converter/`, `StorageCleaner/` |
|
||||||
| 页面入口 | `index.tsx` | `pages/Timestamp/index.tsx` |
|
| 页面入口 | `index.tsx` | `src/pages/Timestamp/index.tsx` |
|
||||||
| 组件文件 | PascalCase `.tsx` | `TopBar.tsx`, `LiveClock.tsx`, `ResultView.tsx` |
|
| 组件文件 | PascalCase `.tsx` | `TopBar.tsx`, `LiveClock.tsx`, `ResultView.tsx` |
|
||||||
| 自定义 Hook | camelCase `.ts` | `useTimestampConverter.ts`, `useStorageCleaner.ts` |
|
| 自定义 Hook | camelCase `.ts` | `useTimestampConverter.ts`, `useStorageCleaner.ts` |
|
||||||
| 工具函数 | camelCase `.ts` | `chromeStorage.ts`, `base64Converter.ts`, `clipboard.ts` |
|
| 工具函数 | camelCase `.ts` | `chromeStorage.ts`, `base64Converter.ts`, `clipboard.ts` |
|
||||||
@@ -527,12 +527,12 @@ const handleClean = useCallback(async () => { ... }, []);
|
|||||||
测试文件放在源代码同级的 `__tests__/` 目录下:
|
测试文件放在源代码同级的 `__tests__/` 目录下:
|
||||||
|
|
||||||
```
|
```
|
||||||
utils/__tests__/jwt.test.ts
|
src/utils/__tests__/jwt.test.ts
|
||||||
utils/__tests__/base64Converter.test.ts
|
src/utils/__tests__/base64Converter.test.ts
|
||||||
utils/__tests__/useStorageState.test.ts
|
src/utils/__tests__/useStorageState.test.ts
|
||||||
components/__tests__/SwitchButtonGroup.test.tsx
|
src/components/__tests__/SwitchButtonGroup.test.tsx
|
||||||
components/__tests__/ErrorBoundary.test.tsx
|
src/components/__tests__/ErrorBoundary.test.tsx
|
||||||
pages/Timestamp/__tests__/index.test.tsx
|
src/pages/Timestamp/__tests__/index.test.tsx
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7.2 describe / it 命名
|
### 7.2 describe / it 命名
|
||||||
@@ -653,16 +653,16 @@ export function useTimestampConverter(): UseTimestampConverterReturn { ... }
|
|||||||
|
|
||||||
### 8.2 Hook 存放位置
|
### 8.2 Hook 存放位置
|
||||||
|
|
||||||
- **全局通用 Hook**:放在 `utils/` 目录下
|
- **全局通用 Hook**:放在 `src/utils/` 目录下
|
||||||
- **页面专属 Hook**:与页面组件同目录
|
- **页面专属 Hook**:与页面组件同目录
|
||||||
|
|
||||||
```
|
```
|
||||||
utils/useStorageState.ts — Chrome Storage 状态持久化
|
src/utils/useStorageState.ts — Chrome Storage 状态持久化
|
||||||
utils/useLazyTranslation.ts — i18n 懒加载
|
src/utils/useLazyTranslation.ts — i18n 懒加载
|
||||||
utils/useContextMenuData.ts — 右键菜单数据
|
src/utils/useContextMenuData.ts — 右键菜单数据
|
||||||
utils/useDebounce.ts — 防抖
|
src/utils/useDebounce.ts — 防抖
|
||||||
pages/Timestamp/useTimestampConverter.ts — 页面级 Hook
|
src/pages/Timestamp/useTimestampConverter.ts — 页面级 Hook
|
||||||
pages/StorageCleaner/useStorageCleaner.ts — 页面级 Hook
|
src/pages/StorageCleaner/useStorageCleaner.ts — 页面级 Hook
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -767,7 +767,7 @@ const [themeMode, setThemeMode, isInitialized] = useStorageState(
|
|||||||
### 11.1 页面组件结构
|
### 11.1 页面组件结构
|
||||||
|
|
||||||
```
|
```
|
||||||
pages/FeatureName/
|
src/pages/FeatureName/
|
||||||
├── index.tsx # 页面 UI(纯展示,使用 shadcn/ui 组件)
|
├── index.tsx # 页面 UI(纯展示,使用 shadcn/ui 组件)
|
||||||
├── useFeatureName.ts # 业务逻辑 Hook(状态管理 + 转换逻辑)
|
├── useFeatureName.ts # 业务逻辑 Hook(状态管理 + 转换逻辑)
|
||||||
├── constants.ts # 常量定义(可选)
|
├── constants.ts # 常量定义(可选)
|
||||||
@@ -780,18 +780,17 @@ pages/FeatureName/
|
|||||||
### 11.2 目录职责
|
### 11.2 目录职责
|
||||||
|
|
||||||
| 目录 | 职责 |
|
| 目录 | 职责 |
|
||||||
| ---------------- | ------------------------------------------------------------ |
|
| -------------------- | ------------------------------------------------------------ |
|
||||||
| `config/` | 应用配置(功能定义、路由映射) |
|
| `src/config/` | 应用配置(功能定义、路由映射) |
|
||||||
| `entrypoints/` | 扩展入口点(popup、options、sidepanel、background、content) |
|
| `src/entrypoints/` | 扩展入口点(popup、options、sidepanel、background、content) |
|
||||||
| `pages/` | 功能页面组件(懒加载) |
|
| `src/pages/` | 功能页面组件(懒加载) |
|
||||||
| `components/` | 可复用 UI 组件 |
|
| `src/components/` | 可复用 UI 组件 |
|
||||||
| `components/ui/` | shadcn/ui 基础组件(button、dialog、select 等) |
|
| `src/components/ui/` | shadcn/ui 基础组件(button、dialog、select 等) |
|
||||||
| `providers/` | React Context(Router、Theme 等) |
|
| `src/providers/` | React Context(Router、Theme 等) |
|
||||||
| `hooks/` | 自定义 React Hooks |
|
| `src/hooks/` | 自定义 React Hooks |
|
||||||
| `utils/` | 工具函数与服务抽象 |
|
| `src/utils/` | 工具函数与服务抽象 |
|
||||||
| `types/` | TypeScript 类型声明 |
|
| `src/types/` | TypeScript 类型声明 |
|
||||||
| `lib/` | 通用工具函数(cn、utils) |
|
| `src/lib/` | 通用工具函数(cn、utils) |
|
||||||
| `i18n/` | 国际化资源 |
|
|
||||||
| `public/` | 静态资源 |
|
| `public/` | 静态资源 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -43,16 +43,18 @@ Pre-commit hook(`.husky/pre-commit` 调用 `lint-staged`,任一步骤返回
|
|||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
```
|
```
|
||||||
config/features.tsx # 功能定义(路由 + 元数据的单一事实来源)
|
src/ # 源代码根目录
|
||||||
entrypoints/ # 扩展入口点 (popup/, options/, sidepanel/, background.ts, content.ts)
|
config/features.tsx # 功能定义(路由 + 元数据的单一事实来源)
|
||||||
pages/ # 功能页面组件 (懒加载)
|
entrypoints/ # 扩展入口点 (popup/, options/, sidepanel/, background.ts, content.ts)
|
||||||
components/ # 可复用 UI 组件
|
pages/ # 功能页面组件 (懒加载)
|
||||||
components/ui/ # shadcn/ui 基础组件 (button, dialog, select 等)
|
components/ # 可复用 UI 组件
|
||||||
providers/ # React Context (Router, Theme 等)
|
components/ui/ # shadcn/ui 基础组件 (button, dialog, select 等)
|
||||||
hooks/ # 自定义 React Hooks
|
providers/ # React Context (Router, Theme 等)
|
||||||
utils/ # 工具函数与服务抽象
|
hooks/ # 自定义 React Hooks
|
||||||
types/ # TypeScript 类型声明
|
utils/ # 工具函数与服务抽象
|
||||||
i18n/locales/{zh,en}/ # 国际化资源 (common.json, features.json 及各功能独立 JSON)
|
types/ # TypeScript 类型声明
|
||||||
|
lib/ # 通用工具函数(cn、utils)
|
||||||
|
public/ # 静态资源(图标、_locales 等)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 页面组件模式
|
### 页面组件模式
|
||||||
@@ -60,7 +62,7 @@ i18n/locales/{zh,en}/ # 国际化资源 (common.json, features.json 及各功
|
|||||||
典型功能页面遵循 **UI + Hook 分离** 模式:
|
典型功能页面遵循 **UI + Hook 分离** 模式:
|
||||||
|
|
||||||
```
|
```
|
||||||
pages/FeatureName/
|
src/pages/FeatureName/
|
||||||
├── index.tsx # 页面 UI(纯展示,使用 shadcn/ui 组件)
|
├── index.tsx # 页面 UI(纯展示,使用 shadcn/ui 组件)
|
||||||
├── useFeatureName.ts # 业务逻辑 Hook(状态管理 + 转换逻辑)
|
├── useFeatureName.ts # 业务逻辑 Hook(状态管理 + 转换逻辑)
|
||||||
└── constants.ts # 常量定义
|
└── constants.ts # 常量定义
|
||||||
@@ -72,15 +74,15 @@ pages/FeatureName/
|
|||||||
|
|
||||||
## 关键架构决策
|
## 关键架构决策
|
||||||
|
|
||||||
**路由**: 不使用 React Router。通过 `config/features.tsx` 的 `FEATURES` 数组管理,`RouterProvider` 根据 `PageType`
|
**路由**: 不使用 React Router。通过 `src/config/features.tsx` 的 `FEATURES` 数组管理,`RouterProvider` 根据 `PageType`
|
||||||
渲染对应组件。支持三种渲染模式:popup(弹窗)、sidepanel(侧边栏)和 browser-tab(浏览器新标签页,通过 `open_in_tab` 打开)。
|
渲染对应组件。支持三种渲染模式:popup(弹窗)、sidepanel(侧边栏)和 browser-tab(浏览器新标签页,通过 `open_in_tab` 打开)。
|
||||||
每种模式有独立的路由和可见页面配置(`app/popupRoute`、`app/sidepanelRoute`、`app/tabRoute` 等)。
|
每种模式有独立的路由和可见页面配置(`app/popupRoute`、`app/sidepanelRoute`、`app/tabRoute` 等)。
|
||||||
|
|
||||||
**存储**: 所有 Chrome Storage 键必须在 `types/storage.d.ts` 的 `StorageSchema` 中定义,键名使用 kebab-case 格式(如 `app/currentRoute`)。
|
**存储**: 所有 Chrome Storage 键必须在 `src/types/storage.d.ts` 的 `StorageSchema` 中定义,键名使用 kebab-case 格式(如 `app/currentRoute`)。
|
||||||
使用 `utils/chromeStorage.ts` 及其 Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照以消除首屏闪烁。
|
使用 `src/utils/chromeStorage.ts` 及其 Hook。Router 同时使用 `chrome.storage.local` 和 `localStorage` 做快照以消除首屏闪烁。
|
||||||
修改 StorageSchema 时,必须在 `utils/chromeStorage.ts` 添加版本迁移函数,并在测试中覆盖迁移场景。
|
修改 StorageSchema 时,必须在 `src/utils/chromeStorage.ts` 添加版本迁移函数,并在测试中覆盖迁移场景。
|
||||||
|
|
||||||
**通信**: 使用 `@webext-core/messaging`,协议定义在 `utils/messages.ts`。
|
**通信**: 使用 `@webext-core/messaging`,协议定义在 `src/utils/messages.ts`。
|
||||||
|
|
||||||
**路径别名**: `@/` 映射到项目根目录 (已在 tsconfig 和 vitest.config 中配置)。
|
**路径别名**: `@/` 映射到项目根目录 (已在 tsconfig 和 vitest.config 中配置)。
|
||||||
|
|
||||||
@@ -116,9 +118,9 @@ pages/FeatureName/
|
|||||||
|
|
||||||
## 新功能开发清单
|
## 新功能开发清单
|
||||||
|
|
||||||
1. 在 `types/storage.d.ts` 添加 `PageType` 联合类型
|
1. 在 `src/types/storage.d.ts` 添加 `PageType` 联合类型
|
||||||
2. 在 `config/features.tsx` 的 `FEATURES` 数组添加配置(指定 key、翻译键、图标、三种渲染模式的组件)
|
2. 在 `src/config/features.tsx` 的 `FEATURES` 数组添加配置(指定 key、翻译键、图标、三种渲染模式的组件)
|
||||||
3. 在 `pages/` 创建页面组件 (懒加载):
|
3. 在 `src/pages/` 创建页面组件 (懒加载):
|
||||||
- `index.tsx` — UI 组件,使用 `useLazyTranslation` 获取翻译
|
- `index.tsx` — UI 组件,使用 `useLazyTranslation` 获取翻译
|
||||||
- `useFeatureName.ts` — 业务逻辑 Hook
|
- `useFeatureName.ts` — 业务逻辑 Hook
|
||||||
- `constants.ts` — 常量(可选)
|
- `constants.ts` — 常量(可选)
|
||||||
@@ -131,7 +133,7 @@ pages/FeatureName/
|
|||||||
- 禁止使用 `any` (测试文件除外)
|
- 禁止使用 `any` (测试文件除外)
|
||||||
- 未使用变量/参数: 使用 `_` 前缀 (如 `_unused`)
|
- 未使用变量/参数: 使用 `_` 前缀 (如 `_unused`)
|
||||||
- 样式: 使用 Tailwind CSS + shadcn/ui (通过 `className` 和 `cn()` 工具)
|
- 样式: 使用 Tailwind CSS + shadcn/ui (通过 `className` 和 `cn()` 工具)
|
||||||
- UI 组件: 优先使用 `components/ui/` 下的 shadcn/ui 组件 (button, dialog, select 等)
|
- UI 组件: 优先使用 `src/components/ui/` 下的 shadcn/ui 组件 (button, dialog, select 等)
|
||||||
- 图标: 使用 `lucide-react` 图标库
|
- 图标: 使用 `lucide-react` 图标库
|
||||||
- 格式: Prettier (`.prettierrc`: 100 字符宽, 单引号, 尾逗号 all, LF 换行)
|
- 格式: Prettier (`.prettierrc`: 100 字符宽, 单引号, 尾逗号 all, LF 换行)
|
||||||
- ESLint 使用 `typescript-eslint` 的 `projectService: true`(无需手动维护 project 路径)
|
- ESLint 使用 `typescript-eslint` 的 `projectService: true`(无需手动维护 project 路径)
|
||||||
|
|||||||
@@ -84,22 +84,22 @@
|
|||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
```text
|
```text
|
||||||
├── components/ # 可复用 React 组件
|
├── src/ # 源代码根目录
|
||||||
├── config/ # 应用配置
|
│ ├── components/ # 可复用 React 组件
|
||||||
│ └── features.tsx # 功能定义与路由映射
|
│ ├── config/ # 应用配置
|
||||||
├── entrypoints/ # 扩展程序入口点
|
│ │ └── features.tsx # 功能定义与路由映射
|
||||||
│ ├── popup/ # 点击图标弹出的主界面
|
│ ├── entrypoints/ # 扩展程序入口点
|
||||||
│ ├── options/ # 扩展程序设置页面
|
│ │ ├── popup/ # 点击图标弹出的主界面
|
||||||
│ ├── sidepanel/ # 浏览器侧边栏集成
|
│ │ ├── options/ # 扩展程序设置页面
|
||||||
│ ├── background.ts # 后台 Service Worker
|
│ │ ├── sidepanel/ # 浏览器侧边栏集成
|
||||||
│ └── content.ts # 网页注入脚本
|
│ │ ├── background.ts # 后台 Service Worker
|
||||||
├── pages/ # 各功能模块的页面组件
|
│ │ └── content.ts # 网页注入脚本
|
||||||
├── providers/ # 全局状态提供者 (Router, Theme 等)
|
│ ├── pages/ # 各功能模块的页面组件
|
||||||
├── hooks/ # 自定义 React Hooks
|
│ ├── providers/ # 全局状态提供者 (Router, Theme 等)
|
||||||
├── utils/ # 工具函数与服务抽象
|
│ ├── hooks/ # 自定义 React Hooks
|
||||||
├── types/ # TypeScript 类型声明
|
│ ├── utils/ # 工具函数与服务抽象
|
||||||
├── lib/ # 通用工具函数 (cn, utils 等)
|
│ ├── types/ # TypeScript 类型声明
|
||||||
├── i18n/ # 国际化资源
|
│ └── lib/ # 通用工具函数 (cn, utils 等)
|
||||||
├── public/ # 静态资源 (图标、manifest 资源等)
|
├── public/ # 静态资源 (图标、manifest 资源等)
|
||||||
├── wxt.config.ts # WXT 框架核心配置
|
├── wxt.config.ts # WXT 框架核心配置
|
||||||
└── package.json # 项目元数据与依赖管理
|
└── package.json # 项目元数据与依赖管理
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
# 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`
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# 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 <component-name>
|
|
||||||
```
|
|
||||||
|
|
||||||
组件配置在项目根目录的 `components.json` 中定义。
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# entrypoints/
|
|
||||||
|
|
||||||
WXT 框架要求的扩展生命周期入口点,对应 Chrome Extension 的各个上下文。
|
|
||||||
|
|
||||||
## 入口文件
|
|
||||||
|
|
||||||
| 文件 | 用途 |
|
|
||||||
| ------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
||||||
| `background.ts` | Service Worker 入口:注册右键菜单、监听菜单点击、处理消息通信、管理侧边栏状态、注入主环境脚本 |
|
|
||||||
| `content.ts` | Content Script 入口:注入所有页面(`<all_urls>`),在 `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` 的不同配置实现独立路由
|
|
||||||
+1
-8
@@ -28,14 +28,7 @@ export default tseslint.config(
|
|||||||
|
|
||||||
// 4. 核心业务全受控大管线(Hooks, Entrypoints, Components 统一护航)
|
// 4. 核心业务全受控大管线(Hooks, Entrypoints, Components 统一护航)
|
||||||
{
|
{
|
||||||
files: [
|
files: ['src/**/*.{ts,tsx}'],
|
||||||
'hooks/**/*.{ts,tsx}',
|
|
||||||
'entrypoints/**/*.{ts,tsx}',
|
|
||||||
'pages/**/*.{ts,tsx}',
|
|
||||||
'utils/**/*.{ts,tsx}',
|
|
||||||
'components/**/*.{ts,tsx}',
|
|
||||||
'services/**/*.{ts,tsx}',
|
|
||||||
],
|
|
||||||
ignores: ['**/__tests__/**', '**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
|
ignores: ['**/__tests__/**', '**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}'],
|
||||||
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
# lib/
|
|
||||||
|
|
||||||
通用库工具目录,存放与业务无关的底层工具函数。
|
|
||||||
|
|
||||||
## 文件说明
|
|
||||||
|
|
||||||
| 文件 | 用途 |
|
|
||||||
| ---------- | ------------------------------------ |
|
|
||||||
| `utils.ts` | `cn()` 函数 — shadcn/ui 标准工具函数 |
|
|
||||||
|
|
||||||
## cn()
|
|
||||||
|
|
||||||
组合 `clsx` + `tailwind-merge`,用于合并和去重 Tailwind CSS 类名:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
|
|
||||||
// 条件类名 + 合并外部 className
|
|
||||||
<div className={cn(
|
|
||||||
'flex items-center gap-3 px-3',
|
|
||||||
isActive && 'bg-primary text-primary-foreground',
|
|
||||||
className,
|
|
||||||
)}>
|
|
||||||
```
|
|
||||||
|
|
||||||
所有需要动态合并 Tailwind 类名的场景都应使用 `cn()`,而非手动拼接字符串。
|
|
||||||
-140
@@ -1,140 +0,0 @@
|
|||||||
# 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. 添加对应的单元测试
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
# 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`
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# 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,确保透明背景
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# 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 的语义化类名
|
|
||||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -1,4 +1,4 @@
|
|||||||
import '../.wxt/types/imports.d.ts';
|
import '../../.wxt/types/imports.d.ts';
|
||||||
import { browser } from 'wxt/browser';
|
import { browser } from 'wxt/browser';
|
||||||
import { MessageAction, onMessage, sendMessage } from '@/utils/messages';
|
import { MessageAction, onMessage, sendMessage } from '@/utils/messages';
|
||||||
import { createAllContextMenus, parseContextMenuClick } from '@/utils/contextMenu';
|
import { createAllContextMenus, parseContextMenuClick } from '@/utils/contextMenu';
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../.wxt/types/imports.d.ts';
|
import '../../.wxt/types/imports.d.ts';
|
||||||
import { initMessageHandler } from './content/messageHandler';
|
import { initMessageHandler } from './content/messageHandler';
|
||||||
|
|
||||||
export default defineContentScript({
|
export default defineContentScript({
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import AppRoot from '@/providers/AppRoot';
|
import AppRoot from '@/providers/AppRoot';
|
||||||
import '@/src/index.css';
|
import '@/index.css';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import AppRoot from '@/providers/AppRoot';
|
import AppRoot from '@/providers/AppRoot';
|
||||||
import '@/src/index.css';
|
import '@/index.css';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import AppRoot from '@/providers/AppRoot';
|
import AppRoot from '@/providers/AppRoot';
|
||||||
import '@/src/index.css';
|
import '@/index.css';
|
||||||
import App from './App.tsx';
|
import App from './App.tsx';
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user