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
-3
View File
@@ -22,12 +22,10 @@ interface TextModeProps {
export default function TextMode({ onSwitchToImageMode }: TextModeProps = {}) {
const { t } = useI18n('base64Converter');
// 1. 纯净的核心源状态机:只保留输入源和转换方向
const [input, setInput] = useState('');
const [debouncedInput, setDebouncedInput] = useState('');
const [direction, setDirection] = useState<'encode' | 'decode'>('encode');
// 2. 文本高频敲击防抖大闸:斩断频繁进行文本转 Base64 带来的 CPU 计算过热
useEffect(() => {
const handle = setTimeout(() => {
setDebouncedInput(input);
@@ -35,7 +33,6 @@ export default function TextMode({ onSwitchToImageMode }: TextModeProps = {}) {
return () => clearTimeout(handle);
}, [input]);
// 3. 右键联动数据上下文:优雅原地合并受控状态
const handleContextMenuData = useCallback((payload: string) => {
setInput(payload);
setDebouncedInput(payload);
+1 -1
View File
@@ -2,7 +2,7 @@ import { useI18n } from '@/utils/chromeI18n';
import { useStorageState } from '@/utils/useStorageState';
import type { Base64ConverterPageMode } from '@/types/storage';
import TextMode from './TextMode';
import Base64ConverterSection from './Base64ConverterSection'; // ✅ 正确对接全新的一体化大组件
import Base64ConverterSection from './Base64ConverterSection';
import SwitchButtonGroup from '@/components/SwitchButtonGroup';
const VALID_PAGE_MODES: readonly Base64ConverterPageMode[] = ['text', 'file', 'image'];
-2
View File
@@ -229,5 +229,3 @@ const stringifyMultiline = (v: unknown, depth: number): string => {
return formatPrimitive(v);
}
};
// 💡 彻底移除了文件底部引发 TS2484 冲突的 export type { DiffResultProps } 声明
+1 -7
View File
@@ -75,7 +75,7 @@ export default function JsonConvertSection({
placeholder={t(`jsonFormat:${pk}InputPlaceholder`)}
value={input}
onChange={setInput}
externalError={error || runtimeError || undefined} // 融合语法错误与运行时转换错误
externalError={error || runtimeError || undefined}
showClear={true}
allowCopy={true}
minRows={7}
@@ -86,14 +86,12 @@ export default function JsonConvertSection({
{/* Result display */}
{result && result.output ? (
<div className="relative rounded-xl border border-border bg-card text-card-foreground shadow-sm overflow-hidden">
{/* 结果栏精致头部 */}
<div className="flex h-9 items-center justify-between px-4 border-b border-border bg-muted/50 select-none">
<div className="flex gap-4 items-center">
<span className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground/90">
{t(`jsonFormat:${pk}OutputLabel`)}
</span>
{/* 字节比对注入 tabular-nums font-mono,防止容量大小变动时字符横向抽搐 */}
<div className="hidden sm:flex gap-3 items-center font-mono text-[10px] text-muted-foreground/70 tabular-nums">
<span>
{t('jsonFormat:originalSize')}:{' '}
@@ -117,15 +115,11 @@ export default function JsonConvertSection({
/>
</div>
{/* 转换出的数据流承载区:
💡 修复点:移除了互相冲突打架的 select-all 类名,仅保留纯净、支持自由划线选中的 select-text 样式
*/}
<div className="p-4 font-mono text-xs text-foreground/90 whitespace-pre-wrap break-all max-h-[380px] overflow-y-auto leading-relaxed select-text">
{result.output}
</div>
</div>
) : (
/* Empty state */
<div className="p-8 rounded-xl bg-muted/30 border border-dashed border-border/80 text-center flex flex-col items-center justify-center min-h-[120px] select-none">
<p className="text-xs font-semibold text-muted-foreground/80 tracking-wide max-w-[240px] leading-relaxed">
{error ? t('jsonFormat:fixErrorHint') : t(`jsonFormat:${pk}EmptyHint`)}
+2 -2
View File
@@ -146,7 +146,7 @@ export default function Index() {
value={leftInput}
onChange={(val) => {
setLeftInput(val);
setCurrentDiffIndex(0); // 💡 在同一个用户键盘事件中打包批处理,0 副作用开销
setCurrentDiffIndex(0);
}}
error={leftError}
minRows={9}
@@ -157,7 +157,7 @@ export default function Index() {
value={rightInput}
onChange={(val) => {
setRightInput(val);
setCurrentDiffIndex(0); // 💡 在同一个用户键盘事件中打包批处理,0 副作用开销
setCurrentDiffIndex(0);
}}
error={rightError}
minRows={9}
@@ -6,19 +6,16 @@ vi.mock('lucide-react', async (importOriginal) => {
const actual = await importOriginal<typeof import('lucide-react')>();
return {
...actual,
// 增量伪造需要高精嗅探的 QrCode 核心定位图标
QrCode: () => <div data-testid="mock-lucide-qrcode">Icon</div>,
};
});
// Mock useSnackbar
vi.mock('@/components/GlobalSnackbar', () => ({
useSnackbar: () => ({
showMessage: vi.fn(),
}),
}));
// Mock getEntryPointType(保留原厂其他特征配置,仅模拟入口路由环境)
vi.mock('@/config/features', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/config/features')>();
return {
@@ -27,7 +24,6 @@ vi.mock('@/config/features', async (importOriginal) => {
};
});
// Mock 高频变化的子组件,收拢断言边界
vi.mock('@/components/QrCodePreview', () => ({
default: () => <div data-testid="qr-code-preview">QrCodePreview</div>,
}));
@@ -36,7 +32,6 @@ vi.mock('@/components/ImageUploader', () => ({
default: () => <div data-testid="image-uploader">ImageUploader</div>,
}));
// Mock QRious 动态图像离屏生成引擎
vi.mock('qrious', () => ({
default: vi.fn().mockImplementation(() => ({
toDataURL: () => 'data:image/png;base64,mock',
@@ -10,7 +10,7 @@ interface StorageOptionsGridProps extends React.HTMLAttributes<HTMLDivElement> {
options: StorageCleanerOptions;
sizes: Record<string, number>;
allSelected: boolean;
someSelected: boolean; // 重新激活半选状态
someSelected: boolean;
onOptionChange: (key: keyof StorageCleanerOptions) => void;
onSelectAll: (checked: boolean) => void;
}
@@ -36,9 +36,7 @@ export default function StorageOptionsGrid({
{ key: 'serviceWorkers', isCount: true },
];
// 2. 处理全选栏点击事件:包裹整个栏变成超级热区
const handleToggleAll = () => {
// 如果当前已经是全选,点击则取消全选;否则,点击就是全选
onSelectAll(!allSelected);
};
+1 -1
View File
@@ -1,4 +1,4 @@
import { Loader2 } from 'lucide-react'; // 引入标准的高级阻尼 Spinner 图标
import { Loader2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import StorageCleanerConfirm from '@/pages/StorageCleaner/StorageCleanerConfirm';
import { useStorageCleaner } from './useStorageCleaner';
@@ -18,7 +18,7 @@ import {
} from '@/utils/storageCleaner';
import { MessageAction, sendMessage } from '@/utils/messages';
import { useI18n } from '@/utils/chromeI18n';
import { toast } from 'sonner'; // 1. 直接引用 shadcn 推荐的 Sonner 单例通知,踢出回调依赖
import { toast } from 'sonner';
const DEFAULT_OPTIONS: StorageCleanerOptions = {
localStorage: true,
-1
View File
@@ -15,7 +15,6 @@ export default function Index() {
useContextMenuData({ featureKey: 'textStatistics', onData: handleContextMenuData });
// 实时计算统计信息,由 useMemo 拦截非必要计算
const stats = useMemo(() => getTextStats(text), [text]);
const statItems = [
+1 -3
View File
@@ -4,7 +4,7 @@ import CopyButton from '@/components/CopyButton';
import { useSnackbar } from '@/components/GlobalSnackbar';
import type { UnitType } from './constants';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils'; // 引入标准的 shadcn 工具函数
import { cn } from '@/lib/utils';
interface LiveClockProps extends React.HTMLAttributes<HTMLDivElement> {
unit: UnitType;
@@ -24,12 +24,10 @@ const LiveClock = React.memo(({ unit, onUseNow, className, ...props }: LiveClock
};
});
// 始终保持外部回调指针最新
useEffect(() => {
onUseNowRef.current = onUseNow;
}, [onUseNow]);
// 2. 高频高灵敏度计时器 (200ms 刷新率)
useEffect(() => {
const tick = () => {
const rightNow = Date.now();
+3 -5
View File
@@ -4,14 +4,14 @@ import CopyButton from '@/components/CopyButton';
import type { UnitType } from './constants';
import { DATE_FORMAT } from './constants';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils'; // shadcn 核心类名合并工具
import { cn } from '@/lib/utils';
interface ResultViewProps extends React.HTMLAttributes<HTMLDivElement> {
result: string;
mode: 'ts2dt' | 'dt2ts';
unit: UnitType;
zone: string;
/** 无结果时是否渲染占位(桌面端右栏使用),默认 false */
showEmptyPlaceholder?: boolean;
}
@@ -27,7 +27,6 @@ const ResultView = React.memo(
}: ResultViewProps) => {
const { t } = useI18n('timestamp');
// 严谨计算时间衍生的附加时区/相对时间状态
const extraInfo = useMemo(() => {
if (!result) return null;
const d =
@@ -44,7 +43,6 @@ const ResultView = React.memo(
};
}, [result, mode, zone, unit]);
// 1. 空状态骨架面板:优雅匹配 shadcn 的中性灰色居中占位
if (!result) {
if (!showEmptyPlaceholder) return null;
return (
@@ -95,7 +93,7 @@ const ResultView = React.memo(
<span
className={cn(
'text-xs text-foreground/90 font-medium break-all text-left sm:text-right tabular-nums',
item.isMono && 'font-mono text-[11px]', // ISO/UTC 等机器时间使用精细化等宽代码体
item.isMono && 'font-mono text-[11px]',
)}
>
{item.value}
+1 -1
View File
@@ -76,7 +76,7 @@ export default function Index() {
]}
onChange={(v) => setUnit(v as 'ms' | 's')}
size="small"
className="sm:w-auto shrink-0" // 窄屏下全宽,宽屏下自适应收缩
className="sm:w-auto shrink-0"
/>
<Select value={zone} onValueChange={(v: string) => setZone(v as typeof zone)}>
+1 -1
View File
@@ -7,7 +7,7 @@ import { useContextMenuData } from '@/utils/useContextMenuData';
export interface UseTimestampConverterReturn {
mode: 'ts2dt' | 'dt2ts';
input: string; // 统一为单一受控输入源
input: string;
unit: UnitType;
zone: ZoneType;
result: string;