Files
testing-tool/config/features.tsx
T
LingandRX 57ea4d9858 Enhance form recognition, optimize UI, and unify components (#19)
- **docs**: 完善组件注释、README 目录结构及 AGENTS.md 文档。
- **refactor**:
  - 提取通用 `PageHeader`、`Button`、`DashboardCard` 及 `ErrorBoundary` 组件。
  - 重构消息通信机制,采用 `@webext-core/messaging` 实现类型安全。
  - 将全局通知系统重构为 `SnackbarProvider` (后合并至 `GlobalSnackbar`)。
  - 迁移样式系统至 MUI 主题,移除冗余 CSS。
  - 优化路由配置,支持独立标签页模式及页面懒加载。
  - 移除未使用文件、URL 工具及表单映射相关功能。
- **feat**:
  - 新增配置导出功能(JSON)及状态提示。
  - 新增侧边栏状态变化通知机制。
  - 新增文本统计及 JWT 解析工具。
  - 优化二维码生成与解析逻辑,换用更轻量的 `qrious` 和 `qr-scanner`。
  - 增强高亮器功能,支持闪烁效果及 Shadow DOM 穿透。
- **style**: 优化仪表盘响应式网格布局及 UI 细节。
- **fix**: 修复 `useStorageState` 依赖缺失及路由初始化性能问题。
- **test**: 更新单元测试以覆盖新增的工具函数及功能特性。
2026-05-03 17:06:32 +08:00

153 lines
4.3 KiB
TypeScript

import React, { ReactNode, lazy } from 'react';
import type { PageType } from '@/types/storage';
import AccessTimeIcon from '@mui/icons-material/AccessTime';
import StorageIcon from '@mui/icons-material/Storage';
import QrCodeIcon from '@mui/icons-material/QrCode';
import DescriptionIcon from '@mui/icons-material/Description';
import VpnKeyIcon from '@mui/icons-material/VpnKey';
import { THEME_COLORS } from './pageTheme';
// 懒加载页面组件
const DashboardPage = lazy(() => import('@/pages/DashboardPage'));
const TimestampPage = lazy(() => import('@/pages/TimestampPage'));
const StorageCleanerPage = lazy(() => import('@/pages/StorageCleanerPage'));
const QrCodePage = lazy(() => import('@/pages/QrCodePage'));
const TextStatisticsPage = lazy(() => import('@/pages/TextStatisticsPage'));
const JwtPage = lazy(() => import('@/pages/JwtPage'));
/**
* 功能配置接口
*
* 整合了路由信息和仪表盘卡片元数据,作为功能的单一事实来源
*/
export interface FeatureConfig {
/** 页面类型标识 */
key: PageType;
/** 功能名称(用于路由标签和卡片标题) */
label: string;
/** 功能描述(用于仪表盘卡片) */
description: string;
/** 主题颜色(用于仪表盘卡片) */
themeColor?: string;
/** 图标组件(用于仪表盘卡片) */
icon?: ReactNode;
/** 默认是否在仪表盘显示 */
defaultVisible: boolean;
/** 不同显示模式对应的组件 */
components: {
/** 弹窗模式组件 */
popup: React.ComponentType;
/** 侧边栏模式组件 */
sidepanel: React.ComponentType;
/** 标签页模式组件 */
tab: React.ComponentType;
};
}
export const FEATURES: FeatureConfig[] = [
{
key: 'dashboard',
label: 'Dashboard',
description: '',
defaultVisible: true,
components: {
popup: DashboardPage,
sidepanel: DashboardPage,
tab: DashboardPage,
},
},
{
key: 'timestamp',
label: '时间戳',
description: 'Unix 毫秒数转换与格式化',
themeColor: THEME_COLORS.primary,
icon: <AccessTimeIcon sx={{ fontSize: 20 }} />,
defaultVisible: true,
components: {
popup: TimestampPage,
sidepanel: TimestampPage,
tab: TimestampPage,
},
},
{
key: 'storageCleaner',
label: '存储清理',
description: '清理缓存、Cookies 及本地存储',
themeColor: THEME_COLORS.warning,
icon: <StorageIcon sx={{ fontSize: 20 }} />,
defaultVisible: true,
components: {
popup: StorageCleanerPage,
sidepanel: StorageCleanerPage,
tab: StorageCleanerPage,
},
},
{
key: 'qrCode',
label: '二维码工具',
description: '生成当前选中的 URL 的二维码',
themeColor: THEME_COLORS.success,
icon: <QrCodeIcon sx={{ fontSize: 20 }} />,
defaultVisible: true,
components: {
popup: QrCodePage,
sidepanel: QrCodePage,
tab: QrCodePage,
},
},
{
key: 'textStatistics',
label: '文本统计',
description: '实时分析文本字符、单词及字节',
themeColor: THEME_COLORS.purple,
icon: <DescriptionIcon sx={{ fontSize: 20 }} />,
defaultVisible: true,
components: {
popup: TextStatisticsPage,
sidepanel: TextStatisticsPage,
tab: TextStatisticsPage,
},
},
{
key: 'jwt',
label: 'JWT 解析',
description: 'JSON Web Token 解码与查看',
themeColor: THEME_COLORS.indigo,
icon: <VpnKeyIcon sx={{ fontSize: 20 }} />,
defaultVisible: true,
components: {
popup: JwtPage,
sidepanel: JwtPage,
tab: JwtPage,
},
},
];
export function getFeatureByKey(key: PageType): FeatureConfig | undefined {
return FEATURES.find((f) => f.key === key);
}
export function getDefaultVisibleFeatureKeys(): PageType[] {
return FEATURES.filter((f) => f.defaultVisible).map((f) => f.key);
}
export function getAllFeatureKeys(): PageType[] {
return FEATURES.map((f) => f.key);
}
export function getDefaultPageOrder(): PageType[] {
return FEATURES.filter((f) => f.key !== 'dashboard').map((f) => f.key);
}
export function getEntryPointType(): 'popup' | 'sidepanel' | 'tab' {
const pathname = window.location.pathname;
if (pathname.includes('sidepanel')) {
return 'sidepanel';
}
if (new URLSearchParams(window.location.search).get('mode') === 'tab') {
return 'tab';
}
return 'popup';
}