import { useCallback, useEffect, useMemo, useState } from 'react'; import TextInputArea from '@/components/TextInputArea'; import { useI18n } from '@/utils/chromeI18n'; import CopyButton from '@/components/CopyButton'; import { base64ToText, textToBase64 } from '@/utils/base64Converter'; import SwitchButtonGroup from '@/components/SwitchButtonGroup'; import { useContextMenuData } from '@/utils/useContextMenuData'; import { Button } from '@/components/ui/button'; const IMAGE_DATA_URI_PATTERN = /^\s*data:image\//i; const ERROR_MESSAGE_TO_I18N: Record = { 'Invalid Base64 string': 'invalidBase64', 'Input appears to be binary data (e.g. an image). Please use the Image tab instead.': 'binaryDataDetected', }; interface TextModeProps { onSwitchToImageMode?: () => void; } 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); }, 200); return () => clearTimeout(handle); }, [input]); // 3. 右键联动数据上下文:优雅原地合并受控状态 const handleContextMenuData = useCallback((payload: string) => { setInput(payload); setDebouncedInput(payload); setDirection('decode'); }, []); useContextMenuData({ featureKey: 'base64Converter', onData: handleContextMenuData }); // 💡 4. 贯彻方案 A(彻底消灭 setOutput / setError): // 让所有的转化逻辑、类型安全校验在 useMemo 内存管道中单次渲染一气呵成! const conversionPipeline = useMemo(() => { const trimmed = debouncedInput.trim(); if (!trimmed) return { output: '', error: null }; try { if (direction === 'encode') { const result = textToBase64(debouncedInput); return { output: result.output, error: null }; } else { const decoded = base64ToText(trimmed); return { output: decoded, error: null }; } } catch (e) { const message = e instanceof Error ? e.message : ''; const i18nKey = ERROR_MESSAGE_TO_I18N[message]; return { output: '', error: i18nKey ? t(i18nKey) : message || t('conversionFailed'), }; } }, [debouncedInput, direction, t]); const output = conversionPipeline.output; const error = conversionPipeline.error; const placeholder = direction === 'encode' ? t('textInputPlaceholder') : t('base64InputPlaceholder'); const outputLabel = direction === 'encode' ? t('base64Output') : t('textOutput'); const showImageHint = useMemo( () => direction === 'decode' && IMAGE_DATA_URI_PATTERN.test(input), [direction, input], ); const handleDirectionChange = (value: 'encode' | 'decode') => { if (value === direction) return; setDirection(value); setInput(''); setDebouncedInput(''); }; const handleClear = () => { setInput(''); setDebouncedInput(''); }; return (
{/* 受控方向切流中枢 */}
{/* 高性能受控文本输入端 */} {/* 图片 URI 类型劫持警告引导区: - 💡 修复点:彻底废除原生亮色硬编码 hover:bg-blue-100 类名, - 完美向全站 shadcn 暗黑生态看齐,采用标准的 bg-primary/10 混合变体。 */} {showImageHint && (
{t('imageDataUriHint')}
)} {/* 5. 编码/解码核心数据承载流卡片 */} {output && (
{outputLabel}
2000 ? `${output.substring(0, 2000)}...` : output} showClear={false} minRows={4} />
)}
); }