diff --git a/src/components/PageSkeleton.tsx b/src/components/PageSkeleton.tsx
index cf7ed82..35c5bdb 100644
--- a/src/components/PageSkeleton.tsx
+++ b/src/components/PageSkeleton.tsx
@@ -4,30 +4,6 @@
* 用于 Suspense fallback 和初始加载状态,提供平滑的视觉过渡
* 避免白屏闪烁,减少布局偏移
*/
-interface PageSkeletonProps {
- /** 骨架屏类型 */
- variant?: 'dashboard' | 'tool';
-}
-
-/**
- * 仪表盘卡片骨架屏
- */
-function DashboardCardSkeleton() {
- return (
-
- );
-}
/**
* 工具页面骨架屏
@@ -57,22 +33,9 @@ function ToolPageSkeleton() {
/**
* 页面加载骨架屏
- *
- * @param props - PageSkeletonProps
- * @returns 骨架屏 JSX 元素
*/
-export default function PageSkeleton({ variant = 'dashboard' }: PageSkeletonProps) {
- if (variant === 'tool') {
- return ;
- }
-
- return (
-
- {Array.from({ length: 6 }).map((_, index) => (
-
- ))}
-
- );
+export default function PageSkeleton() {
+ return ;
}
PageSkeleton.displayName = 'PageSkeleton';
diff --git a/src/components/RouterContainer.tsx b/src/components/RouterContainer.tsx
index ef29af1..86186ab 100644
--- a/src/components/RouterContainer.tsx
+++ b/src/components/RouterContainer.tsx
@@ -30,7 +30,7 @@ function LoadedPage({ pageKey }: { pageKey: PageType }) {
}, [pageKey]);
if (!Page) {
- return ;
+ return ;
}
return ;
@@ -39,9 +39,6 @@ function LoadedPage({ pageKey }: { pageKey: PageType }) {
export default function RouterContainer() {
const { currentPage } = useRouter();
- const animationClass =
- currentPage === 'dashboard' ? 'page-transition-dashboard' : 'page-transition-enter';
-
const currentFeature = getFeatureByKey(currentPage);
return (
@@ -49,8 +46,7 @@ export default function RouterContainer() {
key={currentPage}
className={cn(
'flex-1 flex flex-col overflow-x-hidden overflow-y-auto',
- 'motion-reduce:transition-none',
- animationClass,
+ 'motion-reduce:transition-none page-transition-enter',
)}
>
diff --git a/src/components/__tests__/PageSkeleton.test.tsx b/src/components/__tests__/PageSkeleton.test.tsx
index 34ae7bf..3839723 100644
--- a/src/components/__tests__/PageSkeleton.test.tsx
+++ b/src/components/__tests__/PageSkeleton.test.tsx
@@ -4,41 +4,17 @@ import PageSkeleton from '@/components/PageSkeleton';
describe('PageSkeleton 组件', () => {
describe('渲染测试', () => {
- it('默认应渲染 dashboard 骨架屏', () => {
+ it('应渲染工具页面骨架', () => {
const { container } = render();
- // dashboard 骨架屏包含 6 个卡片
- const cards = container.querySelectorAll('.rounded-xl');
- expect(cards.length).toBe(6);
- });
-
- it('variant 为 dashboard 时应渲染仪表盘卡片骨架', () => {
- const { container } = render();
-
- // 每个卡片有 2 个骨架元素(图标、文本),6 个卡片共 12 个
- const cards = container.querySelectorAll('.rounded-xl');
- expect(cards.length).toBe(6);
- });
-
- it('variant 为 tool 时应渲染工具页面骨架', () => {
- const { container } = render();
-
- // tool 骨架屏包含标题、输入区、控制栏 3 个按钮、结果区
const skeletons = container.querySelectorAll('.animate-pulse');
expect(skeletons.length).toBeGreaterThan(0);
});
});
describe('布局结构测试', () => {
- it('dashboard 骨架屏应使用 grid 布局', () => {
- const { container } = render();
- const gridContainer = container.firstChild as HTMLElement;
-
- expect(gridContainer).toHaveClass('grid');
- });
-
- it('tool 骨架屏应有内边距', () => {
- const { container } = render();
+ it('工具骨架屏应有内边距', () => {
+ const { container } = render();
const toolContainer = container.firstChild as HTMLElement;
expect(toolContainer).toHaveClass('p-5');
@@ -46,16 +22,8 @@ describe('PageSkeleton 组件', () => {
});
describe('骨架屏元素测试', () => {
- it('dashboard 骨架屏应包含圆角和边框样式', () => {
- const { container } = render();
-
- // 获取第一个卡片容器
- const card = container.querySelector('.rounded-xl.border');
- expect(card).toBeInTheDocument();
- });
-
- it('tool 骨架屏应包含动画脉冲效果', () => {
- const { container } = render();
+ it('工具骨架屏应包含动画脉冲效果', () => {
+ const { container } = render();
const skeletons = container.querySelectorAll('.animate-pulse');
expect(skeletons.length).toBeGreaterThan(0);
diff --git a/src/components/__tests__/RouterContainer.test.tsx b/src/components/__tests__/RouterContainer.test.tsx
index 1691e8c..d2e170c 100644
--- a/src/components/__tests__/RouterContainer.test.tsx
+++ b/src/components/__tests__/RouterContainer.test.tsx
@@ -6,12 +6,10 @@ import type { PageType } from '@/types/storage';
import React from 'react';
const mockRouterValue = {
- currentPage: 'dashboard' as PageType,
- visiblePages: ['dashboard', 'timestamp'] as PageType[],
+ currentPage: 'timestamp' as PageType,
+ visiblePages: ['timestamp', 'jwt'] as PageType[],
pageOrder: ['timestamp'] as PageType[],
navigateTo: vi.fn(),
- syncNavigation: vi.fn(),
- goHome: vi.fn(),
setVisiblePages: vi.fn(),
setPageOrder: vi.fn(),
recentlyUsedTools: [] as PageType[],
@@ -34,21 +32,14 @@ describe('RouterContainer 组件', () => {
describe('渲染测试', () => {
it('mount 后应直接渲染页面结构(不等待 storage 加载)', () => {
- mockRouterValue.currentPage = 'dashboard';
+ mockRouterValue.currentPage = 'timestamp';
const { container } = renderWithProvider();
- expect(container.querySelector('.page-transition-dashboard')).toBeInTheDocument();
+ expect(container.querySelector('.page-transition-enter')).toBeInTheDocument();
});
});
describe('动画类测试', () => {
- it('在 dashboard 页面应应用 dashboard 动画类', () => {
- mockRouterValue.currentPage = 'dashboard';
- renderWithProvider();
- const box = document.querySelector('.page-transition-dashboard');
- expect(box).toBeInTheDocument();
- });
-
- it('在非 dashboard 页面应应用 enter 动画类', () => {
+ it('应应用 enter 动画类', () => {
mockRouterValue.currentPage = 'timestamp';
renderWithProvider();
const box = document.querySelector('.page-transition-enter');
@@ -60,7 +51,7 @@ describe('RouterContainer 组件', () => {
it('currentPage 变化时应更新', () => {
const { rerender } = renderWithProvider();
- mockRouterValue.currentPage = 'timestamp';
+ mockRouterValue.currentPage = 'jwt';
rerender({});
const box = document.querySelector('.page-transition-enter');
@@ -70,11 +61,10 @@ describe('RouterContainer 组件', () => {
describe('页面级错误隔离', () => {
it('PageErrorBoundary 应包裹页面内容', () => {
- mockRouterValue.currentPage = 'dashboard';
+ mockRouterValue.currentPage = 'timestamp';
const { container } = renderWithProvider();
- // 验证 RouterContainer 的 Box 结构存在
- const routerBox = container.querySelector('.page-transition-dashboard');
+ const routerBox = container.querySelector('.page-transition-enter');
expect(routerBox).toBeInTheDocument();
});
});
diff --git a/src/config/__tests__/features.lazy.test.ts b/src/config/__tests__/features.lazy.test.ts
index 5588792..69e9ed2 100644
--- a/src/config/__tests__/features.lazy.test.ts
+++ b/src/config/__tests__/features.lazy.test.ts
@@ -4,10 +4,6 @@ const pageLoadTracker = vi.hoisted(() => ({
loaded: [] as string[],
}));
-vi.mock('@/pages/Dashboard', () => {
- pageLoadTracker.loaded.push('Dashboard');
- return { default: () => null };
-});
vi.mock('@/pages/Timestamp', () => {
pageLoadTracker.loaded.push('Timestamp');
return { default: () => null };
@@ -63,24 +59,24 @@ describe('features 懒加载', () => {
it('访问 FEATURES 元数据时不应加载任何页面模块', async () => {
const { FEATURES } = await import('@/config/features');
- expect(FEATURES).toHaveLength(10);
+ expect(FEATURES).toHaveLength(9);
expect(pageLoadTracker.loaded).toEqual([]);
});
it('loadPage 应仅加载对应页面模块', async () => {
const { loadPage } = await import('@/config/pageLoaders/index');
- await loadPage('dashboard');
+ await loadPage('timestamp');
- expect(pageLoadTracker.loaded).toEqual(['Dashboard']);
+ expect(pageLoadTracker.loaded).toEqual(['Timestamp']);
});
it('loadPage 切换页面时不应加载无关页面模块', async () => {
const { loadPage } = await import('@/config/pageLoaders/index');
- await loadPage('timestamp');
+ await loadPage('jwt');
- expect(pageLoadTracker.loaded).toEqual(['Timestamp']);
+ expect(pageLoadTracker.loaded).toEqual(['Jwt']);
expect(pageLoadTracker.loaded).not.toContain('QrCode');
expect(pageLoadTracker.loaded).not.toContain('TestDataGenerator');
});
diff --git a/src/config/__tests__/features.test.ts b/src/config/__tests__/features.test.ts
index d6b06d0..99d8949 100644
--- a/src/config/__tests__/features.test.ts
+++ b/src/config/__tests__/features.test.ts
@@ -9,8 +9,8 @@ import {
describe('features', () => {
describe('FEATURES', () => {
- it('应该有10个功能定义', () => {
- expect(FEATURES).toHaveLength(10);
+ it('应该有9个功能定义', () => {
+ expect(FEATURES).toHaveLength(9);
});
it('应该有每个功能的所有必需属性', () => {
@@ -24,12 +24,9 @@ describe('features', () => {
expect(typeof feature.label).toBe('string');
expect(typeof feature.description).toBe('string');
expect(typeof feature.defaultVisible).toBe('boolean');
-
- if (feature.key !== 'dashboard') {
- expect(feature).toHaveProperty('icon');
- expect(feature).toHaveProperty('themeColorKey');
- expect(typeof feature.themeColorKey).toBe('string');
- }
+ expect(feature).toHaveProperty('icon');
+ expect(feature).toHaveProperty('themeColorKey');
+ expect(typeof feature.themeColorKey).toBe('string');
});
});
@@ -41,13 +38,6 @@ describe('features', () => {
});
describe('getFeatureByKey', () => {
- it('应该返回dashboard功能', () => {
- const feature = getFeatureByKey('dashboard');
- expect(feature).toBeDefined();
- expect(feature?.key).toBe('dashboard');
- expect(feature?.label).toBe('仪表盘');
- });
-
it('应该返回时间戳功能', () => {
const feature = getFeatureByKey('timestamp');
expect(feature).toBeDefined();
@@ -78,9 +68,8 @@ describe('features', () => {
});
});
- it('应该包含仪表盘、时间戳、存储清理、二维码', () => {
+ it('应该包含时间戳、存储清理、二维码', () => {
const visibleKeys = getDefaultVisibleFeatureKeys();
- expect(visibleKeys).toContain('dashboard');
expect(visibleKeys).toContain('timestamp');
expect(visibleKeys).toContain('storageCleaner');
expect(visibleKeys).toContain('qrCode');
@@ -90,8 +79,7 @@ describe('features', () => {
describe('getAllFeatureKeys', () => {
it('应该返回所有功能key', () => {
const allKeys = getAllFeatureKeys();
- expect(allKeys).toHaveLength(10);
- expect(allKeys).toContain('dashboard');
+ expect(allKeys).toHaveLength(9);
expect(allKeys).toContain('timestamp');
expect(allKeys).toContain('storageCleaner');
expect(allKeys).toContain('qrCode');
@@ -105,9 +93,9 @@ describe('features', () => {
});
describe('getDefaultPageOrder', () => {
- it('应该排除仪表盘从页面顺序', () => {
+ it('应该包含所有工具页面', () => {
const pageOrder = getDefaultPageOrder();
- expect(pageOrder).not.toContain('dashboard');
+ expect(pageOrder).toHaveLength(9);
});
it('应该包含时间戳、存储清理、二维码在页面顺序', () => {
@@ -116,10 +104,5 @@ describe('features', () => {
expect(pageOrder).toContain('storageCleaner');
expect(pageOrder).toContain('qrCode');
});
-
- it('应该有9个项目在页面顺序', () => {
- const pageOrder = getDefaultPageOrder();
- expect(pageOrder).toHaveLength(9);
- });
});
});
diff --git a/src/config/featureMeta.ts b/src/config/featureMeta.ts
index e2e2444..6a2637f 100644
--- a/src/config/featureMeta.ts
+++ b/src/config/featureMeta.ts
@@ -25,12 +25,6 @@ export interface FeatureConfig {
}
export const FEATURES: FeatureConfig[] = [
- {
- key: 'dashboard',
- label: '仪表盘',
- description: '',
- defaultVisible: true,
- },
{
key: 'timestamp',
label: '时间戳',
@@ -118,5 +112,5 @@ export function getAllFeatureKeys(): PageType[] {
}
export function getDefaultPageOrder(): PageType[] {
- return FEATURES.filter((f) => f.key !== 'dashboard').map((f) => f.key);
+ return FEATURES.map((f) => f.key);
}
diff --git a/src/config/pageLoaders/dashboard.ts b/src/config/pageLoaders/dashboard.ts
deleted file mode 100644
index e4b3377..0000000
--- a/src/config/pageLoaders/dashboard.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type { ComponentType } from 'react';
-
-export default function loadDashboard(): Promise<{ default: ComponentType }> {
- return import('@/pages/Dashboard');
-}
diff --git a/src/config/pageLoaders/index.ts b/src/config/pageLoaders/index.ts
index d1ff4ad..839504c 100644
--- a/src/config/pageLoaders/index.ts
+++ b/src/config/pageLoaders/index.ts
@@ -16,8 +16,6 @@ function ensurePagesStyles(): Promise {
export function loadPage(key: PageType): Promise {
return ensurePagesStyles().then(() => {
switch (key) {
- case 'dashboard':
- return import('@/config/pageLoaders/dashboard').then((m) => m.default());
case 'timestamp':
return import('@/config/pageLoaders/timestamp').then((m) => m.default());
case 'storageCleaner':
diff --git a/src/entrypoints/popup/App.tsx b/src/entrypoints/popup/App.tsx
index 9086530..c7b3915 100644
--- a/src/entrypoints/popup/App.tsx
+++ b/src/entrypoints/popup/App.tsx
@@ -1,20 +1,25 @@
import RouterProvider from '@/providers/RouterProvider';
import TopBar from '@/layout/TopBar';
+import FeatureNav from '@/layout/FeatureNav';
import RouterContainer from '@/components/RouterContainer';
import ErrorBoundary from '@/components/ErrorBoundary';
export default function App() {
return (
-
-
-
-
-
+
);
diff --git a/src/entrypoints/popup/index.html b/src/entrypoints/popup/index.html
index 98c4a48..1e8cc68 100644
--- a/src/entrypoints/popup/index.html
+++ b/src/entrypoints/popup/index.html
@@ -21,7 +21,7 @@