refactor: 统一中文文案并简化组件结构,提升代码可读性

移除 chrome.i18n 后,将各功能页面、布局与工具模块的 UI 文案改为内联中文;
删除冗余注释与过度抽象(如 ToolCard、StatCard),精简 props 与状态管理;
同步优化 JsonTools、StorageCleaner、Timestamp、TestDataGenerator、Dashboard、
Base64Converter、RightClickRestorer、TextStatistics、TopBar、RouterProvider、
ThemeModeProvider 及生成器库与 utils 工具文件。
This commit is contained in:
雨霖铃
2026-06-27 10:49:36 +08:00
parent f3a9838017
commit d38bafe237
85 changed files with 324 additions and 864 deletions
@@ -5,7 +5,6 @@ import Index from '../index';
import { clearStorage, getCookieSize, getCurrentTab } from '@/utils/storageCleaner';
import { toast } from 'sonner';
// Mock the chrome APIs
vi.mock('@/utils/chromeStorage', () => ({
storageUtil: {
get: vi.fn().mockResolvedValue(null),
@@ -1,18 +1,3 @@
/**
* 自动刷新开关行
*
* 用于 StorageCleaner 页面的操作区域内(非独立卡片),
* 控制清理后是否自动刷新页面。左侧为标签文本,右侧为 shadcn/ui Switch 开关。
*
* @example
* ```tsx
* <AutoRefreshToggle
* reloadAfterClean={reloadAfterClean}
* onChange={(checked) => setReloadAfterClean(checked)}
* />
* ```
*/
import React from 'react';
import { cn } from '@/lib/utils';
import { Switch } from '@/components/ui/switch';
@@ -22,20 +7,10 @@ export interface AutoRefreshToggleProps extends Omit<
React.HTMLAttributes<HTMLDivElement>,
'onChange'
> {
/** 开关的当前状态(受控) */
reloadAfterClean: boolean;
/** 状态变化时的回调函数 */
onChange: (checked: boolean) => void;
}
/**
* 自动刷新开关
*
* @param reloadAfterClean - 当前开关状态
* @param onChange - 状态变化回调,接收新的布尔值
* @param className - 额外的 CSS 类名,用于覆盖或扩展样式
* @param props - 透传给外层 div 的其他 HTML 属性
*/
export default function AutoRefreshToggle({
reloadAfterClean,
onChange,
@@ -54,7 +29,7 @@ export default function AutoRefreshToggle({
htmlFor="auto-refresh-switch"
className="text-xs font-bold text-muted-foreground/90 cursor-pointer select-none tracking-wide uppercase"
>
{'清理后自动刷新页面'}
</Label>
<Switch id="auto-refresh-switch" checked={reloadAfterClean} onCheckedChange={onChange} />
@@ -24,7 +24,7 @@ export default function ErrorDisplay({ error, className, ...props }: ErrorDispla
</p>
<p className="text-xs font-medium leading-relaxed text-muted-foreground/90 px-2">
{'存储清理功能仅适用于标准网页'}
</p>
</div>
</div>
@@ -2,11 +2,11 @@ import React from 'react';
import { formatBytes } from '@/utils/format';
import { cn } from '@/lib/utils';
import type { StorageSizeInfo } from '../useStorageCleaner';
import { OPTION_LABELS } from '../constants';
import { CLEAN_OPTION_KEYS, OPTION_LABELS } from '../constants';
import { Checkbox } from '@/components/ui/checkbox';
interface OptionItemProps extends React.HTMLAttributes<HTMLDivElement> {
labelKey: string;
labelKey: (typeof CLEAN_OPTION_KEYS)[number];
checked: boolean;
sizeInfo?: StorageSizeInfo;
onChange: () => void;
@@ -22,8 +22,7 @@ export default function OptionItem({
}: OptionItemProps) {
const sizeValue = sizeInfo?.value;
const isCount = sizeInfo?.displayType === 'count';
const label =
labelKey in OPTION_LABELS ? OPTION_LABELS[labelKey as keyof typeof OPTION_LABELS] : labelKey;
const label = OPTION_LABELS[labelKey];
return (
<div
@@ -58,7 +57,7 @@ export default function OptionItem({
</span>
) : (
<span className="block text-[10px] font-medium text-muted-foreground/50 mt-0.5 italic">
{'无数据'}
</span>
)}
</div>
@@ -10,7 +10,7 @@ import {
import { Badge } from '@/components/ui/badge';
import { AlertTriangle } from 'lucide-react';
import type { StorageCleanerOptions } from '@/types/storage';
import { OPTION_LABELS } from '../constants';
import { OPTION_LABELS, CLEAN_OPTION_KEYS } from '../constants';
import { cn } from '@/lib/utils';
export interface StorageCleanerConfirmProps {
@@ -26,11 +26,9 @@ export function StorageCleanerConfirm({
onConfirm,
options,
}: StorageCleanerConfirmProps) {
const selectedOptions = Object.entries(options)
.filter(([_, value]) => value)
.map(([key]) =>
key in OPTION_LABELS ? OPTION_LABELS[key as keyof typeof OPTION_LABELS] : key,
);
const selectedOptions = CLEAN_OPTION_KEYS.filter((key) => options[key]).map(
(key) => OPTION_LABELS[key],
);
return (
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && onClose()}>
@@ -42,14 +40,14 @@ export function StorageCleanerConfirm({
{/* 头部标题区域 */}
<DialogHeader className="pt-1">
<DialogTitle className="text-center text-lg font-bold tracking-tight text-foreground">
{'确认清理数据?'}
</DialogTitle>
</DialogHeader>
{/* 内容主体:限制最大宽度,防止内部元素在大分辨率下被横向拉得太松散 */}
<div className="text-center py-4 flex flex-col items-center w-full max-w-[280px] mx-auto">
<DialogDescription className="mb-4 text-xs font-medium text-muted-foreground/90 leading-relaxed">
{'您将永久删除当前页面的以下选定存储项。'}
</DialogDescription>
{/* 待清理项目徽章群 */}
@@ -69,7 +67,7 @@ export function StorageCleanerConfirm({
<div className="inline-flex items-center justify-center gap-1.5 px-3.5 py-2 rounded-lg bg-destructive/5 border border-dashed border-destructive/20 w-full max-w-[240px]">
<AlertTriangle className="h-3.5 w-3.5 text-destructive shrink-0" />
<span className="text-[11px] font-bold text-destructive leading-none tracking-tight">
{'此操作不可撤销'}
</span>
</div>
</div>
@@ -81,7 +79,7 @@ export function StorageCleanerConfirm({
onClick={onConfirm}
className="w-full text-xs font-bold shadow-sm h-9"
>
{'确认清理'}
</Button>
<Button
variant="outline"
@@ -89,7 +87,7 @@ export function StorageCleanerConfirm({
onClick={onClose}
className="w-full text-xs font-semibold shadow-sm h-9 text-muted-foreground hover:text-foreground"
>
{'取消'}
</Button>
</DialogFooter>
</DialogContent>
@@ -51,7 +51,7 @@ export default function StorageOptionsGrid({
className="border-t border-border flex justify-between items-center pl-3.5 pr-7 py-2.5 bg-muted/20 hover:bg-muted/40 cursor-pointer select-none transition-colors"
>
<Label className="text-xs font-bold text-muted-foreground/90 cursor-pointer tracking-wide uppercase">
{'全选所有项'}
</Label>
<Checkbox
+2 -2
View File
@@ -34,7 +34,7 @@ export default function Index() {
<div className="flex flex-col items-center justify-center py-12 min-h-[280px] w-full">
<Loader2 className="h-6 w-6 text-muted-foreground/80" />
<span className="text-xs text-muted-foreground mt-2 font-medium tracking-wide">
{'正在读取数据...'}
...
</span>
</div>
);
@@ -73,7 +73,7 @@ export default function Index() {
{loading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
{'正在清理...'}
...
</>
) : (
'立即清理'
+5 -11
View File
@@ -117,7 +117,6 @@ export function useStorageCleaner(): UseStorageCleanerReturn {
};
}, []);
// 核心数据拉取链条
const loadInfo = useCallback(async () => {
const currentRequestId = ++requestIdRef.current;
setIsRefreshingSizes(true);
@@ -189,7 +188,6 @@ export function useStorageCleaner(): UseStorageCleanerReturn {
}, 300);
}, []);
// 监听浏览器标签行为
useEffect(() => {
loadInfoRef.current().catch(console.error);
@@ -216,15 +214,12 @@ export function useStorageCleaner(): UseStorageCleanerReturn {
if (storageTimerRef.current) clearTimeout(storageTimerRef.current);
storageTimerRef.current = setTimeout(async () => {
try {
await storageUtil.set('storageCleaner/preferences', {
await storageUtil
.set('storageCleaner/preferences', {
reloadAfterClean,
selectedTypes: options,
});
} catch (err) {
console.error('Failed to save storage cleaner preferences:', err);
toast.warning('偏好保存失败,本次设置可能不会保留');
}
})
.catch(console.error);
}, 500);
}, [options, reloadAfterClean, isInitializing]);
@@ -251,7 +246,7 @@ export function useStorageCleaner(): UseStorageCleanerReturn {
if (loadingRef.current) return;
const tab = await getCurrentTab();
if (!tab || tab.id === undefined || !tab.url) {
if (!tab || !tab.id || !tab.url) {
toast.warning('无法获取当前标签页');
return;
}
@@ -286,7 +281,6 @@ export function useStorageCleaner(): UseStorageCleanerReturn {
toast.error(`清理失败: ${String(err)}`);
} finally {
setLoading(false);
setShowConfirm(false);
}
}, [options, reloadAfterClean, loadInfo]);