From 0977f068217d9ca566d6a5da184e847e8a2fa330 Mon Sep 17 00:00:00 2001 From: rickyzakariap Date: Thu, 2 Oct 2025 15:57:09 +0700 Subject: [PATCH] feat: add Hash Generator tool with MD5, SHA-1, SHA-256, SHA-512 support - Add comprehensive hash generation tool supporting multiple algorithms - Implement single hash and batch hash generation modes - Add file upload support for text files (max 10MB) - Include copy to clipboard functionality for all results - Add color-coded hash results with timestamps - Implement proper error handling and loading states - Use Web Crypto API for SHA algorithms (SHA-1, SHA-256, SHA-512) - Add demo MD5 implementation with security warnings - Follow existing codebase patterns and UI consistency - Include comprehensive documentation and README Features: - Tabbed interface for single vs batch generation - File drag-and-drop support - Sample text functionality - Clear/reset functionality - Responsive design matching existing tools - TypeScript with proper type definitions --- src/App.tsx | 3 + src/features/hash-generator/HashGenerator.tsx | 288 ++++++++++++++++++ src/features/hash-generator/README.md | 75 +++++ .../hash-generator/useHashGenerator.ts | 138 +++++++++ src/lib/types.ts | 9 +- 5 files changed, 512 insertions(+), 1 deletion(-) create mode 100644 src/features/hash-generator/HashGenerator.tsx create mode 100644 src/features/hash-generator/README.md create mode 100644 src/features/hash-generator/useHashGenerator.ts diff --git a/src/App.tsx b/src/App.tsx index 99181ee..699e4a3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,6 +12,7 @@ import { HtmlToJsx } from "@/features/html-to-jsx/HtmlToJsx"; import { ToolType } from "@/lib/types"; import { StringEncoder } from "./features/StringEncoder/StringEncoder"; import { JWTDecoder } from "./features/jwt-decoder/JWTDecoder"; +import { HashGenerator } from "./features/hash-generator/HashGenerator"; import WindowBar from "./components/windows/window-bar"; function App() { @@ -37,6 +38,8 @@ function App() { return ; case 'jwt-decoder': return ; + case 'hash-generator': + return ; default: return ; } diff --git a/src/features/hash-generator/HashGenerator.tsx b/src/features/hash-generator/HashGenerator.tsx new file mode 100644 index 0000000..a2851b8 --- /dev/null +++ b/src/features/hash-generator/HashGenerator.tsx @@ -0,0 +1,288 @@ +import { useState, useRef } from 'react'; +import { ToolCard } from '@/components/ui/tool-card'; +import { SectionHeader } from '@/components/ui/section-header'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import { Input } from '@/components/ui/input'; +import { Badge } from '@/components/ui/badge'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Copy, Hash, Upload, Wand2, Trash2, FileText } from 'lucide-react'; +import { toast } from 'sonner'; +import { useHashGenerator, type HashType, type HashResult } from './useHashGenerator'; + +export function HashGenerator() { + const { + results, + isGenerating, + generateSingleHash, + generateAllHashes, + generateHashFromFile, + clearResults + } = useHashGenerator(); + + const [input, setInput] = useState(''); + const [selectedHashType, setSelectedHashType] = useState('sha256'); + const [singleResult, setSingleResult] = useState(null); + const fileInputRef = useRef(null); + + const hashTypes: { value: HashType; label: string; description: string }[] = [ + { value: 'md5', label: 'MD5', description: '128-bit hash (demo implementation, not real MD5)' }, + { value: 'sha1', label: 'SHA-1', description: '160-bit hash (deprecated for security)' }, + { value: 'sha256', label: 'SHA-256', description: '256-bit hash (recommended)' }, + { value: 'sha512', label: 'SHA-512', description: '512-bit hash (most secure)' } + ]; + + const handleGenerateSingle = async () => { + try { + const result = await generateSingleHash(input, selectedHashType); + setSingleResult(result); + toast.success(`${selectedHashType.toUpperCase()} hash generated successfully`); + } catch (error) { + toast.error(error instanceof Error ? error.message : 'Failed to generate hash'); + } + }; + + const handleGenerateAll = async () => { + try { + await generateAllHashes(input); + toast.success('All hashes generated successfully'); + } catch (error) { + toast.error(error instanceof Error ? error.message : 'Failed to generate hashes'); + } + }; + + const handleCopy = async (text: string, type: string) => { + try { + await navigator.clipboard.writeText(text); + toast.success(`${type} copied to clipboard`); + } catch (error) { + toast.error('Failed to copy to clipboard'); + } + }; + + const handleFileUpload = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + // Limit file size to 10MB for performance + if (file.size > 10 * 1024 * 1024) { + toast.error('File size must be less than 10MB'); + return; + } + + try { + const result = await generateHashFromFile(file, selectedHashType); + setSingleResult(result); + toast.success(`Hash generated for file: ${file.name}`); + } catch (error) { + toast.error(error instanceof Error ? error.message : 'Failed to process file'); + } + + // Reset file input + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }; + + const handleClear = () => { + setInput(''); + setSingleResult(null); + clearResults(); + }; + + const sampleText = "Hello, World! This is a sample text for hash generation."; + const handleLoadSample = () => { + setInput(sampleText); + }; + + const getHashColor = (type: HashType): string => { + switch (type) { + case 'md5': return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'; + case 'sha1': return 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200'; + case 'sha256': return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'; + case 'sha512': return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'; + default: return 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200'; + } + }; + + return ( + +
+ {/* Input Section */} +
+ +
+ + + +
+
+