All in One Hub

style.css code Css :root { --background-color: #1E1E2F; --text-color: #EAEAEA; --header-background: #2B2D42; --accent-color: #FFD700; --tool-card-background: #3A3D5B; --button-hover-color: #E6C200; --box-shadow-color: rgba(255, 215, 0, 0.2); } * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: Arial, sans-serif; background-color: var(--background-color); color: var(--text-color); line-height: 1.6; padding: 20px; } header { background-color: var(--header-background); padding: 20px 0; text-align: center; margin-bottom: 30px; border-radius: 8px; box-shadow: 0 4px 10px var(--box-shadow-color); } h1 { color: var(--accent-color); font-size: 2.5em; letter-spacing: 2px; } .tool-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 25px; max-width: 1200px; margin: 0 auto; padding: 0 15px; } .tool-card { background-color: var(--tool-card-background); padding: 30px; border-radius: 10px; text-align: center; transition: all 0.3s ease-in-out; cursor: pointer; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); display: flex; flex-direction: column; justify-content: space-between; } .tool-card:hover { background-color: var(--accent-color); color: var(--background-color); transform: translateY(-8px); box-shadow: 0 8px 20px var(--box-shadow-color); } .tool-card:hover h2, .tool-card:hover p { color: var(--background-color); } .tool-card h2 { color: var(--accent-color); margin-bottom: 15px; font-size: 1.8em; transition: color 0.3s ease-in-out; } .tool-card p { color: var(--text-color); font-size: 1.1em; margin-bottom: 20px; flex-grow: 1; transition: color 0.3s ease-in-out; } .tool-card button { background-color: var(--accent-color); color: var(--background-color); border: none; padding: 12px 25px; border-radius: 5px; font-size: 1.1em; font-weight: bold; cursor: pointer; transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); } .tool-card button:hover { background-color: var(--button-hover-color); transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); } /* Modal Styles */ .modal { display: none; /* Hidden by default */ position: fixed; /* Stay in place */ z-index: 1000; /* Sit on top */ left: 0; top: 0; width: 100%; /* Full width */ height: 100%; /* Full height */ overflow: auto; /* Enable scroll if needed */ background-color: rgba(0,0,0,0.7); /* Black w/ opacity */ backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); padding-top: 60px; } .modal-content { background-color: var(--tool-card-background); margin: 5% auto; /* 15% from the top and centered */ padding: 30px; border-radius: 10px; width: 90%; /* Could be more responsive */ max-width: 800px; position: relative; box-shadow: 0 10px 30px var(--box-shadow-color); animation: fadeIn 0.3s ease-out; } .close-button { color: var(--accent-color); position: absolute; top: 15px; right: 25px; font-size: 35px; font-weight: bold; cursor: pointer; transition: color 0.2s ease; } .close-button:hover, .close-button:focus { color: var(--button-hover-color); text-decoration: none; cursor: pointer; } #modal-title { color: var(--accent-color); text-align: center; margin-bottom: 25px; font-size: 2.2em; } #modal-body { padding: 15px 0; color: var(--text-color); font-size: 1.1em; } /* Specific tool input/output styling within modal */ .modal-content input[type="file"], .modal-content input[type="number"], .modal-content input[type="date"], .modal-content input[type="text"], .modal-content textarea, .modal-content select { width: calc(100% - 20px); padding: 12px; margin-bottom: 15px; border: 1px solid var(--accent-color); border-radius: 5px; background-color: var(--background-color); color: var(--text-color); font-size: 1em; transition: border-color 0.3s ease, box-shadow 0.3s ease; } .modal-content input[type="file"]::file-selector-button { background-color: var(--accent-color); color: var(--background-color); border: none; padding: 8px 15px; border-radius: 3px; margin-right: 10px; cursor: pointer; transition: background-color 0.2s ease; } .modal-content input[type="file"]::file-selector-button:hover { background-color: var(--button-hover-color); } .modal-content input[type="number"]:focus, .modal-content input[type="date"]:focus, .modal-content input[type="text"]:focus, .modal-content textarea:focus, .modal-content select:focus { border-color: var(--button-hover-color); outline: none; box-shadow: 0 0 8px var(--box-shadow-color); } .modal-content button { background-color: var(--accent-color); color: var(--background-color); border: none; padding: 12px 25px; border-radius: 5px; font-size: 1.1em; font-weight: bold; cursor: pointer; transition: background-color 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); margin-top: 10px; margin-right: 10px; } .modal-content button:hover { background-color: var(--button-hover-color); transform: translateY(-2px); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); } .modal-content label { display: block; margin-bottom: 8px; color: var(--text-color); font-size: 1em; } .result-output { background-color: var(--background-color); border: 1px solid var(--header-background); padding: 15px; border-radius: 5px; margin-top: 20px; min-height: 50px; font-family: 'Courier New', Courier, monospace; white-space: pre-wrap; word-break: break-all; } canvas { max-width: 100%; height: auto; border: 1px dashed var(--accent-color); margin-top: 15px; display: block; margin-left: auto; margin-right: auto; } .flex-row { display: flex; gap: 15px; margin-bottom: 15px; align-items: center; } .flex-row > div { flex: 1; } .button-group button { margin-right: 10px; margin-bottom: 10px; } /* Animations */ @keyframes fadeIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } } /* Responsive Design */ @media (max-width: 1024px) { .tool-grid { grid-template-columns: repeat(2, 1fr); } } @media (max-width: 768px) { .tool-grid { grid-template-columns: 1fr; } h1 { font-size: 2em; } .tool-card { padding: 25px; } .tool-card h2 { font-size: 1.5em; } .tool-card p { font-size: 1em; } .tool-card button { padding: 10px 20px; font-size: 1em; } .modal-content { margin: 10% auto; padding: 25px; width: 95%; } #modal-title { font-size: 1.8em; } .close-button { font-size: 30px; top: 10px; right: 20px; } } @media (max-width: 480px) { body { padding: 15px; } header { margin-bottom: 20px; } h1 { font-size: 1.8em; } .tool-grid { gap: 20px; } .tool-card { padding: 20px; } .tool-card h2 { font-size: 1.3em; } .modal-content { margin: 15% auto; padding: 20px; } #modal-title { font-size: 1.5em; } .modal-content input, .modal-content textarea, .modal-content select, .modal-content button { font-size: 0.9em; padding: 10px; } .modal-content button { padding: 10px 18px; } .flex-row { flex-direction: column; gap: 10px; } } script.js code JavaScript document.addEventListener('DOMContentLoaded', () => { const tools = [ { id: 'image-converter', name: 'Image Converter', description: 'Convert between JPG, PNG, and WEBP.' }, { id: 'image-compressor', name: 'Image Compressor', description: 'Compress image file size.' }, { id: 'image-cropper', name: 'Image Cropper', description: 'Upload and crop image.' }, { id: 'video-converter', name: 'Video Converter', description: 'Convert video format (MP4 ↔ WebM).' }, { id: 'audio-converter', name: 'Audio Converter', description: 'Convert between MP3 and WAV.' }, { id: 'audio-trimmer', name: 'Audio Trimmer', description: 'Trim audio based on start/end time.' }, { id: 'age-calculator', name: 'Age Calculator', description: 'Calculate age in years, months, and days.' }, { id: 'emi-calculator', name: 'EMI Calculator', description: 'Calculate monthly EMI for loans.' }, { id: 'sip-calculator', name: 'SIP Calculator', description: 'Calculate future value of SIP.' }, { id: 'qr-generator', name: 'QR Code Generator', description: 'Generate downloadable QR codes.' }, { id: 'password-generator', name: 'Password Generator', description: 'Generate secure passwords.' }, { id: 'word-counter', name: 'Word Counter', description: 'Live count of words, characters, and reading time.' }, { id: 'base64-converter', name: 'Base64 Encoder/Decoder', description: 'Convert plain text to base64 and vice versa.' }, { id: 'color-picker', name: 'Color Picker Tool', description: 'Pick a color and display HEX, RGB, and HSL.' }, { id: 'text-to-speech', name: 'Text to Speech', description: 'Convert text into spoken words.' }, { id: 'speech-to-text', name: 'Speech to Text', description: 'Convert voice into text.' }, { id: 'json-formatter', name: 'JSON Formatter', description: 'Format and validate JSON.' }, { id: 'unit-converter', name: 'Unit Converter', description: 'Convert values between various units.' }, { id: 'bmi-calculator', name: 'BMI Calculator', description: 'Calculate Body Mass Index.' }, { id: 'timer-stopwatch', name: 'Timer / Stopwatch', description: 'Simple timer and stopwatch.' }, ]; const toolGrid = document.getElementById('tool-grid'); const toolModal = document.getElementById('tool-modal'); const modalTitle = document.getElementById('modal-title'); const modalBody = document.getElementById('modal-body'); const closeModalButton = document.querySelector('.close-button'); // Populate tool cards tools.forEach(tool => { const toolCard = document.createElement('div'); toolCard.classList.add('tool-card'); toolCard.innerHTML = `

${tool.name}

${tool.description}

`; toolGrid.appendChild(toolCard); }); // Open modal listener toolGrid.addEventListener('click', (event) => { if (event.target.tagName === 'BUTTON') { const toolId = event.target.dataset.toolId; openToolModal(toolId); } }); // Close modal listener closeModalButton.addEventListener('click', () => { toolModal.style.display = 'none'; modalBody.innerHTML = ''; // Clear modal content on close }); toolModal.addEventListener('click', (event) => { if (event.target === toolModal) { toolModal.style.display = 'none'; modalBody.innerHTML = ''; } }); function openToolModal(toolId) { const tool = tools.find(t => t.id === toolId); if (!tool) return; modalTitle.textContent = tool.name; modalBody.innerHTML = ''; // Clear previous tool's content switch (toolId) { case 'image-converter': renderImageConverter(); break; case 'image-compressor': renderImageCompressor(); break; case 'image-cropper': renderImageCropper(); break; case 'video-converter': renderVideoConverter(); break; case 'audio-converter': renderAudioConverter(); break; case 'audio-trimmer': renderAudioTrimmer(); break; case 'age-calculator': renderAgeCalculator(); break; case 'emi-calculator': renderEMICalculator(); break; case 'sip-calculator': renderSIPCalculator(); break; case 'qr-generator': renderQRCodeGenerator(); break; case 'password-generator': renderPasswordGenerator(); break; case 'word-counter': renderWordCounter(); break; case 'base64-converter': renderBase64Converter(); break; case 'color-picker': renderColorPicker(); break; case 'text-to-speech': renderTextToSpeech(); break; case 'speech-to-text': renderSpeechToText(); break; case 'json-formatter': renderJSONFormatter(); break; case 'unit-converter': renderUnitConverter(); break; case 'bmi-calculator': renderBMICalculator(); break; case 'timer-stopwatch': renderTimerStopwatch(); break; default: modalBody.innerHTML = '

Tool not implemented yet.

'; } toolModal.style.display = 'block'; } // --- TOOL RENDERING FUNCTIONS --- function renderImageConverter() { modalBody.innerHTML = ` `; const input = document.getElementById('imageConvertInput'); const formatSelect = document.getElementById('imageConvertFormat'); const convertBtn = document.getElementById('convertImageBtn'); const canvas = document.getElementById('imageConvertCanvas'); const statusP = document.getElementById('imageConvertStatus'); const ctx = canvas.getContext('2d'); convertBtn.addEventListener('click', () => { if (!input.files.length) { statusP.textContent = 'Please select an image file.'; statusP.style.display = 'block'; return; } statusP.textContent = 'Processing...'; statusP.style.display = 'block'; const file = input.files[0]; const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => { canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); const mimeType = formatSelect.value; const fileName = file.name.split('.').slice(0, -1).join('.'); const newExtension = mimeType.split('/')[1]; canvas.toBlob((blob) => { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${fileName}.${newExtension}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); statusP.textContent = `Image converted and downloaded as ${newExtension.toUpperCase()}.`; statusP.style.display = 'block'; }, mimeType, 1); // 1 for maximum quality }; img.src = e.target.result; }; reader.readAsDataURL(file); }); } function renderImageCompressor() { modalBody.innerHTML = ` `; const input = document.getElementById('imageCompressInput'); const qualityInput = document.getElementById('compressQuality'); const compressBtn = document.getElementById('compressImageBtn'); const canvas = document.getElementById('imageCompressCanvas'); const statusP = document.getElementById('imageCompressStatus'); const ctx = canvas.getContext('2d'); compressBtn.addEventListener('click', () => { if (!input.files.length) { statusP.textContent = 'Please select an image file.'; statusP.style.display = 'block'; return; } statusP.textContent = 'Processing...'; statusP.style.display = 'block'; const file = input.files[0]; const reader = new FileReader(); const quality = parseFloat(qualityInput.value); if (isNaN(quality) || quality < 0.1 || quality > 1) { statusP.textContent = 'Please enter a valid quality between 0.1 and 1.0.'; return; } reader.onload = (e) => { const img = new Image(); img.onload = () => { canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); const mimeType = file.type; const fileName = file.name.split('.').slice(0, -1).join('.'); const extension = file.name.split('.').pop(); canvas.toBlob((blob) => { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${fileName}_compressed.${extension}`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); statusP.textContent = `Image compressed and downloaded. Original size: ${(file.size / 1024).toFixed(2)} KB, New size: ${(blob.size / 1024).toFixed(2)} KB.`; statusP.style.display = 'block'; }, mimeType, quality); }; img.src = e.target.result; }; reader.readAsDataURL(file); }); } function renderImageCropper() { modalBody.innerHTML = `
`; const input = document.getElementById('imageCropInput'); const loadImageBtn = document.getElementById('loadImageToCropBtn'); const cropBtn = document.getElementById('cropImageBtn'); const canvas = document.getElementById('imageCropCanvas'); const ctx = canvas.getContext('2d'); const cropSelection = document.getElementById('cropSelection'); const cropX = document.getElementById('cropX'); const cropY = document.getElementById('cropY'); const cropWidth = document.getElementById('cropWidth'); const cropHeight = document.getElementById('cropHeight'); const statusP = document.getElementById('imageCropStatus'); let originalImage = null; let imgLoaded = false; let isDragging = false; let startX, startY; let currentCrop = { x: 0, y: 0, width: 100, height: 100 }; function drawImage() { if (!originalImage) return; canvas.width = originalImage.width; canvas.height = originalImage.height; ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(originalImage, 0, 0); // Set max crop values cropX.max = originalImage.width; cropY.max = originalImage.height; cropWidth.max = originalImage.width; cropHeight.max = originalImage.height; } function updateCropSelectionDiv() { if (!imgLoaded) return; const canvasRect = canvas.getBoundingClientRect(); const scaleX = canvas.width / canvasRect.width; const scaleY = canvas.height / canvasRect.height; cropSelection.style.display = 'block'; cropSelection.style.left = `${currentCrop.x / scaleX}px`; cropSelection.style.top = `${currentCrop.y / scaleY}px`; cropSelection.style.width = `${currentCrop.width / scaleX}px`; cropSelection.style.height = `${currentCrop.height / scaleY}px`; cropX.value = currentCrop.x; cropY.value = currentCrop.y; cropWidth.value = currentCrop.width; cropHeight.value = currentCrop.height; } loadImageBtn.addEventListener('click', () => { if (!input.files.length) { statusP.textContent = 'Please select an image file.'; statusP.style.display = 'block'; return; } const file = input.files[0]; const reader = new FileReader(); reader.onload = (e) => { originalImage = new Image(); originalImage.onload = () => { imgLoaded = true; drawImage(); currentCrop = { x: 0, y: 0, width: Math.min(100, originalImage.width), height: Math.min(100, originalImage.height) }; updateCropSelectionDiv(); statusP.textContent = 'Image loaded. Drag and resize the crop area.'; statusP.style.display = 'block'; }; originalImage.src = e.target.result; }; reader.readAsDataURL(file); }); cropSelection.addEventListener('mousedown', (e) => { if (imgLoaded) { e.preventDefault(); isDragging = true; startX = e.clientX - cropSelection.getBoundingClientRect().left; startY = e.clientY - cropSelection.getBoundingClientRect().top; cropSelection.style.cursor = 'grabbing'; } }); document.addEventListener('mousemove', (e) => { if (!isDragging || !imgLoaded) return; e.preventDefault(); const canvasRect = canvas.getBoundingClientRect(); const selectionRect = cropSelection.getBoundingClientRect(); let newX = e.clientX - startX; let newY = e.clientY - startY; // Constrain movement within canvas bounds newX = Math.max(canvasRect.left, newX); newX = Math.min(canvasRect.right - selectionRect.width, newX); newY = Math.max(canvasRect.top, newY); newY = Math.min(canvasRect.bottom - selectionRect.height, newY); const scaleX = canvas.width / canvasRect.width; const scaleY = canvas.height / canvasRect.height; currentCrop.x = (newX - canvasRect.left) * scaleX; currentCrop.y = (newY - canvasRect.top) * scaleY; updateCropSelectionDiv(); }); document.addEventListener('mouseup', () => { isDragging = false; cropSelection.style.cursor = 'move'; }); new ResizeObserver(() => { if (imgLoaded) { const canvasRect = canvas.getBoundingClientRect(); const selectionRect = cropSelection.getBoundingClientRect(); const scaleX = canvas.width / canvasRect.width; const scaleY = canvas.height / canvasRect.height; currentCrop.width = selectionRect.width * scaleX; currentCrop.height = selectionRect.height * scaleY; updateCropSelectionDiv(); } }).observe(cropSelection); [cropX, cropY, cropWidth, cropHeight].forEach(inputEl => { inputEl.addEventListener('input', () => { if (!imgLoaded) return; currentCrop.x = parseInt(cropX.value) || 0; currentCrop.y = parseInt(cropY.value) || 0; currentCrop.width = parseInt(cropWidth.value) || 0; currentCrop.height = parseInt(cropHeight.value) || 0; // Ensure crop area stays within image bounds currentCrop.x = Math.max(0, Math.min(currentCrop.x, originalImage.width - currentCrop.width)); currentCrop.y = Math.max(0, Math.min(currentCrop.y, originalImage.height - currentCrop.height)); currentCrop.width = Math.max(1, Math.min(currentCrop.width, originalImage.width - currentCrop.x)); currentCrop.height = Math.max(1, Math.min(currentCrop.height, originalImage.height - currentCrop.y)); updateCropSelectionDiv(); }); }); cropBtn.addEventListener('click', () => { if (!originalImage || !imgLoaded) { statusP.textContent = 'Please load an image first.'; statusP.style.display = 'block'; return; } const croppedCanvas = document.createElement('canvas'); const croppedCtx = croppedCanvas.getContext('2d'); croppedCanvas.width = currentCrop.width; croppedCanvas.height = currentCrop.height; croppedCtx.drawImage( originalImage, currentCrop.x, currentCrop.y, currentCrop.width, currentCrop.height, 0, 0, currentCrop.width, currentCrop.height ); croppedCanvas.toBlob((blob) => { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url;