text-transformer / src /components /Base64Converter.jsx
OnyxlMunkey's picture
Text Transformer: Docker app for HF Space
0f5ca68
import React, { useState, useEffect } from 'react';
const Base64Converter = ({ addToMessage, sharedInput, setSharedInput }) => {
const [output, setOutput] = useState('');
const [copied, setCopied] = useState(false);
const [toBase64, setToBase64] = useState(true);
const [hasError, setHasError] = useState(false);
const convertToBase64 = (text) => {
try {
// Handle unicode characters
const encoded = btoa(unescape(encodeURIComponent(text)));
setOutput(encoded);
setHasError(false);
} catch (error) {
setOutput('Error: Invalid input for Base64 encoding.');
setHasError(true);
}
};
const convertFromBase64 = (base64) => {
try {
// Handle unicode characters
const decoded = decodeURIComponent(escape(atob(base64)));
setOutput(decoded);
setHasError(false);
} catch (error) {
setOutput('Error: Invalid Base64 string.');
setHasError(true);
}
};
const handleInputChange = (e) => {
const newText = e.target.value;
// Limit input to 50,000 characters to prevent performance issues
if (newText.length > 50000) {
return;
}
setSharedInput(newText);
if (toBase64) {
convertToBase64(newText);
} else {
convertFromBase64(newText);
}
};
// Recalculate output when component mounts or sharedInput changes from another converter
useEffect(() => {
if (sharedInput) {
if (toBase64) {
convertToBase64(sharedInput);
} else {
convertFromBase64(sharedInput);
}
} else {
setOutput('');
setHasError(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [sharedInput, toBase64]);
const copyToClipboard = () => {
navigator.clipboard.writeText(output);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
const swapConversion = () => {
const oldInput = sharedInput;
setSharedInput(output);
setOutput(oldInput);
setToBase64(!toBase64);
};
return (
<div className="p-6 bg-gradient-to-br from-gray-900 to-black rounded-lg border border-gray-800 hover-glow transition-all duration-300">
<h2 className="text-3xl font-bold mb-6 text-white flex items-center gap-2">
<span className="text-blue-500">🔐</span>
Base64 Converter
</h2>
<div className="grid grid-cols-1 md:grid-cols-[1fr_auto_1fr] gap-6 items-center">
<div className="space-y-2">
<label htmlFor="base64-input" className="block text-sm font-medium text-gray-300 mb-2">
{toBase64 ? 'Text Input' : 'Base64 Input'}
</label>
<textarea
id="base64-input"
className="w-full h-40 p-4 bg-black border-2 border-blue-500/30 rounded-lg text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all duration-300 hover:border-blue-500/50 resize-none"
placeholder={toBase64 ? 'Enter text...' : 'Enter Base64...'}
value={sharedInput}
onChange={handleInputChange}
/>
</div>
<div className="flex justify-center">
<button
onClick={swapConversion}
className="p-3 bg-gradient-to-br from-blue-600 to-blue-800 text-white rounded-full hover:from-blue-500 hover:to-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all duration-300 hover:scale-110 active:scale-95 shadow-lg hover:shadow-blue-500/50"
title="Swap conversion direction"
>
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7h12m0 0l-4-4m4 4l-4 4m2 5H4m0 0l4 4m-4-4l4-4" />
</svg>
</button>
</div>
<div className="space-y-2">
<label htmlFor="base64-output" className="block text-sm font-medium text-gray-300 mb-2">
{toBase64 ? 'Base64 Output' : 'Text Output'}
</label>
<div id="base64-output" className="w-full h-40 p-4 bg-black border-2 border-blue-500/30 rounded-lg text-white relative overflow-auto hover:border-blue-500/50 transition-all duration-300">
<div className={`break-words ${hasError ? 'text-red-400' : ''}`}>
{output || <span className="text-gray-500">Output will appear here...</span>}
</div>
{output && !hasError && (
<div className="absolute top-2 right-2 flex flex-col gap-2">
<button
onClick={copyToClipboard}
className="px-4 py-2 bg-gradient-to-r from-invader-pink to-pink-600 text-black font-semibold rounded-md hover:from-pink-500 hover:to-pink-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-invader-pink transition-all duration-300 shadow-md hover:shadow-lg hover:scale-105 active:scale-95"
>
{copied ? '✓ Copied!' : '📋 Copy'}
</button>
<button
onClick={() => addToMessage(output)}
className="px-4 py-2 bg-gradient-to-r from-invader-green to-green-600 text-black font-semibold rounded-md hover:from-green-500 hover:to-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-invader-green transition-all duration-300 shadow-md hover:shadow-lg hover:scale-105 active:scale-95"
>
➕ Add
</button>
</div>
)}
</div>
</div>
</div>
</div>
);
};
export default Base64Converter;