commit 750156eb45ddf34224b5c745c7823836599ddd1c
parent 76206b5299ca7f6449d56fcae9a1d27f40dd0777
Author: Amit Dutta <mail@amit.is-a.dev>
Date: Mon, 20 Jul 2026 20:29:28 +0530
Update index.html
Diffstat:
| M | index.html | | | 389 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
1 file changed, 236 insertions(+), 153 deletions(-)
diff --git a/index.html b/index.html
@@ -10,153 +10,151 @@
const urlParams = new URLSearchParams(window.location.search);
const item = urlParams.get('item');
if (item) {
- // Instantly redirect to the raw Hugging Face Dataset URL
window.location.replace(`https://huggingface.co/datasets/notamitgamer/uploads/resolve/main/${item}`);
}
</script>
- <style>
- :root {
- --bg: #f4f4f5;
- --text: #18181b;
- --primary: #000000;
- --border: #e4e4e7;
- --card-bg: #ffffff;
- --radius: 12px;
+ <!-- Fonts: Lexend -->
+ <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" />
+
+ <!-- Tailwind CSS -->
+ <script src="https://cdn.tailwindcss.com"></script>
+ <script>
+ tailwind.config = {
+ theme: {
+ extend: {
+ fontFamily: {
+ sans: ['Lexend', 'sans-serif'],
+ },
+ colors: {
+ // Material 3 Color Palette (Light/Dark adaptive approximations)
+ m3: {
+ surface: '#FEF7FF',
+ surfaceDark: '#141218',
+ surfaceContainer: '#F3EDF7',
+ surfaceContainerDark: '#211F26',
+ primary: '#6750A4',
+ primaryDark: '#D0BCFF',
+ onPrimary: '#FFFFFF',
+ onPrimaryDark: '#381E72',
+ secondaryContainer: '#E8DEF8',
+ onSecondaryContainer: '#1D192B',
+ error: '#B3261E',
+ errorDark: '#F2B8B5'
+ }
+ }
+ }
+ }
}
+ </script>
+
+ <style>
body {
- font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, sans-serif;
- background-color: var(--bg);
- color: var(--text);
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- min-height: 100vh;
- margin: 0;
- padding: 20px;
- box-sizing: border-box;
- -webkit-font-smoothing: antialiased;
+ /* Fallback background if Tailwind fails to load instantly */
+ background-color: #FEF7FF;
}
- .container {
- background: var(--card-bg);
- padding: 2.5rem;
- border-radius: var(--radius);
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0,0,0,0.02);
- width: 100%;
- max-width: 440px;
- text-align: center;
+ @media (prefers-color-scheme: dark) {
+ body { background-color: #141218; }
}
- h1 {
- margin-top: 0;
- margin-bottom: 1.5rem;
- font-size: 1.4rem;
- font-weight: 600;
- letter-spacing: -0.5px;
- }
- input[type="text"] {
- width: 100%;
- padding: 0.85rem 1rem;
- margin-bottom: 1.2rem;
- border: 1px solid var(--border);
- border-radius: 8px;
- font-size: 0.95rem;
- box-sizing: border-box;
- outline: none;
- transition: all 0.2s ease;
- background-color: #fafafa;
- }
- input[type="text"]:focus {
- border-color: #a1a1aa;
- background-color: #ffffff;
- box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.05);
+
+ .progress-bar-fill {
+ transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
- #dropzone {
- border: 2px dashed #d4d4d8;
- border-radius: 10px;
- padding: 3rem 1.5rem;
- cursor: pointer;
+
+ .m3-input {
transition: all 0.2s ease;
- background-color: #fafafa;
- position: relative;
- }
- #dropzone.hover {
- border-color: var(--primary);
- background-color: #f4f4f5;
- }
- #dropzone p {
- margin: 0;
- color: #71717a;
- font-size: 0.95rem;
- line-height: 1.5;
- pointer-events: none;
- }
- #dropzone svg {
- width: 32px;
- height: 32px;
- color: #a1a1aa;
- margin-bottom: 12px;
- transition: color 0.2s ease;
+ box-shadow: 0 1px 0 0 #49454F; /* Bottom border effect */
}
- #dropzone.hover svg {
- color: var(--primary);
- }
- #result {
- margin-top: 1.5rem;
- font-size: 0.9rem;
- word-break: break-all;
- min-height: 24px;
- color: #52525b;
- }
- a {
- color: var(--text);
- text-decoration: none;
- font-weight: 500;
- background: #f4f4f5;
- padding: 0.5rem 0.85rem;
- border-radius: 6px;
- display: inline-block;
- margin-top: 0.75rem;
- border: 1px solid var(--border);
- transition: all 0.2s ease;
+ .m3-input:focus {
+ box-shadow: 0 2px 0 0 #6750A4; /* Active bottom border */
+ outline: none;
}
- a:hover {
- background: #e4e4e7;
+ @media (prefers-color-scheme: dark) {
+ .m3-input { box-shadow: 0 1px 0 0 #CAC4D0; }
+ .m3-input:focus { box-shadow: 0 2px 0 0 #D0BCFF; }
}
- .loader {
- display: inline-block;
- width: 16px;
- height: 16px;
- border: 2px solid rgba(0,0,0,0.1);
- border-radius: 50%;
- border-top-color: var(--primary);
- animation: spin 0.8s ease-in-out infinite;
- vertical-align: middle;
- margin-right: 8px;
+
+ /* Smooth fade ins */
+ .fade-in {
+ animation: fadeIn 0.3s ease-in forwards;
}
- @keyframes spin {
- to { transform: rotate(360deg); }
+ @keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
}
- .error { color: #dc2626; }
- .success { color: #16a34a; font-weight: 500; }
</style>
</head>
-<body>
+<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">
- <div class="container" id="ui-container" style="display: none;">
- <h1>Upload File</h1>
-
- <input type="text" id="custom-id" placeholder="Custom link name (optional)">
+ <!-- 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="dropzone">
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
- </svg>
- <p><strong>Click to upload</strong> or drag and drop<br>Max size: 4.5MB (Vercel Limit)</p>
- <input type="file" id="file-input" style="display: none;">
+ <!-- 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>
+ </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>
+ </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>
+ <input type="file" id="file-input" class="hidden">
</div>
- <div id="result"></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>
+ </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>
+ </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">
+ <span class="material-symbols-outlined">error</span>
+ <span id="error-text" class="text-sm font-medium mt-0.5"></span>
+ </div>
+
+ </div>
</div>
<script>
@@ -168,20 +166,36 @@
const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('file-input');
const customIdInput = document.getElementById('custom-id');
- const resultDiv = document.getElementById('result');
+
+ // 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 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
dropzone.addEventListener('click', () => fileInput.click());
dropzone.addEventListener('dragover', (e) => {
e.preventDefault();
- dropzone.classList.add('hover');
+ dropzone.classList.add('border-m3-primary', 'dark:border-m3-primaryDark', 'bg-m3-surface', 'dark:bg-[#2B2930]');
});
- dropzone.addEventListener('dragleave', () => dropzone.classList.remove('hover'));
+ dropzone.addEventListener('dragleave', () => {
+ dropzone.classList.remove('border-m3-primary', 'dark:border-m3-primaryDark', 'bg-m3-surface', 'dark:bg-[#2B2930]');
+ });
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
- dropzone.classList.remove('hover');
+ dropzone.classList.remove('border-m3-primary', 'dark:border-m3-primaryDark', 'bg-m3-surface', 'dark:bg-[#2B2930]');
if (e.dataTransfer.files.length) {
uploadFile(e.dataTransfer.files[0]);
}
@@ -193,13 +207,56 @@
}
});
- async function uploadFile(file) {
+ // Copy button logic
+ copyBtn.addEventListener('click', () => {
+ const linkToCopy = finalLink.href;
+
+ // Modern clipboard API
+ navigator.clipboard.writeText(linkToCopy).then(() => {
+ // Success visual feedback
+ copyIcon.textContent = 'check';
+ copyBtn.classList.add('bg-green-500', 'dark:bg-green-400');
+
+ setTimeout(() => {
+ copyIcon.textContent = 'content_copy';
+ copyBtn.classList.remove('bg-green-500', 'dark:bg-green-400');
+ }, 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();
+ document.execCommand("copy");
+ document.body.removeChild(tempInput);
+
+ copyIcon.textContent = 'check';
+ setTimeout(() => { copyIcon.textContent = 'content_copy'; }, 2000);
+ });
+ });
+
+ function resetUI() {
+ statusArea.classList.remove('hidden');
+ progressContainer.classList.add('hidden');
+ resultContainer.classList.add('hidden');
+ errorContainer.classList.add('hidden');
+ progressBar.style.width = '0%';
+ progressText.innerText = '0%';
+ }
+
+ function uploadFile(file) {
+ resetUI();
+
if (file.size > 4.5 * 1024 * 1024) {
- resultDiv.innerHTML = `<span class="error">Error: File exceeds 4.5MB Vercel limit.</span>`;
+ errorText.innerText = "Error: File exceeds 4.5MB Vercel limit.";
+ errorContainer.classList.remove('hidden');
return;
}
- resultDiv.innerHTML = '<span class="loader"></span> Uploading...';
+ // Show progress state
+ progressContainer.classList.remove('hidden');
+
const formData = new FormData();
formData.append('file', file);
@@ -208,29 +265,55 @@
formData.append('custom_id', customId);
}
- try {
- // Send to the Vercel serverless function
- const response = await fetch('/api/upload', {
- method: 'POST',
- body: formData
- });
-
- const data = await response.json();
+ // 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);
+ progressBar.style.width = percentComplete + '%';
+ progressText.innerText = percentComplete + '%';
+ }
+ };
+
+ xhr.onload = function() {
+ progressContainer.classList.add('hidden'); // Hide progress bar
- if (response.ok) {
- const finalUrl = `https://upload.amit.is-a.dev/?item=${data.item_id}`;
- resultDiv.innerHTML = `
- <span class="success">Upload Complete!</span><br>
- <a href="${finalUrl}" target="_blank">${finalUrl}</a>
- `;
- fileInput.value = '';
- customIdInput.value = '';
+ if (xhr.status >= 200 && xhr.status < 300) {
+ try {
+ const data = JSON.parse(xhr.responseText);
+ const finalUrl = `https://upload.amit.is-a.dev/?item=${data.item_id}`;
+
+ finalLink.href = finalUrl;
+ finalLink.innerText = finalUrl;
+ resultContainer.classList.remove('hidden');
+
+ // Reset inputs
+ fileInput.value = '';
+ customIdInput.value = '';
+ } catch(err) {
+ errorText.innerText = "Error: Invalid response from server.";
+ errorContainer.classList.remove('hidden');
+ }
} else {
- resultDiv.innerHTML = `<span class="error">Error: ${data.detail || 'Upload failed'}</span>`;
+ try {
+ const errData = JSON.parse(xhr.responseText);
+ errorText.innerText = `Error: ${errData.detail || 'Upload failed'}`;
+ } catch(err) {
+ errorText.innerText = "Error: Upload failed.";
+ }
+ errorContainer.classList.remove('hidden');
}
- } catch (error) {
- resultDiv.innerHTML = `<span class="error">System Error: Could not connect to server.</span>`;
- }
+ };
+
+ xhr.onerror = function() {
+ progressContainer.classList.add('hidden');
+ errorText.innerText = "System Error: Could not connect to server.";
+ errorContainer.classList.remove('hidden');
+ };
+
+ xhr.send(formData);
}
</script>
</body>