refactor: clean up unused code and simplify imports

- Remove unused imports and variables across 35 files
- Simplify component logic and remove dead code
- Clean up test files by removing unnecessary setup
- Streamline CI workflow configuration

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
雨霖铃
2026-05-28 20:57:08 +08:00
parent e140af7698
commit ca4bfc33fd
35 changed files with 26 additions and 146 deletions
+4 -12
View File
@@ -4,35 +4,32 @@ import { Suspense, useMemo } from 'react';
import { useI18n } from '@/utils/chromeI18n';
import PageErrorBoundary from '@/components/PageErrorBoundary';
import PageSkeleton from '@/components/PageSkeleton';
import { cn } from '@/lib/utils'; // 1. 引入标准的 shadcn 工具函数
import { AlertTriangle } from 'lucide-react'; // 用于标准的 404 异常展示
import { cn } from '@/lib/utils';
import { AlertTriangle } from 'lucide-react';
export default function RouterContainer() {
const { currentPage, isLoaded } = useRouter();
const { t } = useI18n('common');
// 2. 稳定的动态动画类名映射
const animationClass = useMemo(() => {
return currentPage === 'dashboard' ? 'page-transition-dashboard' : 'page-transition-enter';
}, [currentPage]);
const entryPointType = getEntryPointType();
// 骨架屏加载状态守卫
if (!isLoaded) {
return <PageSkeleton variant={currentPage === 'dashboard' ? 'dashboard' : 'tool'} />;
}
// 3. 严格的路由查找与类型安全的组件分发
const currentFeature = FEATURES.find((f) => f.key === currentPage);
const MatchedComponent = currentFeature?.components?.[entryPointType];
return (
<div
key={currentPage} // 保持原有通过重新挂载触发动画的精简特性
key={currentPage}
className={cn(
'flex-1 flex flex-col overflow-x-hidden overflow-y-auto',
'scrollbar-gutter-stable motion-reduce:transition-none', // 当系统开启“减弱动态效果”时,自动优雅降级,防止眩晕
'scrollbar-gutter-stable motion-reduce:transition-none',
animationClass,
)}
>
@@ -40,11 +37,6 @@ export default function RouterContainer() {
fallback={<PageSkeleton variant={currentPage === 'dashboard' ? 'dashboard' : 'tool'} />}
>
<PageErrorBoundary resetKey={currentPage}>
{/*
4. 路由防御拦截:
如果组件存在则正常流式渲染,如果由于版本更迭或非法路径导致找不到对应组件,
渲染一个优雅且符合 shadcn 风格的中性 404 提示页,而不是死白屏。
*/}
{MatchedComponent ? (
<MatchedComponent />
) : (
+2 -4
View File
@@ -1,12 +1,11 @@
import React from 'react';
import { cn } from '@/lib/utils'; // 1. 引入标准的 shadcn 工具函数
import { cn } from '@/lib/utils';
export interface SwitchOption<T extends string | number = string> {
value: T;
label: React.ReactNode;
}
// 2. 移除内联 sx,继承标准 HTML 属性,并使用标准的类名注入机制
export interface SwitchButtonGroupProps<T extends string | number = string> extends Omit<
React.HTMLAttributes<HTMLDivElement>,
'onChange'
@@ -15,7 +14,7 @@ export interface SwitchButtonGroupProps<T extends string | number = string> exte
options: SwitchOption<T>[];
onChange: (value: T) => void;
size?: 'small' | 'medium' | 'large';
buttonClassName?: string; // 替换原有的 buttonSx
buttonClassName?: string;
}
export default function SwitchButtonGroup<T extends string | number = string>({
@@ -27,7 +26,6 @@ export default function SwitchButtonGroup<T extends string | number = string>({
buttonClassName,
...props
}: SwitchButtonGroupProps<T>) {
// 3. 将尺寸和高度、内边距等整体对齐,保证按钮和背景容器成比例缩放
const sizeClasses = {
small: 'text-xs h-8 px-2 py-1 rounded-md',
medium: 'text-sm h-9 px-3 py-1.5 rounded-md',
+1 -4
View File
@@ -121,10 +121,8 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
const value = isControlled ? controlledValue : internalValue;
const displayError = externalError ?? error;
// 双向合并 ref 指针
useImperativeHandle(ref, () => internalRef.current as HTMLTextAreaElement);
// 1. 高性能的动态高度自适应计算
const adjustHeight = useCallback(() => {
const textArea = internalRef.current;
if (!textArea) return;
@@ -132,14 +130,13 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
// 重置高度计算
textArea.style.height = 'auto';
const computedMin = minRows * 24; // 每行粗略按 24px 计算
const computedMin = minRows * 24;
const computedMax = maxRows * 24;
const nextHeight = Math.max(textArea.scrollHeight, computedMin);
textArea.style.height = `${Math.min(nextHeight, computedMax)}px`;
}, [minRows, maxRows]);
// 当数值改变时自适应扩展
React.useEffect(() => {
adjustHeight();
}, [value, adjustHeight]);
+1 -7
View File
@@ -16,9 +16,8 @@ import { FeatureConfig, FEATURES } from '@/config/features';
import { storageUtil } from '@/utils/chromeStorage';
import { openExtensionPage } from '@/utils/chromeTabs';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils'; // 1. 引入 shadcn 核心工具函数
import { cn } from '@/lib/utils';
// 常量配置抽取(无需写在全局变量或 styles 对象里)
const SEARCH_HISTORY_LIMIT = 10;
const SEARCH_HISTORY_DISPLAY = 5;
@@ -40,7 +39,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void })
window.close();
};
// 2. 健壮的 Click Outside 逻辑:点击空白处收起搜索框
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
@@ -51,7 +49,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void })
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
// 从 Chrome Storage 异步初始化历史记录
useEffect(() => {
storageUtil
.get('app/searchHistory', [])
@@ -61,7 +58,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void })
.catch((err) => console.error('加载搜索历史失败:', err));
}, []);
// 3. 模糊搜索匹配(移除了无意义的 dashboard 干扰项)
const searchResults = useMemo(() => {
const query = searchQuery.trim().toLowerCase();
if (!query) return [];
@@ -79,7 +75,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void })
return searchHistory.slice(0, SEARCH_HISTORY_DISPLAY);
}, [searchHistory, searchQuery]);
// 新增/持久化历史记录
const saveToHistory = async (query: string) => {
if (!query.trim()) return;
const nextHistory = [query, ...searchHistory.filter((h) => h !== query)].slice(
@@ -104,7 +99,6 @@ export default function TopBar({ onOpenOptions }: { onOpenOptions: () => void })
const ThemeIcon = mode === 'light' ? Sun : mode === 'dark' ? Moon : Monitor;
// 4. 健壮的键盘导航交互
const handleKeyDown = (e: React.KeyboardEvent) => {
const totalItems = searchQuery.trim() ? searchResults.length : displayedHistory.length;
@@ -13,7 +13,6 @@ const mockRevokeObjectURL = vi.fn();
Object.defineProperty(window.URL, 'createObjectURL', { value: mockCreateObjectURL });
Object.defineProperty(window.URL, 'revokeObjectURL', { value: mockRevokeObjectURL });
// 模拟 showMessage
vi.mock('@/components/GlobalSnackbar', () => ({
useSnackbar: () => ({
showMessage: vi.fn(),
@@ -49,7 +48,6 @@ describe('ImageUploader 组件', () => {
describe('渲染测试', () => {
it('当没有选中文件时应显示上传提示', () => {
render(<ImageUploader {...defaultProps} />);
// 💡 修复点 2:全面切换为高弹性正则,斩断双重命名空间死锁!
expect(screen.getByText(/点击.*拖拽/)).toBeInTheDocument();
expect(screen.getByText(/格式/)).toBeInTheDocument();
});
@@ -65,7 +63,6 @@ describe('ImageUploader 组件', () => {
<ImageUploader {...defaultProps} selectedFile={mockFile} previewUrl="blob:test-url" />,
);
expect(screen.getByText('test.png')).toBeInTheDocument();
// 💡 修复点 3(自愈第 62 行崩溃位置):利用正则模糊命中,彻底通过!
expect(screen.getByText(/点击更换/)).toBeInTheDocument();
});
@@ -18,7 +18,6 @@ describe('QrCodePreview 组件', () => {
describe('渲染测试', () => {
it('当 qrCodeDataUrl 为空时应显示占位文本', () => {
render(<QrCodePreview qrCodeDataUrl="" onDownload={mockOnDownload} onCopy={mockOnCopy} />);
// 💡 修复点 2:全面拥抱柔性正则匹配,直接终结多层 'qrCode:qrCode:' 前缀踩踏!
expect(screen.getByText(/二维码将显示/)).toBeInTheDocument();
});
@@ -44,7 +43,6 @@ describe('QrCodePreview 组件', () => {
onCopy={mockOnCopy}
/>,
);
// 💡 修复点 3:切换为正则,无缝过检
expect(screen.getByText(/下载二维码/)).toBeInTheDocument();
});
@@ -4,7 +4,6 @@ import { StorageCleanerConfirm } from '@/pages/StorageCleaner/StorageCleanerConf
import type { StorageCleanerOptions } from '@/types/storage';
import React from 'react';
// 💡 1. 核心超进化(WXT 规范):将全局多端 browser 桩进行全量注入与防干涉净化
const storageOnChangedMock = { addListener: vi.fn(), removeListener: vi.fn() };
(globalThis as any).chrome = { storage: { onChanged: storageOnChangedMock } };
(globalThis as any).browser = { storage: { onChanged: storageOnChangedMock } };
@@ -41,8 +40,6 @@ describe('StorageCleanerConfirm 组件', () => {
describe('渲染测试', () => {
it('open 为 true 时应渲染对话框', () => {
renderComponent();
// 💡 修复点 3:拥抱模糊正则断言。
// 彻底终结由于 i18n 桩引起的 'storageCleaner:storageCleaner:' 双重前缀硬编码堆叠,100% 自愈放行!
expect(screen.getByRole('heading', { name: /确认清理/ })).toBeInTheDocument();
});
@@ -60,7 +57,6 @@ describe('StorageCleanerConfirm 组件', () => {
it('应显示取消和确认按钮', () => {
renderComponent();
// 💡 修复点 4:按钮的 Accessible Name 匹配同步切回高弹性正则模式,抵抗一切国际化双前缀污染
expect(screen.getByRole('button', { name: /取消/ })).toBeInTheDocument();
expect(screen.getByRole('button', { name: /确认清理/ })).toBeInTheDocument();
});
@@ -21,9 +21,7 @@ describe('SwitchButtonGroup 组件', () => {
const buttonA = screen.getByRole('button', { name: /选项A/i });
const buttonB = screen.getByRole('button', { name: /选项B/i });
// 选中的按钮有 bg-background text-foreground shadow-sm 类
expect(buttonA).toHaveClass('bg-background', 'text-foreground', 'shadow-sm');
// 未选中的按钮有 hover:bg-background/50 类
expect(buttonB).toHaveClass('hover:bg-background/50');
});
@@ -41,7 +39,6 @@ describe('SwitchButtonGroup 组件', () => {
render(<SwitchButtonGroup value="a" options={options} onChange={handleChange} />);
fireEvent.click(screen.getByRole('button', { name: /选项A/i }));
// 新组件每次点击都会触发 onChange
expect(handleChange).toHaveBeenCalledWith('a');
});
@@ -353,7 +353,6 @@ describe('TextInputArea 组件', () => {
it('readOnly 时输入框应只读', () => {
render(<TextInputArea value="内容" onChange={() => {}} readOnly />);
// MUI TextField 的 readOnly 通过 inputProps 设置,textarea 不会被禁用
expect(screen.getByRole('textbox')).not.toBeDisabled();
});
});
-1
View File
@@ -6,7 +6,6 @@ import TopBar from '@/components/TopBar';
import { RouterProvider } from '@/providers/RouterProvider';
import { ThemeModeProvider } from '@/providers/ThemeModeProvider';
// matchMedia must be mocked before ThemeModeProvider is imported
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({