refactor: remove meaningless comments

Remove 50+ noise comments across the codebase:
- AI-generated verbose prose (💡 emoji, '超进化', '大闸', etc.)
- Comments that restate what the code obviously does
- Comments about deleted code
- Import-level noise comments
- Overly verbose Chinese section markers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
雨霖铃
2026-05-28 20:37:52 +08:00
parent b79fe7dd49
commit e140af7698
34 changed files with 32 additions and 242 deletions
+1 -3
View File
@@ -44,8 +44,6 @@ export default function TextMode({ onSwitchToImageMode }: TextModeProps = {}) {
useContextMenuData({ featureKey: 'base64Converter', onData: handleContextMenuData });
// 💡 4. 贯彻方案 A(彻底消灭 setOutput / setError):
// 让所有的转化逻辑、类型安全校验在 useMemo 内存管道中单次渲染一气呵成!
const conversionPipeline = useMemo(() => {
const trimmed = debouncedInput.trim();
if (!trimmed) return { output: '', error: null };
@@ -112,7 +110,7 @@ export default function TextMode({ onSwitchToImageMode }: TextModeProps = {}) {
placeholder={placeholder}
value={input}
onChange={setInput}
externalError={error || undefined} // 💡 流式异常大闸动态注入
externalError={error || undefined}
showClear={true}
allowCopy={true}
minRows={5}
@@ -130,7 +130,6 @@ export function useBase64Converter({ mode }: UseBase64ConverterProps) {
return '';
}, [customFileName, decoded]);
// 💡 托管最大文件体积字符串算子,清除下游引入风险
const maxFileSizeStr = `${MAX_FILE_SIZE / 1024 / 1024} MB`;
return {
+1 -1
View File
@@ -3,7 +3,7 @@ import { Download, Trash2 } from 'lucide-react';
import { useI18n } from '@/utils/chromeI18n';
import CopyButton from '@/components/CopyButton';
import SwitchButtonGroup from '@/components/SwitchButtonGroup';
import { Button } from '@/components/ui/button'; // 💡 1. 全面回归规范:引入原生的 shadcn 原子 Button
import { Button } from '@/components/ui/button';
import { useStorageState } from '@/utils/useStorageState';
import type { HtmlToMarkdownPreviewMode } from '@/types/storage';
import { downloadMarkdownFile, htmlToMarkdown, SAMPLE_HTML } from '@/utils/htmlToMarkdown';
+3 -9
View File
@@ -1,7 +1,7 @@
import React from 'react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils'; // 1. 引入标准的 shadcn 工具函数
import { cn } from '@/lib/utils';
export interface DiffNavigatorProps extends React.HTMLAttributes<HTMLDivElement> {
total: number;
@@ -21,11 +21,9 @@ export default function DiffNavigator({
}: DiffNavigatorProps) {
const { t } = useI18n('jsonDiff');
// 计算当前的边界禁用状态守卫
const isFirst = currentIndex <= 0;
const isLast = currentIndex >= total - 1;
// 2. 空状态面板:对齐 shadcn 规范的中性低调卡片
if (total === 0) {
return (
<div
@@ -45,9 +43,6 @@ export default function DiffNavigator({
return (
<div
className={cn(
// 3. 完美适配暗黑模式:
// 废除 bg-primary/10,采用标准的低阻尼中性色 bg-secondary/60 配合 border-border/80
// 在任何主题皮肤下都能呈现出高级的暗钛金控制栏质感。
'inline-flex items-center justify-center gap-3 px-3 h-9 rounded-md border border-border/80 bg-secondary/60 shadow-sm',
className,
)}
@@ -62,13 +57,12 @@ export default function DiffNavigator({
className={cn(
'p-1 rounded-md text-muted-foreground hover:bg-accent hover:text-foreground',
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
'disabled:pointer-events-none disabled:opacity-30', // 4. 边界拦截:触顶时优雅淡化并锁死点击
'disabled:pointer-events-none disabled:opacity-30',
)}
>
<ChevronLeft className="h-4 w-4" />
</button>
{/* 计数看板:强制等宽防止数字长短不一时产生宽度挤压跳动 */}
<span className="text-xs font-bold font-mono min-w-[54px] text-center text-foreground/90 tabular-nums select-none">
{currentIndex + 1} <span className="text-muted-foreground/60 font-sans mx-0.5">/</span>{' '}
{total}
@@ -83,7 +77,7 @@ export default function DiffNavigator({
className={cn(
'p-1 rounded-md text-muted-foreground hover:bg-accent hover:text-foreground',
'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
'disabled:pointer-events-none disabled:opacity-30', // 4. 边界拦截:触底时优雅淡化并锁死点击
'disabled:pointer-events-none disabled:opacity-30',
)}
>
<ChevronRight className="h-4 w-4" />
-11
View File
@@ -4,7 +4,6 @@ import { cn } from '@/lib/utils';
import JsonTree from './JsonTree';
import type { DiffNode, DiffResult as DiffResultType, DiffType, ViewMode } from './types';
// 💡 顶层 Interface 继承原生 HTML 容器属性,扩展灵活性
export interface DiffResultProps extends React.HTMLAttributes<HTMLDivElement> {
result: DiffResultType;
viewMode: ViewMode;
@@ -39,10 +38,6 @@ export default function DiffResult({
}
return (
/* 1. 单栏拍平视图容器:
- 对齐 shadcn 规范,使用 bg-card、border-border 隔离。
- 注入 tabular-nums 配合 font-mono,消灭任何行高和字符抖动。
*/
<div
className={cn(
'rounded-xl border border-border bg-card font-mono text-xs shadow-sm overflow-x-auto min-h-[200px] max-h-[520px] overflow-y-auto p-1.5',
@@ -79,8 +74,6 @@ const prefixForType = (type: DiffType): string => {
return ' ';
};
// 2. 状态色彩超进化:
// 拒绝硬编码实色系,全部换用高度安全的语义色变体与暗黑模式自适应。
const typeThemeMap = {
added: {
text: 'text-emerald-600 dark:text-emerald-400',
@@ -191,7 +184,6 @@ interface UnifiedRowProps {
}
const UnifiedRow = ({ depth, type, text, active, multiline }: UnifiedRowProps) => {
// 3. 高精度提取状态样式映射
const currentTheme = typeThemeMap[type] || typeThemeMap.unchanged;
return (
@@ -200,17 +192,14 @@ const UnifiedRow = ({ depth, type, text, active, multiline }: UnifiedRowProps) =
'flex items-start w-full font-mono py-0.5 select-text group',
currentTheme.bg,
currentTheme.text,
// 4. 高亮定位条:不再使用生硬的蓝圆环,改为现代编辑器的“侧边左高亮带”设计,质感直接拉满
active &&
'bg-primary/10 relative before:absolute before:left-0 before:top-0 before:bottom-0 before:w-1 before:bg-blue-500',
)}
style={{
// 维持高精度的 Padding 基线缩进
paddingLeft: `${Math.max(0.5, depth * 1.25)}rem`,
paddingRight: '0.5rem',
}}
>
{/* 5. 前缀标识:等宽锁定,强行占据 w-5 并让符号居中对齐,达成 VSCode 般的整洁排版 */}
<span className="font-bold w-5 shrink-0 text-center select-none opacity-70 tabular-nums">
{prefixForType(type)}
</span>
+3 -8
View File
@@ -32,7 +32,7 @@ export default function JsonConvertSection({
const pk = translationPrefix;
// 1. 高阶性能调优:将文本变化收拢进行 250ms 极速防抖落盘,避免每一次敲击键盘都触发底层的复杂序列化算法
// Debounce input
useEffect(() => {
const handle = setTimeout(() => {
setDebouncedInput(input);
@@ -40,14 +40,10 @@ export default function JsonConvertSection({
return () => clearTimeout(handle);
}, [input]);
// 💡 2. 贯彻方案 A(衍生变量超进化):
// 彻底删掉 error 状态和对应的受控 useEffect 节点。
// 语法错误由防抖文本在内存中同步推导,彻底斩断二次级联渲染链条,ESLint 警告自愈!
const error = useMemo(() => {
return validateJson(debouncedInput);
}, [debouncedInput]);
// 3. 核心魔法:纯净的即时流式转换转换管线 (Live Compilation Pipeline)
const conversionPipeline = useMemo(() => {
const trimmed = debouncedInput.trim();
if (!trimmed || error) return null;
@@ -63,7 +59,6 @@ export default function JsonConvertSection({
}
}, [debouncedInput, error, convertFunction]);
// 判定运行时异常
const runtimeError =
conversionPipeline && 'isRuntimeError' in conversionPipeline
? conversionPipeline.errorMessage
@@ -88,7 +83,7 @@ export default function JsonConvertSection({
onClear={() => setInput('')}
/>
{/* 4. 结果展示或状态引导卡片区 */}
{/* Result display */}
{result && result.output ? (
<div className="relative rounded-xl border border-border bg-card text-card-foreground shadow-sm overflow-hidden">
{/* 结果栏精致头部 */}
@@ -130,7 +125,7 @@ export default function JsonConvertSection({
</div>
</div>
) : (
/* 5. 空状态提示容器:完美的中性虚线引导,不喧宾夺主 */
/* 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
View File
@@ -2,8 +2,6 @@ import React from 'react';
import TextInputArea from '@/components/TextInputArea';
import { cn } from '@/lib/utils';
// 💡 核心修复:使用 Omit<..., 'onChange'> 强行挖掉原生的 onChange 签名
// 这样我们自定义的 (value: string) => void 就能独占鳌头,彻底消灭 TS2430 接口冲突!
export interface JsonDiffInputProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
label: string;
placeholder: string;
+2 -8
View File
@@ -21,7 +21,7 @@ export default function JsonFormatSection() {
const [indentSize, setIndentSize] = useState<number>(2);
const [sortKeys, setSortKeys] = useState(false);
// 1. 高频打字防抖落盘:防止大体积 JSON 在高频输入时发生卡顿
// Debounce input
useEffect(() => {
const handle = setTimeout(() => {
setDebouncedInput(input);
@@ -29,14 +29,11 @@ export default function JsonFormatSection() {
return () => clearTimeout(handle);
}, [input]);
// 💡 2. 贯彻方案 A(衍生变量超进化):
// 彻底删除原有的 setError 状态和相关的 useEffect。
// 语法错误由防抖文本在内存中同步推导,彻底斩断二次级联渲染链条,ESLint 警告瞬间消亡!
const error = useMemo(() => {
return validateJson(debouncedInput);
}, [debouncedInput]);
// 3. 实时流式格式化管线
// Real-time formatting pipeline
const formattedPipeline = useMemo(() => {
const trimmed = debouncedInput.trim();
if (!trimmed || error) return null;
@@ -149,9 +146,6 @@ export default function JsonFormatSection() {
/>
</div>
{/* 核心格式化数据面板:
💡 修复点:移除了互相打架的 select-all 类名,仅保留纯正的代码高亮可选样式 select-text
*/}
<div className="p-4 font-mono text-xs text-foreground/90 whitespace-pre-wrap break-all max-h-[420px] overflow-y-auto leading-relaxed select-text">
{result.formatted}
</div>
-7
View File
@@ -38,7 +38,6 @@ const getValueForSide = (node: DiffNode, side: TreeSide): unknown => {
return side === 'left' ? node.oldValue : node.newValue;
};
// 1. 核心状态色彩映射调色盘:完美自适应双色模式
const typeThemeMap = {
added: {
text: 'text-emerald-600 dark:text-emerald-400',
@@ -62,10 +61,6 @@ const isContainerValue = (v: unknown): boolean => {
return (typeof v === 'object' && v !== null) || Array.isArray(v);
};
/**
* 💡 性能调优大闸:将 NodeRow 抽离为顶层独立组件并裹上 React.memo。
* 配合精准的 Props Diff,使得某一行的展开闭合绝对不会连累到其他平级和上级节点。
*/
const NodeRow = React.memo(
({ node, side, depth, defaultExpandDepth, activePath, isLastChild }: NodeRowProps) => {
const [override, setOverride] = useState<'auto' | 'open' | 'closed'>('auto');
@@ -113,8 +108,6 @@ const NodeRow = React.memo(
const isActive = activePath === node.path;
const isRoot = depth === 0;
// 缩进样式封装:
// 💡 视觉魔法:通过在左侧追加 before 细线,在每一层级下自动垂下一条优雅的 IDE 级“缩进指引线”
const indentStyle = {
paddingLeft: `${Math.max(0.25, depth * 1.15)}rem`,
};
+2 -4
View File
@@ -43,7 +43,7 @@ const diffNode = (
return {
key,
type: 'added',
oldValue: undefined, // 💡 补齐:对齐移除 ? 后的类型规范
oldValue: undefined,
newValue: right,
path,
isLeaf: !isObject(right) && !isArray(right),
@@ -58,7 +58,7 @@ const diffNode = (
key,
type: 'removed',
oldValue: left,
newValue: undefined, // 💡 补齐:对齐移除 ? 后的类型规范
newValue: undefined,
path,
isLeaf: !isObject(left) && !isArray(left),
hasDiffInChildren: false, // 自身即是删除,子树无需向下检索
@@ -87,7 +87,6 @@ const diffNode = (
children.push(diffNode(l, r, k, childPath, diffPaths));
});
// 💡 核心改良:判定子节点中是否存在任何变动
const hasDiffInChildren = children.some((c) => c.type !== 'unchanged' || c.hasDiffInChildren);
return {
@@ -115,7 +114,6 @@ const diffNode = (
children[i] = diffNode(l, r, k, childPath, diffPaths);
}
// 💡 核心改良:判定子项中是否存在任何变动
const hasDiffInChildren = children.some((c) => c.type !== 'unchanged' || c.hasDiffInChildren);
return {
+4 -7
View File
@@ -40,11 +40,11 @@ export default function Index() {
const { t } = useI18n(['jsonDiff', 'jsonFormat']);
const [pageMode, setPageMode] = useStorageState('jsonTools/pageMode', 'diff', isValidPageMode);
// 1. 受控原始输入源
// Debounce input
const [leftInput, setLeftInput] = useState('');
const [rightInput, setRightInput] = useState('');
// 2. 纯净的异步防抖管道:仅负责切断高频打字开销
// Debounced values
const [debouncedLeft, setDebouncedLeft] = useState('');
const [debouncedRight, setDebouncedRight] = useState('');
@@ -56,7 +56,7 @@ export default function Index() {
return () => clearTimeout(handle);
}, [leftInput, rightInput]);
// 3. 贯彻方案A:利用 useMemo 将防抖文本同步转化为解析树和错误提示
// Parse debounced inputs
const parseState = useMemo(() => {
const invalidMsg = t('jsonDiff:invalidJson');
return {
@@ -71,7 +71,7 @@ export default function Index() {
const [viewMode, setViewMode] = useState<ViewMode>('sideBySide');
const [currentDiffIndex, setCurrentDiffIndex] = useState(0);
// 4. 实时比对流式计算
// Real-time diff computation
const diffResult = useMemo(() => {
const { left, right } = parseState;
if (left.error || right.error || debouncedLeft.trim() === '' || debouncedRight.trim() === '') {
@@ -80,9 +80,6 @@ export default function Index() {
return diffJson(left.value, right.value);
}, [parseState, debouncedLeft, debouncedRight]);
// 💡 彻底删除了原本在此处的侦听 [diffResult] 的 useEffect。
// 状态重置已完全委托给事件源头,级联更新警告从根源上永久自愈!
const total = diffResult?.diffPaths.length ?? 0;
const handlePrev = useCallback(() => {
+1 -6
View File
@@ -9,7 +9,7 @@ import { cn } from '@/lib/utils';
interface SectionProps {
title: string;
content: unknown;
colorClass: string; // 💡 1. 废除硬编码十六进制色值,改用语义化的 Tailwind 类名
colorClass: string;
bgClass: string;
borderClass: string;
}
@@ -27,10 +27,6 @@ const Section = ({ title, content, colorClass, bgClass, borderClass }: SectionPr
className="h-6 w-6 rounded-md border text-muted-foreground"
/>
</div>
{/* 💡 排版微距精雕:
- 彻底移除 border-black/5 这种非暗黑模式友好的硬隔离。
- 统一收拢为标准的 bg-muted/40 配合 font-mono text-xs
*/}
<pre className="m-0 p-3 bg-muted/30 dark:bg-muted/10 rounded-lg text-xs font-mono overflow-x-auto whitespace-pre-wrap break-all border border-border/50 text-foreground/90 leading-relaxed select-text">
{content ? stringifyJson(content) : t('jwt:invalidFormat')}
</pre>
@@ -59,7 +55,6 @@ export default function Index() {
useContextMenuData({ featureKey: 'jwt', onData: handleContextMenuData });
// 3. 贯彻方案 A:衍生变量流。直接消费防抖后的文本
const result = useMemo(() => {
if (!debouncedInput.trim()) {
return null;
-3
View File
@@ -18,7 +18,6 @@ import { cn } from '@/lib/utils';
const isValidPreviewMode = (val: unknown): val is MarkdownToHtmlPreviewMode =>
typeof val === 'string' && ['split', 'preview', 'html'].includes(val);
// 💡 规范回归:保持最纯净的通用选择器集合,内部变量全部交由全局 :root 驱动
const PREVIEW_STYLES = `
.markdown-body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
@@ -94,7 +93,6 @@ export default function MarkdownToHtmlPage() {
const result = useMemo(() => markdownToHtml(markdown), [markdown]);
const error = result.hasError ? (result.error ?? null) : null;
// 💡 自适应大总管:以极高严谨度拼装明暗大闸,用标准换行切断粘连风险
useEffect(() => {
const iframe = iframeRef.current;
if (!iframe) return;
@@ -123,7 +121,6 @@ export default function MarkdownToHtmlPage() {
--md-link-color: #3b82f6;
}`;
// 💡 3. 核心大清洗:将全局基础树(html, body)与派生样式完全独立硬编码,杜绝任何语法踩踏
const baseGlobalStyles = `
html, body {
margin: 0;
@@ -11,17 +11,12 @@ export default function GeneratePanel() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 w-full items-stretch select-none p-0.5">
{/* 左翼:高性能受控输入翼终端 */}
<div
className={cn(
'border border-border rounded-xl bg-card text-card-foreground shadow-sm flex flex-col p-4',
'focus-within:ring-1 focus-within:ring-ring focus-within:border-ring',
)}
>
{/* 💡 2. 独立外置标签架(A11y 无障碍对齐):
- 彻底删掉 TextInputArea 上引发崩溃的违规属性。
- 改用正统的 <Label />,并注入标准的高度无障碍样式,间距比例极度平滑。
*/}
<div className="flex flex-col space-y-2.5 h-full">
<Label className="text-[10px] font-bold text-muted-foreground/90 uppercase tracking-wider select-none pl-0.5">
{t('qrCode:urlInputLabel')}
@@ -44,7 +39,6 @@ export default function GeneratePanel() {
</div>
</div>
{/* 右翼:活态二维码高精生成区 */}
<div className="flex flex-col h-full">
<QrCodePreview
qrCodeDataUrl={generatorState.qrCodeDataUrl}
@@ -18,7 +18,6 @@ export default function ParsePanel() {
const items = e.clipboardData?.items;
if (!items) return;
// 检查是否有图片
for (let i = 0; i < items.length; i++) {
if (items[i].type.startsWith('image/')) {
e.preventDefault();
@@ -31,7 +30,6 @@ export default function ParsePanel() {
}
}
// 检查是否有 Base64 字符串
const text = e.clipboardData?.getData('text/plain');
if (text && text.startsWith('data:image/')) {
e.preventDefault();
@@ -58,11 +56,7 @@ export default function ParsePanel() {
}, [handlePaste]);
return (
/* 💡 统一大视觉轴:
- 追加 p-0.5 微隔离,配合 gap-6 建立与生成面板(GeneratePanel)绝对像素对齐的网格天平。
*/
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 w-full items-stretch select-none p-0.5">
{/* 左翼:图片接收/拖拽/剪贴板上传终端 */}
<div className="flex flex-col h-full">
<ImageUploader
selectedFile={parserState.selectedFile}
@@ -75,16 +69,13 @@ export default function ParsePanel() {
/>
</div>
{/* 右翼:高阶解析出码只读终端 */}
<div
className={cn(
'border border-border rounded-xl bg-card text-card-foreground shadow-sm flex flex-col p-4',
// 💡 视觉对称增强:加入相同的聚焦变量环联动,使双翼权重达成完美绝对平衡
'focus-within:ring-1 focus-within:ring-ring focus-within:border-ring',
)}
>
<div className="flex flex-col space-y-2.5 h-full">
{/* 💡 修复点:物理剔除 TextInputArea 上的违规 title,改用符合 Vercel 美学的极致大写极细原子标签 */}
<Label className="text-[10px] font-bold text-muted-foreground/90 uppercase tracking-wider pl-0.5">
{t('qrCode:resultLabel')}
</Label>
+3 -8
View File
@@ -1,23 +1,19 @@
import type { Dispatch, SetStateAction } from 'react'; // 💡 1. 显式解构导入类型,彻底掐灭 TS2304 报错
import type { Dispatch, SetStateAction } from 'react';
import { createContext, useContext } from 'react';
import type { QrCodeGeneratorState, QrCodeMode, QrCodeParserState } from '../types';
export interface QrCodeContextValue {
// 核心主视图路由模式切换卡
mode: QrCodeMode;
setMode: (mode: QrCodeMode) => void;
// 1. 流式生成器终端状态机驱动
generatorState: QrCodeGeneratorState;
setTextToEncode: (text: string) => void;
// 💡 架构纯净化:物理剔除暴露给外部的命令式 generateQrCode 算子。
// 外部面板只需 setTextToEncode 驱动源文本更新,生成动作由内部流式管线全自动自发自愈完成!
downloadQrCode: () => void;
copyQrCode: () => Promise<void>;
// 2. 活态反向解析器终端状态机驱动
parserState: QrCodeParserState;
setParserState: Dispatch<SetStateAction<QrCodeParserState>>; // 💡 规整为纯净的直接类型使用
setParserState: Dispatch<SetStateAction<QrCodeParserState>>;
parseQrCode: (file: File) => Promise<void>;
handleFileChange: (file: File) => void;
handleClearFile: () => void;
@@ -28,7 +24,6 @@ export const QrCodeContext = createContext<QrCodeContextValue | null>(null);
export function useQrCodeContext() {
const context = useContext(QrCodeContext);
if (!context) {
// 边界鲁棒性防护大闸
throw new Error('useQrCodeContext must be used within a valid QrCodeProvider container');
}
return context;
-15
View File
@@ -12,10 +12,8 @@ export function useQrCode(): QrCodeContextValue {
const { t } = useI18n('qrCode');
const { showMessage } = useSnackbar();
// 核心路由视图模式
const [mode, setMode] = useState<QrCodeMode>('generate');
// 1. 生成器状态流(大幅瘦身:剔除 generating 状态)
const [generatorState, setGeneratorState] = useState<
Omit<QrCodeGeneratorState, 'generating' | 'qrCodeDataUrl'>
>({
@@ -23,7 +21,6 @@ export function useQrCode(): QrCodeContextValue {
inputError: '',
});
// 2. 解析器状态流
const [parserState, setParserState] = useState<QrCodeParserState>({
decodedResult: '',
parsing: false,
@@ -33,12 +30,8 @@ export function useQrCode(): QrCodeContextValue {
dragging: false,
});
// 3. 高频打字极速防抖
const debouncedTextToEncode = useDebounce(generatorState.textToEncode, 200);
// 💡 4. 贯彻方案 A(无副作用超导管线):
// 彻底删除原有的 generateQrCodeRef、3个 useEffect、1个 useRef 以及相关的复杂状态机。
// 二维码画布纯粹作为防抖文本的派生变量同步算出,0重绘死循环风险,体验平滑如镜!
const qrCodeDataUrl = useMemo(() => {
const text = debouncedTextToEncode.trim();
if (!text) return '';
@@ -49,14 +42,12 @@ export function useQrCode(): QrCodeContextValue {
url = 'https://' + url;
}
// 💡 暗黑模式自适应大闸:实时嗅探系统 DOM 阶度
const isDark = document.documentElement.classList.contains('dark');
const qr = new QRious({
value: url,
size: 260,
level: 'H',
// 暗黑模式下使用透明底、月白前景色;白天模式下使用标准现代黑白配
foreground: isDark ? '#f3f4f6' : '#0f172a',
background: isDark ? 'transparent' : '#ffffff',
});
@@ -68,7 +59,6 @@ export function useQrCode(): QrCodeContextValue {
}
}, [debouncedTextToEncode]);
// 融合派生数据至完整状态体,满足外部组件强类型契合
const fullGeneratorState = useMemo<QrCodeGeneratorState>(
() => ({
...generatorState,
@@ -78,12 +68,10 @@ export function useQrCode(): QrCodeContextValue {
[generatorState, qrCodeDataUrl],
);
// 设置输入文本
const setTextToEncode = useCallback((text: string) => {
setGeneratorState((prev) => ({ ...prev, textToEncode: text, inputError: '' }));
}, []);
// 处理右键菜单数据上下文
const handleContextMenuData = useCallback((payload: string) => {
setMode('generate');
setGeneratorState((prev) => ({ ...prev, textToEncode: payload, inputError: '' }));
@@ -119,7 +107,6 @@ export function useQrCode(): QrCodeContextValue {
[t, showMessage],
);
// 下载二维码
const downloadQrCode = useCallback(() => {
if (!qrCodeDataUrl) return;
@@ -130,7 +117,6 @@ export function useQrCode(): QrCodeContextValue {
showMessage(t('qrCode:qrCodeDownloadSuccess'), { severity: 'success', autoHideDuration: 1000 });
}, [qrCodeDataUrl, showMessage, t]);
// 复制二维码至剪贴板
const copyQrCode = useCallback(async () => {
if (!qrCodeDataUrl) return;
@@ -151,7 +137,6 @@ export function useQrCode(): QrCodeContextValue {
}
}, [qrCodeDataUrl, showMessage, t]);
// 处理文件选择
const handleFileChange = useCallback(
(file: File) => {
setParserState((prev) => {
-9
View File
@@ -19,12 +19,7 @@ export default function Index() {
return (
<QrCodeContext.Provider value={qrCode}>
{/* 💡 统一视觉规范大超进化:
- 彻底剥离破坏流式宽度的 max-w-[400px] 枷锁,开启标准的 w-full 全自适应包裹。
- 替换为标准的 p-4 呼吸内边距配合 flex flex-col space-y-4,接管系统级重排!
*/}
<div className="p-4 w-full flex flex-col space-y-4 min-h-[500px] select-none">
{/* 流式中央控制切流卡:注入 sm 断点防御,防范单栏状态下发生变形 */}
<div className="w-full sm:w-fit pt-0.5">
<SwitchButtonGroup
value={qrCode.mode}
@@ -34,10 +29,6 @@ export default function Index() {
/>
</div>
{/* 💡 面板渲染沙箱:
- 在切流渲染时,利用独立的 mt-2 增加纵深边界线。
- 配合内部自带的双翼 Flex 聚焦大边框,形成坚固如铁的架构闭环!
*/}
<div className="w-full pt-1.5">
{qrCode.mode === 'generate' ? (
<div>
+1 -8
View File
@@ -3,7 +3,7 @@ import { CheckCircle, XCircle } from 'lucide-react';
import type { CleaningResult as CleaningResultType } from '@/types/storage';
import { formatCleaningResult } from '@/utils/storageCleaner';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils'; // 1. 引入 shadcn 核心类名合并工具
import { cn } from '@/lib/utils';
interface CleaningResultProps extends React.HTMLAttributes<HTMLDivElement> {
result: CleaningResultType | null;
@@ -18,11 +18,6 @@ export default function CleaningResult({ result, className, ...props }: Cleaning
return (
<div className={cn('w-full', className)} {...props}>
{/* 2. 彻底重构容器类名结构:
- 成功状态:采用 Tailwind 官方推荐的 emerald 体系,利用 /10 (10% 透明度) 和 /20 (边框)。
- 失败状态:完全放权给标准的 border-destructive/20 和 bg-destructive/5。
- 这样在明暗双色模式切换时,色彩会自动与背景完美融为一体。
*/}
<div
className={cn(
'flex items-start gap-3 rounded-xl py-2.5 px-3.5 border shadow-sm',
@@ -31,14 +26,12 @@ export default function CleaningResult({ result, className, ...props }: Cleaning
: 'bg-destructive/5 border-destructive/20 text-destructive',
)}
>
{/* 3. 图标样式向系统语义全面对齐 */}
{isSuccess ? (
<CheckCircle className="h-4 w-4 shrink-0 mt-0.5 text-emerald-500" />
) : (
<XCircle className="h-4 w-4 shrink-0 mt-0.5 text-destructive" />
)}
{/* 4. 文本排版细节微调 */}
<span className="text-xs sm:text-sm font-semibold leading-relaxed break-all">
{isSuccess
? formatCleaningResult(result, t)
@@ -10,7 +10,6 @@ export default function ErrorDisplay({ error, className, ...props }: ErrorDispla
const { t } = useI18n('storageCleaner');
return (
// 1. 精简层级:单层外壳直接搞定居中、响应式高度与外部类名扩展
<div
className={cn(
'flex flex-col items-center justify-center py-8 min-h-[240px] sm:min-h-[360px] p-4 text-center',
@@ -18,13 +17,7 @@ export default function ErrorDisplay({ error, className, ...props }: ErrorDispla
)}
{...props}
>
{/* 2. 核心卡片容器:
- 彻底放弃 bg-red-50,改用标准的 bg-destructive/53%~5% 透明度的系统危险色)。
- 边框改为 border-destructive/20。
- 这样在黑夜模式下会自动完美混色,绝不刺眼。
*/}
<div className="w-full max-w-xs flex flex-col items-center justify-center rounded-xl p-5 border border-destructive/20 bg-destructive/5 shadow-sm">
{/* 3. 图标与主要错误信息全面对接 text-destructive 语义色 */}
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-destructive/10 text-destructive mb-3.5 shrink-0">
<AlertCircle className="h-5 w-5" />
</div>
@@ -33,7 +26,6 @@ export default function ErrorDisplay({ error, className, ...props }: ErrorDispla
{error}
</p>
{/* 次要提示文本维持柔和的中性高级灰 */}
<p className="text-xs font-medium leading-relaxed text-muted-foreground/90 px-2">
{t('storageCleaner:errorStandardOnly')}
</p>
-11
View File
@@ -26,14 +26,9 @@ export default function OptionItem({
return (
<div
// 3. 跨越级交互升级:将外部容器升级为一个高度敏感的可点击 Tab 热区
onClick={onChange}
className={cn(
'flex justify-between items-center py-2.5 px-3.5 rounded-xl border cursor-pointer select-none',
// 4. 彻底抛弃硬编码黄底:
// - 选中时:使用 bg-primary/5 (系统主色超淡叠加) 配合标准 border-primary/30。
// - 未选中时:保持透明 border-transparent,悬停呈现 bg-muted。
// 这样在暗黑模式下会自动无缝混色,极为深邃、高级。
checked
? 'bg-primary/5 border-primary/30 shadow-sm'
: 'bg-transparent border-transparent hover:bg-muted/70',
@@ -64,14 +59,8 @@ export default function OptionItem({
)}
</div>
{/* 5. 超进化:全面替换原生 input 标签
完美调用 shadcn 的 Checkbox 组件。它自带全站统一的主色(Primary)、
打钩选中时的平滑微放大缩放动效(Scale Animation),
并且阻止冒泡,防范与外层的全局覆盖点击事件产生双重冲突。
*/}
<Checkbox
checked={checked}
// 阻止 Checkbox 自身的点击事件冒泡,因为外层 div 已经代理了点击逻辑
onClick={(e) => e.stopPropagation()}
onCheckedChange={onChange}
className="h-4 w-4 shrink-0 rounded border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground"
@@ -34,11 +34,6 @@ export function StorageCleanerConfirm({
return (
<Dialog open={open} onOpenChange={(isOpen) => !isOpen && onClose()}>
{/* 💡 终极修复秘诀:
- 移除原来的 sm:max-w-[360px],改用 max-w-[calc(100%-32px)] 或者是 w-[88%]。
- 这样无论插件弹窗多窄,它的左右两侧都必然会被强制挤出至少 16px 的完美空白护边!
- 将 p-5 转换为明确的 p-6,增大弹窗内部的呼吸感。
*/}
<DialogContent
className={cn(
'w-[90%] max-w-[340px] p-6 gap-0 rounded-2xl overflow-hidden shadow-xl border border-border bg-card text-card-foreground',
@@ -79,11 +74,6 @@ export function StorageCleanerConfirm({
</div>
</div>
{/* 底部操作按钮区:
💡 修复要点:
- 增加 pt-2 隔开上方危险条。
- 显式通过 w-full 配合 flex-col 铺满,在移动端/窄插件下垂直堆叠,最符合小屏直觉。
*/}
<DialogFooter className="flex flex-col gap-2 w-full pt-2">
<Button
variant="destructive"
@@ -3,7 +3,6 @@ import type { StorageCleanerOptions } from '@/types/storage';
import OptionItem from './OptionItem';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils';
// 1. 引入官方标准的 Checkbox 原子组件
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
@@ -51,12 +50,9 @@ export default function StorageOptionsGrid({
)}
{...props}
>
{/* 核心网格区 */}
<div className="p-3">
{/* 💡 优化点:加入 items-stretch,确保左右卡片高度绝对对齐 */}
<div className="grid grid-cols-2 gap-2.5 items-stretch">
{optionKeys.map(({ key, isCount }) => (
/* 💡 终极修复:直接把 key 挂在 OptionItem 上,移除了无意义的包裹 div */
<OptionItem
key={key}
labelKey={`storageCleaner:options.${key}`}
@@ -69,10 +65,6 @@ export default function StorageOptionsGrid({
</div>
</div>
{/* 3. 全选功能底护栏超进化:
- 整体赋予 cursor-pointer 和 onClick,点击一整行都能触发全选。
- 悬停时自动变色提示可点击 (hover:bg-muted/50)。
*/}
<div
onClick={handleToggleAll}
className="border-t border-border flex justify-between items-center px-4 py-2.5 bg-muted/20 hover:bg-muted/50 cursor-pointer select-none"
@@ -81,11 +73,6 @@ export default function StorageOptionsGrid({
{t('storageCleaner:selectAll')}
</Label>
{/* 4. 降维打击:调用标准的 shadcn/ui Checkbox
- 阻止冒泡:防止事件重复触发。
- 完美注入半选逻辑:当 allSelected 为 false 但 someSelected 为 true 时,
组件会自动呈现优雅的 "—" (减号) 半选视觉状态,向主流系统控制台高标准看齐!
*/}
<Checkbox
checked={allSelected ? true : someSelected ? 'indeterminate' : false}
onClick={(e) => e.stopPropagation()}
-10
View File
@@ -16,7 +16,6 @@ const LiveClock = React.memo(({ unit, onUseNow, className, ...props }: LiveClock
const { showMessage } = useSnackbar();
const onUseNowRef = useRef(onUseNow);
// 1. 采用毫秒/秒的双态原子计数,避免无意义的重绘
const [currentDisplay, setCurrentDisplay] = useState(() => {
const initNow = Date.now();
return {
@@ -36,21 +35,17 @@ const LiveClock = React.memo(({ unit, onUseNow, className, ...props }: LiveClock
const rightNow = Date.now();
const nextText = String(Math.floor(rightNow / (unit === 'ms' ? 1 : 1000)));
// 性能核心:只有当生成的文本内容发生变化时,才触发 React 的 State 更新。
// 在“秒(s)”单位下,这可以让组件的渲染频率暴跌 90%,做到极度省电和高性能。
setCurrentDisplay((prev) => {
if (prev.text === nextText) return prev;
return { rawTime: rightNow, text: nextText };
});
};
// 200ms 的高速低延迟轮询,比 1000ms 更具响应灵敏度,且因为上面有过滤,完全不用担心引发性能损耗
const tickId = setInterval(tick, 200);
return () => clearInterval(tickId);
}, [unit]);
const handleUseNow = useCallback(() => {
// 捕获真实极其精准的绝对时间戳
onUseNowRef.current(currentDisplay.rawTime);
showMessage?.(t('timestamp:usedSuccess'), { severity: 'success' });
}, [currentDisplay.rawTime, showMessage, t]);
@@ -58,9 +53,6 @@ const LiveClock = React.memo(({ unit, onUseNow, className, ...props }: LiveClock
return (
<div
className={cn(
// 3. 完美适配 shadcn 暗黑模式:
// 不再写死 bg-primary/10,改用更高级的 bg-secondary/50 和中性边框,
// 在任何主题色下都能表现得低调且极具质感。
'flex items-center gap-3 px-3 h-10 rounded-lg border border-border/80 bg-secondary/50',
className,
)}
@@ -70,12 +62,10 @@ const LiveClock = React.memo(({ unit, onUseNow, className, ...props }: LiveClock
{t('timestamp:currentTs')}
</span>
{/* 4. tabular-nums 强制使用等宽数字布局,彻底消灭数字跳动时字符宽度不同带来的抖动颤噪感 */}
<span className="flex-1 font-mono font-bold text-foreground text-sm tracking-tight leading-none truncate tabular-nums">
{currentDisplay.text}
</span>
{/* 5. 按钮重构成精巧的 shadcn 原子微动效风格 */}
<button
type="button"
onClick={handleUseNow}
-10
View File
@@ -67,11 +67,6 @@ const ResultView = React.memo(
{t('timestamp:resultLabel')}
</span>
{/*
2. 核心结果大卡片:
对齐 shadcn 官方卡片风格,使用 bg-card、border-border 构筑多层级阴影。
核心数值直接拉粗为 text-foreground (在黑夜模式下会自动转为大气的纯白,完美避开刺眼强光)
*/}
<div className="bg-card text-card-foreground border border-border p-4 sm:p-5 rounded-xl relative mb-3.5 shadow-sm flex justify-between items-center gap-4 focus-within:ring-1 focus-within:ring-ring">
<span className="font-mono font-extrabold text-foreground break-all text-xl sm:text-2xl tracking-tight leading-tight select-all tabular-nums">
{result}
@@ -83,11 +78,6 @@ const ResultView = React.memo(
/>
</div>
{/*
3. 衍生的附加参考数据区:
背景改为低饱和度的 bg-muted/40 隔离带。
内部数值降级为 text-muted-foreground,建立教科书般的完美“视觉权重层级”。
*/}
<div className="bg-muted/40 p-4 rounded-xl border border-border/50 flex flex-col gap-3">
{[
{ label: t('timestamp:relativeTime'), value: extraInfo?.relative },
-24
View File
@@ -6,7 +6,6 @@ import { useTimestampConverter } from './useTimestampConverter';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils';
// 1. 引入标准的 shadcn/ui 原子表单组件(代替原生的原生 Input 和 Select
import { Input } from '@/components/ui/input';
import {
Select,
@@ -19,7 +18,6 @@ import {
export default function Index() {
const { t } = useI18n('timestamp');
// 2. 完美对接全新重构后的统一单源响应式 Hook
const {
mode,
input,
@@ -37,15 +35,11 @@ export default function Index() {
return (
<div className="min-h-screen bg-background text-foreground antialiased selection:bg-primary/20">
<div className="max-w-7xl mx-auto p-4 sm:p-6 space-y-4">
{/* 动态参考信息时钟条 */}
<LiveClock unit={unit} onUseNow={handleUseNow} />
{/* 核心工作台网格:桌面端 md+ 左右等宽分栏;移动端单栏堆叠 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-stretch">
{/* 左栏:转换工作台 */}
<div className="p-5 rounded-xl border border-border bg-card text-card-foreground shadow-sm flex flex-col justify-between">
<div className="flex flex-col gap-4">
{/* 模式选择切换组 */}
<SwitchButtonGroup
value={mode}
options={[
@@ -56,12 +50,7 @@ export default function Index() {
size="small"
/>
{/* 输入交互区 */}
<div className="flex flex-col gap-1.5">
{/*
3. 替换为标准的 shadcn <Input /> 组件:
享受原生高水准的 focus-visible 环形动画响应。
*/}
<Input
type="text"
placeholder={
@@ -75,13 +64,10 @@ export default function Index() {
)}
/>
{/* 错误自愈提示 */}
{error && <p className="text-destructive text-xs font-medium px-0.5">{error}</p>}
</div>
{/* 核心配置群:单位切换 + 时区选择紧凑横排 */}
<div className="flex flex-col sm:flex-row items-stretch gap-3 w-full">
{/* 时间精度单位选择 */}
<SwitchButtonGroup
value={unit}
options={[
@@ -93,10 +79,6 @@ export default function Index() {
className="sm:w-auto shrink-0" // 窄屏下全宽,宽屏下自适应收缩
/>
{/*
4. 降维打击:将原生 <select> 强行超进化为标准的 shadcn <Select>
全操作系统的样式绝对一致,完美融合暗黑模式,悬浮弹窗自带微距磨砂玻璃阻尼动效。
*/}
<Select value={zone} onValueChange={(v: string) => setZone(v as typeof zone)}>
<SelectTrigger className="flex-1 font-mono font-semibold h-9 shadow-sm bg-background">
<SelectValue placeholder="选择时区" />
@@ -115,14 +97,8 @@ export default function Index() {
</Select>
</div>
</div>
{/*
5. 彻底移除了原先丑陋的手动 convert 按钮!
在响应式 Hook 的加持下,此处留白或由排版自然撑开,界面视觉极其干净。
*/}
</div>
{/* 右栏:结果实时流展示卡片 */}
<div className="p-5 rounded-xl border border-border bg-card text-card-foreground shadow-sm h-full flex flex-col">
<ResultView result={result} mode={mode} unit={unit} zone={zone} showEmptyPlaceholder />
</div>
@@ -31,11 +31,8 @@ export function useTimestampConverter(): UseTimestampConverterReturn {
const [unit, setUnit] = useState<UnitType>('ms');
const [zone, setZone] = useState<ZoneType>('Asia/Shanghai');
// 1. 唯一受控源:不再区分 ts/dt,输入框在当前模式下展现的就是它的值
const [input, setInput] = useState(() => String(Date.now()));
// 2. 核心魔法:利用 useMemo 达成 0 延迟响应式转换 (Reactive Pipeline)
// 只要 input, mode, unit, zone 任何一个发生改变,结果和错误信息自动流出,废除 convert 按钮
const conversionPipeline = useMemo(() => {
const rawInput = input.trim();
if (!rawInput) return { result: '', error: '' };
@@ -63,7 +60,6 @@ export function useTimestampConverter(): UseTimestampConverterReturn {
const { result, error } = conversionPipeline;
// 3. 处理右键菜单联动(直接修改唯一的 input,衍生转换自动触发)
const handleContextMenuData = useCallback((payload: string) => {
const trimmed = payload.trim();
if (isTimestampLike(trimmed)) {
@@ -96,8 +92,6 @@ export function useTimestampConverter(): UseTimestampConverterReturn {
[mode, unit, zone],
);
// 4. 尊享级连贯交互:切换 Mode 时,自动把上一个模式计算出的结果喂进输入框
// 比如:输入时间戳 -> 得到日期 -> 切换模式 -> 日期直接进入输入框,方便用户进行微调反向转换
const handleSetMode = useCallback(
(newMode: 'ts2dt' | 'dt2ts') => {
setMode(newMode);