uploads

Log | Files | Refs

commit 7aab5ef739ddd4ba6c81a26726a02d7ad7d51b6b
parent b9ecab05cdd4b0f9d94c07edf72b5713c3d1cdc1
Author: Amit Dutta <mail@amit.is-a.dev>
Date:   Mon, 20 Jul 2026 20:41:21 +0530

Update index.html
Diffstat:
Mindex.html | 277+++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 137 insertions(+), 140 deletions(-)

diff --git a/index.html b/index.html @@ -5,7 +5,7 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Upload Hub</title> - <!-- This script instantly checks for ?item= and redirects before the page even loads --> + <!-- Instant redirect check --> <script> const urlParams = new URLSearchParams(window.location.search); const item = urlParams.get('item'); @@ -14,15 +14,12 @@ } </script> - <!-- Fonts: Lexend --> + <!-- Fonts: Lexend & Material Symbols --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700&display=swap" rel="stylesheet"> - - <!-- Material Symbols (Icons) --> - <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" rel="stylesheet" /> + <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,1,0" rel="stylesheet" /> - <!-- Tailwind CSS --> <script src="https://cdn.tailwindcss.com"></script> <script> tailwind.config = { @@ -32,20 +29,38 @@ sans: ['Lexend', 'sans-serif'], }, colors: { - // Material 3 Color Palette (Light/Dark adaptive approximations) + // Strict M3 Color Palette (Light/Dark) m3: { - surface: '#FEF7FF', - surfaceDark: '#141218', - surfaceContainer: '#F3EDF7', - surfaceContainerDark: '#211F26', - primary: '#6750A4', - primaryDark: '#D0BCFF', + background: '#FDFCFB', + backgroundDark: '#1A1C19', + surface: '#FDFCFB', + surfaceDark: '#1A1C19', + surfaceContainer: '#F1F1EA', + surfaceContainerDark: '#2D2F2B', + surfaceContainerHigh: '#EBEBE4', + surfaceContainerHighDark: '#333531', + surfaceContainerHighest: '#E5E6DE', + surfaceContainerHighestDark: '#434541', + onSurface: '#1A1C19', + onSurfaceDark: '#E3E3DC', + onSurfaceVariant: '#43483E', + onSurfaceVariantDark: '#C3C8BC', + primary: '#386A20', + primaryDark: '#9CD67D', onPrimary: '#FFFFFF', - onPrimaryDark: '#381E72', - secondaryContainer: '#E8DEF8', - onSecondaryContainer: '#1D192B', - error: '#B3261E', - errorDark: '#F2B8B5' + onPrimaryDark: '#0C3900', + secondaryContainer: '#D7E8CD', + secondaryContainerDark: '#374B2E', + onSecondaryContainer: '#111F0F', + onSecondaryContainerDark: '#D7E8CD', + error: '#BA1A1A', + errorDark: '#FFB4AB', + errorContainer: '#FFDAD6', + errorContainerDark: '#93000A', + onErrorContainer: '#410002', + onErrorContainerDark: '#FFDAD6', + outline: '#74796D', + outlineDark: '#8D9286', } } } @@ -54,148 +69,129 @@ </script> <style> + /* Zero animations, instant state changes only */ body { - /* Fallback background if Tailwind fails to load instantly */ - background-color: #FEF7FF; + background-color: #FDFCFB; + margin: 0; + -webkit-font-smoothing: antialiased; } @media (prefers-color-scheme: dark) { - body { background-color: #141218; } + body { background-color: #1A1C19; } } - .progress-bar-fill { - transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); - } - - .m3-input { - transition: all 0.2s ease; - box-shadow: 0 1px 0 0 #49454F; /* Bottom border effect */ - } - .m3-input:focus { - box-shadow: 0 2px 0 0 #6750A4; /* Active bottom border */ + /* Instant focus states, no transitions */ + input:focus { outline: none; + border-bottom-color: #386A20; } @media (prefers-color-scheme: dark) { - .m3-input { box-shadow: 0 1px 0 0 #CAC4D0; } - .m3-input:focus { box-shadow: 0 2px 0 0 #D0BCFF; } + input:focus { border-bottom-color: #9CD67D; } } - /* Smooth fade ins */ - .fade-in { - animation: fadeIn 0.3s ease-in forwards; - } - @keyframes fadeIn { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } - } + /* Utility classes for instant display toggling */ + .show { display: block !important; } + .show-flex { display: flex !important; } + .hide { display: none !important; } </style> </head> -<body class="bg-m3-surface dark:bg-m3-surfaceDark text-gray-900 dark:text-gray-100 flex flex-col items-center justify-center min-h-screen p-4 m-0 antialiased font-sans transition-colors duration-300"> +<body class="bg-m3-background dark:bg-m3-backgroundDark text-m3-onSurface dark:text-m3-onSurfaceDark flex flex-col items-center justify-center min-h-screen p-4 font-sans"> - <!-- Main Card Container (Hidden initially to prevent flash before redirect check) --> - <div id="ui-container" class="bg-m3-surfaceContainer dark:bg-m3-surfaceContainerDark w-full max-w-[480px] rounded-[28px] p-8 shadow-sm transition-colors duration-300" style="display: none;"> + <div id="ui-container" class="w-full max-w-[480px] hide"> - <!-- Header --> - <div class="flex items-center gap-3 mb-6"> - <div class="bg-m3-secondaryContainer text-m3-onSecondaryContainer p-3 rounded-xl flex items-center justify-center"> - <span class="material-symbols-outlined">cloud_upload</span> - </div> - <div> - <h1 class="text-xl font-semibold leading-tight">Upload File</h1> - <p class="text-sm text-gray-600 dark:text-gray-400">Drag & drop or browse</p> - </div> + <!-- Minimal Header --> + <div class="mb-8"> + <h1 class="text-2xl font-bold tracking-tight">Upload</h1> + <p class="text-sm text-m3-onSurfaceVariant dark:text-m3-onSurfaceVariantDark mt-1">Store files permanently on Hugging Face</p> </div> - <!-- Input Field --> - <div class="mb-6 relative"> - <input type="text" id="custom-id" placeholder=" " class="m3-input block w-full px-4 pt-5 pb-2 text-base bg-m3-surface dark:bg-[#2B2930] rounded-t-lg peer placeholder-transparent"> - <label for="custom-id" class="absolute text-sm text-gray-500 dark:text-gray-400 duration-300 transform -translate-y-3 scale-75 top-4 z-10 origin-[0] left-4 peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-3 peer-focus:text-m3-primary dark:peer-focus:text-m3-primaryDark">Custom link ID (Optional)</label> + <!-- Static Input Field --> + <div class="flex flex-col gap-1.5 mb-6"> + <label for="custom-id" class="text-sm font-medium text-m3-onSurfaceVariant dark:text-m3-onSurfaceVariantDark">Custom ID (Optional)</label> + <input type="text" id="custom-id" placeholder="e.g., source-code" class="px-4 py-3 bg-m3-surfaceContainerHighest dark:bg-m3-surfaceContainerHighestDark text-m3-onSurface dark:text-m3-onSurfaceDark rounded-t-md border-b-2 border-m3-outline dark:border-m3-outlineDark placeholder-gray-500"> </div> - <!-- Dropzone --> - <div id="dropzone" class="border-2 border-dashed border-gray-400 dark:border-gray-600 rounded-[20px] p-8 text-center cursor-pointer hover:bg-m3-surface dark:hover:bg-[#2B2930] hover:border-m3-primary dark:hover:border-m3-primaryDark transition-all duration-200 group relative"> - <span class="material-symbols-outlined text-4xl text-gray-400 group-hover:text-m3-primary dark:group-hover:text-m3-primaryDark mb-3 block transition-colors">upload_file</span> - <p class="text-sm font-medium mb-1">Click or drag a file here</p> - <p class="text-xs text-gray-500 dark:text-gray-400">Max limit: 4.5MB (Vercel limits)</p> + <!-- Solid M3 Dropzone (No dashed borders, solid surface) --> + <div id="dropzone" class="bg-m3-surfaceContainer dark:bg-m3-surfaceContainerDark hover:bg-m3-surfaceContainerHigh dark:hover:bg-m3-surfaceContainerHighDark rounded-[24px] p-10 flex flex-col items-center justify-center cursor-pointer mb-6 border border-transparent"> + <span class="material-symbols-outlined text-[48px] text-m3-primary dark:text-m3-primaryDark mb-4">upload</span> + <span class="text-base font-semibold text-m3-onSurface dark:text-m3-onSurfaceDark">Click or drag file</span> + <span class="text-sm mt-1 text-m3-onSurfaceVariant dark:text-m3-onSurfaceVariantDark">Max 4.5MB per file</span> <input type="file" id="file-input" class="hidden"> </div> - <!-- Status Area (Progress / Result / Error) --> - <div id="status-area" class="mt-6 hidden"> - - <!-- Progress View --> - <div id="progress-container" class="hidden fade-in"> - <div class="flex justify-between items-center mb-2"> - <span class="text-sm font-medium">Uploading...</span> - <span id="progress-text" class="text-sm font-semibold text-m3-primary dark:text-m3-primaryDark">0%</span> - </div> - <div class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2 overflow-hidden"> - <div id="progress-bar" class="progress-bar-fill bg-m3-primary dark:bg-m3-primaryDark h-2 rounded-full w-0"></div> - </div> + <!-- Progress Bar (Utilitarian, no animation on width change) --> + <div id="progress-container" class="hide mb-6"> + <div class="flex justify-between items-center mb-2"> + <span class="text-sm font-semibold text-m3-onSurfaceVariant dark:text-m3-onSurfaceVariantDark">Uploading...</span> + <span id="progress-text" class="text-sm font-bold text-m3-primary dark:text-m3-primaryDark">0%</span> + </div> + <div class="w-full bg-m3-surfaceContainerHighest dark:bg-m3-surfaceContainerHighestDark h-2 rounded-none"> + <div id="progress-bar" class="bg-m3-primary dark:bg-m3-primaryDark h-2 rounded-none" style="width: 0%;"></div> </div> + </div> - <!-- Result View --> - <div id="result-container" class="hidden fade-in bg-m3-surface dark:bg-[#2B2930] rounded-2xl p-4 border border-gray-200 dark:border-gray-700"> - <div class="flex items-center gap-2 mb-3 text-green-600 dark:text-green-400"> - <span class="material-symbols-outlined">check_circle</span> - <span class="font-semibold text-sm">Upload complete!</span> - </div> - - <div class="bg-m3-surfaceContainer dark:bg-m3-surfaceContainerDark p-3 rounded-xl flex items-center justify-between gap-3 overflow-hidden"> - <a id="final-link" href="#" target="_blank" class="text-sm text-m3-primary dark:text-m3-primaryDark truncate hover:underline font-medium"></a> - <button id="copy-btn" class="flex-shrink-0 bg-m3-primary dark:bg-m3-primaryDark text-white dark:text-gray-900 rounded-full w-10 h-10 flex items-center justify-center hover:opacity-90 transition-opacity focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-m3-primary" title="Copy to clipboard"> - <span class="material-symbols-outlined text-[20px]" id="copy-icon">content_copy</span> - </button> - </div> + <!-- Success Result --> + <div id="result-container" class="hide bg-m3-secondaryContainer dark:bg-m3-secondaryContainerDark p-4 rounded-[16px] mb-6 border border-transparent"> + <div class="flex items-center gap-2 mb-3"> + <span class="material-symbols-outlined text-m3-onSecondaryContainer dark:text-m3-onSecondaryContainerDark">check_circle</span> + <span class="font-bold text-m3-onSecondaryContainer dark:text-m3-onSecondaryContainerDark text-sm">Upload Successful</span> + </div> + <div class="flex items-center gap-2 bg-m3-background dark:bg-m3-backgroundDark p-2 pl-3 rounded-[8px]"> + <input type="text" id="final-link" readonly class="bg-transparent border-none outline-none text-sm text-m3-onSurface dark:text-m3-onSurfaceDark w-full truncate cursor-default"> + <button id="copy-btn" class="bg-m3-primary dark:bg-m3-primaryDark text-m3-onPrimary dark:text-m3-onPrimaryDark px-4 py-2 rounded-[8px] text-sm font-medium hover:bg-opacity-90 active:bg-opacity-100 flex items-center gap-2"> + <span class="material-symbols-outlined text-[18px]" id="copy-icon">content_copy</span> + <span>Copy</span> + </button> </div> + </div> - <!-- Error View --> - <div id="error-container" class="hidden fade-in text-m3-error dark:text-m3-errorDark bg-[#F9DEDC] dark:bg-[#8C1D18] bg-opacity-20 dark:bg-opacity-20 p-4 rounded-2xl flex items-start gap-3"> + <!-- Error Container --> + <div id="error-container" class="hide bg-m3-errorContainer dark:bg-m3-errorContainerDark text-m3-onErrorContainer dark:text-m3-onErrorContainerDark p-4 rounded-[16px] mb-6"> + <div class="flex items-start gap-3"> <span class="material-symbols-outlined">error</span> <span id="error-text" class="text-sm font-medium mt-0.5"></span> </div> - </div> + </div> <script> - // Only show the UI if we aren't redirecting an ?item= request + // Reveal UI only if not redirecting if (!item) { - document.getElementById('ui-container').style.display = 'block'; + document.getElementById('ui-container').classList.remove('hide'); + document.getElementById('ui-container').classList.add('show'); } const dropzone = document.getElementById('dropzone'); const fileInput = document.getElementById('file-input'); const customIdInput = document.getElementById('custom-id'); - // UI Elements - const statusArea = document.getElementById('status-area'); const progressContainer = document.getElementById('progress-container'); const progressBar = document.getElementById('progress-bar'); const progressText = document.getElementById('progress-text'); const resultContainer = document.getElementById('result-container'); - const finalLink = document.getElementById('final-link'); + const finalLinkInput = document.getElementById('final-link'); const copyBtn = document.getElementById('copy-btn'); const copyIcon = document.getElementById('copy-icon'); const errorContainer = document.getElementById('error-container'); const errorText = document.getElementById('error-text'); - // Drag and drop event bindings + // Drag & Drop Handling (Instant state swaps, no transition classes) dropzone.addEventListener('click', () => fileInput.click()); dropzone.addEventListener('dragover', (e) => { e.preventDefault(); - dropzone.classList.add('border-m3-primary', 'dark:border-m3-primaryDark', 'bg-m3-surface', 'dark:bg-[#2B2930]'); + dropzone.style.borderColor = 'var(--tw-colors-m3-primary)'; }); dropzone.addEventListener('dragleave', () => { - dropzone.classList.remove('border-m3-primary', 'dark:border-m3-primaryDark', 'bg-m3-surface', 'dark:bg-[#2B2930]'); + dropzone.style.borderColor = 'transparent'; }); dropzone.addEventListener('drop', (e) => { e.preventDefault(); - dropzone.classList.remove('border-m3-primary', 'dark:border-m3-primaryDark', 'bg-m3-surface', 'dark:bg-[#2B2930]'); + dropzone.style.borderColor = 'transparent'; if (e.dataTransfer.files.length) { uploadFile(e.dataTransfer.files[0]); } @@ -207,110 +203,111 @@ } }); - // Copy button logic + // Copy Button Logic (Instant text swap) copyBtn.addEventListener('click', () => { - const linkToCopy = finalLink.href; - - // Modern clipboard API + const linkToCopy = finalLinkInput.value; navigator.clipboard.writeText(linkToCopy).then(() => { - // Success visual feedback copyIcon.textContent = 'check'; - copyBtn.classList.add('bg-green-500', 'dark:bg-green-400'); + copyBtn.querySelector('span:last-child').textContent = 'Copied'; setTimeout(() => { copyIcon.textContent = 'content_copy'; - copyBtn.classList.remove('bg-green-500', 'dark:bg-green-400'); + copyBtn.querySelector('span:last-child').textContent = 'Copy'; }, 2000); }).catch(err => { - console.error('Failed to copy: ', err); - // Fallback for older browsers / strict environments - const tempInput = document.createElement("input"); - tempInput.value = linkToCopy; - document.body.appendChild(tempInput); - tempInput.select(); + // Fallback + finalLinkInput.select(); document.execCommand("copy"); - document.body.removeChild(tempInput); - copyIcon.textContent = 'check'; - setTimeout(() => { copyIcon.textContent = 'content_copy'; }, 2000); + copyBtn.querySelector('span:last-child').textContent = 'Copied'; + setTimeout(() => { + copyIcon.textContent = 'content_copy'; + copyBtn.querySelector('span:last-child').textContent = 'Copy'; + }, 2000); }); }); function resetUI() { - statusArea.classList.remove('hidden'); - progressContainer.classList.add('hidden'); - resultContainer.classList.add('hidden'); - errorContainer.classList.add('hidden'); + progressContainer.classList.remove('show'); + progressContainer.classList.add('hide'); + resultContainer.classList.remove('show'); + resultContainer.classList.add('hide'); + errorContainer.classList.remove('show-flex'); + errorContainer.classList.add('hide'); progressBar.style.width = '0%'; progressText.innerText = '0%'; } + // Real Progress Upload Logic via XMLHttpRequest function uploadFile(file) { resetUI(); if (file.size > 4.5 * 1024 * 1024) { - errorText.innerText = "Error: File exceeds 4.5MB Vercel limit."; - errorContainer.classList.remove('hidden'); + errorText.innerText = "File exceeds 4.5MB serverless limit."; + errorContainer.classList.remove('hide'); + errorContainer.classList.add('show-flex'); return; } - // Show progress state - progressContainer.classList.remove('hidden'); + progressContainer.classList.remove('hide'); + progressContainer.classList.add('show'); const formData = new FormData(); formData.append('file', file); const customId = customIdInput.value.trim(); - if (customId) { - formData.append('custom_id', customId); - } + if (customId) formData.append('custom_id', customId); - // Use XMLHttpRequest to track progress natively const xhr = new XMLHttpRequest(); xhr.open('POST', '/api/upload', true); xhr.upload.onprogress = function(e) { if (e.lengthComputable) { const percentComplete = Math.round((e.loaded / e.total) * 100); + // Instant width update, no CSS transition progressBar.style.width = percentComplete + '%'; progressText.innerText = percentComplete + '%'; } }; xhr.onload = function() { - progressContainer.classList.add('hidden'); // Hide progress bar + progressContainer.classList.remove('show'); + progressContainer.classList.add('hide'); if (xhr.status >= 200 && xhr.status < 300) { try { const data = JSON.parse(xhr.responseText); const finalUrl = `${window.location.origin}/?item=${data.item_id}`; - finalLink.href = finalUrl; - finalLink.innerText = finalUrl; - resultContainer.classList.remove('hidden'); + finalLinkInput.value = finalUrl; + resultContainer.classList.remove('hide'); + resultContainer.classList.add('show'); - // Reset inputs fileInput.value = ''; customIdInput.value = ''; } catch(err) { - errorText.innerText = "Error: Invalid response from server."; - errorContainer.classList.remove('hidden'); + errorText.innerText = "Invalid response from server."; + errorContainer.classList.remove('hide'); + errorContainer.classList.add('show-flex'); } } else { try { const errData = JSON.parse(xhr.responseText); - errorText.innerText = `Error: ${errData.detail || 'Upload failed'}`; + errorText.innerText = errData.detail || 'Upload failed.'; } catch(err) { - errorText.innerText = "Error: Upload failed."; + errorText.innerText = "Upload failed."; } - errorContainer.classList.remove('hidden'); + errorContainer.classList.remove('hide'); + errorContainer.classList.add('show-flex'); } }; xhr.onerror = function() { - progressContainer.classList.add('hidden'); - errorText.innerText = "System Error: Could not connect to server."; - errorContainer.classList.remove('hidden'); + progressContainer.classList.remove('show'); + progressContainer.classList.add('hide'); + errorText.innerText = "System error: Could not connect to server."; + errorContainer.classList.remove('hide'); + errorContainer.classList.add('show-flex'); }; xhr.send(formData);
© notamitgamer • Site Built: 2026-07-21 13:58:23 UTC • git-mirror commit: 1037f62 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror