| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>URL Shortener</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet"> |
| <style> |
| :root { |
| --bg-primary: #000000; |
| --text-primary: #ffffff; |
| --text-secondary: #888888; |
| --accent: #FF3B30; |
| } |
| body { |
| font-family: 'Roboto', sans-serif; |
| background-color: var(--bg-primary); |
| color: var(--text-primary); |
| min-height: 100vh; |
| overflow-x: hidden; |
| } |
| .input-container { |
| transition: transform 0.3s ease-out; |
| } |
| .input-container.focused { |
| transform: translateY(-60vh); |
| } |
| .history-item { |
| transition: all 0.3s ease; |
| } |
| .history-item:hover { |
| background: rgba(255, 255, 255, 0.05); |
| } |
| @keyframes glowAnimation { |
| 0% { box-shadow: 0 0 0 rgba(255, 59, 48, 0); } |
| 50% { box-shadow: 0 0 20px rgba(255, 59, 48, 0.3); } |
| 100% { box-shadow: 0 0 0 rgba(255, 59, 48, 0); } |
| } |
| .success-animation { |
| animation: glowAnimation 1s ease-out; |
| } |
| #developerInfo { |
| transition: all 0.3s ease-out; |
| } |
| |
| #developerInfo > div { |
| transition: all 0.3s ease-out; |
| } |
| |
| .social-link { |
| transition: all 0.2s ease; |
| } |
| |
| .social-link:hover { |
| transform: translateY(-2px); |
| } |
| </style> |
| </head> |
| <body class="p-0 m-0"> |
| |
| <div class="fixed top-0 left-0 right-0 p-6 z-10"> |
| <div class="text-2xl font-light" id="time"></div> |
| <div class="text-sm text-gray-400" id="date"></div> |
| </div> |
|
|
| |
| <div class="pt-24 px-6 pb-32"> |
| <div class="text-xs uppercase tracking-wider text-gray-500 mb-4">Recent Links</div> |
| <div id="history" class="space-y-4"> |
| |
| </div> |
| </div> |
|
|
| |
| <div id="inputContainer" class="input-container fixed bottom-0 left-0 right-0 p-6 bg-black"> |
| <form id="urlForm" class="space-y-4"> |
| <div class="relative"> |
| <input type="text" id="url" |
| class="w-full bg-white/10 text-white px-4 py-3 rounded-2xl border-0 |
| focus:outline-none focus:ring-2 focus:ring-red-500/30 |
| placeholder-gray-500 text-base" |
| placeholder="Paste your URL here" |
| autocomplete="off"> |
| </div> |
| |
| <button type="submit" |
| class="w-full bg-white/10 text-white py-4 rounded-2xl font-medium |
| active:scale-[0.98] transition-all duration-300 |
| hover:bg-red-500/20 focus:outline-none"> |
| Shorten URL |
| </button> |
| </form> |
|
|
| |
| <div id="result" class="mt-4 hidden"> |
| <div class="bg-white/5 rounded-2xl p-4"> |
| <div class="flex items-center justify-between"> |
| <input type="text" id="shortUrl" readonly |
| class="flex-1 bg-transparent border-none focus:outline-none |
| text-white font-light"> |
| <button onclick="copyToClipboard()" |
| class="ml-2 p-2 rounded-xl bg-white/5 text-white/80 hover:bg-white/10 |
| transition-colors duration-200"> |
| Copy |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="developerInfo" class="fixed inset-0 z-50 flex items-center justify-center px-4 bg-black/80 backdrop-blur-sm"> |
| <div class="bg-white/10 rounded-3xl p-6 max-w-md w-full backdrop-blur-md border border-white/10"> |
| <div class="flex justify-between items-start mb-6"> |
| <div class="text-xl font-light">About Developer</div> |
| <button onclick="closeDeveloperInfo()" |
| class="text-white/60 hover:text-white transition-colors"> |
| <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> |
| </svg> |
| </button> |
| </div> |
| |
| <div class="space-y-4"> |
| <p class="text-gray-300 leading-relaxed"> |
| Aditya Devarshi is an AI Engineer passionate about creating innovative projects |
| and continuous learning. This URL shortener is one of his many projects |
| showcasing modern design principles and functionality. |
| </p> |
| |
| <div class="space-y-3 mt-6"> |
| <div class="text-sm text-gray-400 uppercase tracking-wider">Connect & Follow</div> |
| <div class="grid grid-cols-2 gap-3"> |
| <a href="https://www.adityadevarshi.online/" target="_blank" rel="noopener" |
| class="flex items-center gap-2 px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10 |
| transition-colors text-sm text-white/80 hover:text-white"> |
| <span>π</span> |
| <span>Portfolio</span> |
| </a> |
| <a href="https://www.linkedin.com/in/aditya-devarshi/" target="_blank" rel="noopener" |
| class="flex items-center gap-2 px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10 |
| transition-colors text-sm text-white/80 hover:text-white"> |
| <span>πΌ</span> |
| <span>LinkedIn</span> |
| </a> |
| <a href="https://github.com/devarshiadi/" target="_blank" rel="noopener" |
| class="flex items-center gap-2 px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10 |
| transition-colors text-sm text-white/80 hover:text-white"> |
| <span>π¦</span> |
| <span>GitHub</span> |
| </a> |
| <a href="https://medium.com/@devarshia5" target="_blank" rel="noopener" |
| class="flex items-center gap-2 px-4 py-3 rounded-xl bg-white/5 hover:bg-white/10 |
| transition-colors text-sm text-white/80 hover:text-white"> |
| <span>π</span> |
| <span>Medium</span> |
| </a> |
| </div> |
| </div> |
|
|
| <div class="mt-6 text-center"> |
| <button onclick="closeDeveloperInfo()" |
| class="px-6 py-2 rounded-xl bg-white/10 text-white/80 hover:bg-white/20 |
| transition-all duration-300 text-sm"> |
| Close |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let urlHistory = JSON.parse(localStorage.getItem('urlHistory') || '[]'); |
| updateHistoryDisplay(); |
| |
| function updateDateTime() { |
| const now = new Date(); |
| const timeString = now.toLocaleTimeString('en-US', { |
| hour12: false, |
| hour: '2-digit', |
| minute: '2-digit' |
| }); |
| const dateString = now.toLocaleDateString('en-US', { |
| weekday: 'short', |
| month: 'short', |
| day: 'numeric' |
| }); |
| |
| document.getElementById('time').textContent = timeString; |
| document.getElementById('date').textContent = dateString; |
| } |
| updateDateTime(); |
| setInterval(updateDateTime, 1000); |
| |
| const inputContainer = document.getElementById('inputContainer'); |
| const urlInput = document.getElementById('url'); |
| const historySection = document.querySelector('.pt-24'); |
| urlInput.addEventListener('focus', () => { |
| inputContainer.classList.add('focused'); |
| historySection.style.opacity = '0.3'; |
| navigator.vibrate(1); |
| }); |
| document.addEventListener('click', (e) => { |
| if (!inputContainer.contains(e.target)) { |
| inputContainer.classList.remove('focused'); |
| historySection.style.opacity = '1'; |
| } |
| }); |
| |
| function resetForm() { |
| urlInput.value = ''; |
| document.getElementById('result').classList.add('hidden'); |
| } |
| |
| document.getElementById('urlForm').addEventListener('submit', async (e) => { |
| e.preventDefault(); |
| const url = urlInput.value.trim(); |
| |
| if (!url) { |
| showNotification('Please enter a URL', 'error'); |
| return; |
| } |
| |
| if (!url.startsWith('http://') && !url.startsWith('https://')) { |
| showNotification('Please enter a valid URL starting with http:// or https://', 'error'); |
| return; |
| } |
| |
| const submitButton = e.target.querySelector('button'); |
| submitButton.disabled = true; |
| submitButton.innerHTML = '<span class="inline-block animate-pulse">Shortening...</span>'; |
| |
| try { |
| const response = await fetch('/shorten/', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ original_url: url }) |
| }); |
| |
| const data = await response.json(); |
| if (response.ok) { |
| document.getElementById('result').classList.remove('hidden'); |
| document.getElementById('shortUrl').value = data.shortened_url; |
| |
| |
| addToHistory(url, data.shortened_url); |
| |
| |
| inputContainer.classList.add('success-animation'); |
| setTimeout(() => inputContainer.classList.remove('success-animation'), 1000); |
| navigator.vibrate([50, 50, 50]); |
| |
| |
| setTimeout(resetForm, 3000); |
| } else { |
| showNotification(data.detail || 'Error shortening URL', 'error'); |
| } |
| } catch (error) { |
| showNotification('Network error occurred', 'error'); |
| } finally { |
| submitButton.disabled = false; |
| submitButton.textContent = 'Shorten URL'; |
| } |
| }); |
| function addToHistory(originalUrl, shortUrl) { |
| const historyItem = { |
| original: originalUrl, |
| shortened: shortUrl, |
| timestamp: new Date().toISOString() |
| }; |
| |
| urlHistory.unshift(historyItem); |
| if (urlHistory.length > 10) urlHistory.pop(); |
| |
| |
| localStorage.setItem('urlHistory', JSON.stringify(urlHistory)); |
| updateHistoryDisplay(); |
| } |
| function updateHistoryDisplay() { |
| const historyContainer = document.getElementById('history'); |
| if (urlHistory.length === 0) { |
| historyContainer.innerHTML = ` |
| <div class="text-center text-gray-500 py-8"> |
| No shortened URLs yet |
| </div> |
| `; |
| return; |
| } |
| historyContainer.innerHTML = urlHistory.map(item => ` |
| <div class="history-item rounded-xl p-4 bg-white/5 backdrop-blur-sm"> |
| <div class="flex justify-between items-start"> |
| <div class="text-sm font-light text-gray-400 truncate flex-1"> |
| ${item.original} |
| </div> |
| <div class="text-xs text-gray-500 ml-2"> |
| ${new Date(item.timestamp).toLocaleDateString()} |
| </div> |
| </div> |
| <div class="flex justify-between items-center mt-2"> |
| <div class="text-white truncate flex-1">${item.shortened}</div> |
| <div class="flex gap-2"> |
| <button onclick="copyUrl('${item.shortened}')" |
| class="text-xs text-white/60 hover:text-white px-3 py-1 rounded-lg bg-white/10 transition-colors"> |
| Copy |
| </button> |
| <button onclick="deleteHistoryItem('${item.timestamp}')" |
| class="text-xs text-red-400/60 hover:text-red-400 px-3 py-1 rounded-lg bg-red-500/10 transition-colors"> |
| Delete |
| </button> |
| </div> |
| </div> |
| </div> |
| `).join(''); |
| } |
| function deleteHistoryItem(timestamp) { |
| urlHistory = urlHistory.filter(item => item.timestamp !== timestamp); |
| localStorage.setItem('urlHistory', JSON.stringify(urlHistory)); |
| updateHistoryDisplay(); |
| navigator.vibrate(50); |
| } |
| function copyUrl(url) { |
| navigator.clipboard.writeText(url).then(() => { |
| showNotification('Copied to clipboard', 'success'); |
| navigator.vibrate(50); |
| }); |
| } |
| function copyToClipboard() { |
| const shortUrl = document.getElementById('shortUrl'); |
| navigator.clipboard.writeText(shortUrl.value).then(() => { |
| showNotification('Copied to clipboard', 'success'); |
| navigator.vibrate(50); |
| }); |
| } |
| function showNotification(message, type) { |
| const notification = document.createElement('div'); |
| notification.className = `fixed bottom-32 left-1/2 transform -translate-x-1/2 |
| px-6 py-3 rounded-2xl text-white text-center text-sm |
| ${type === 'error' ? 'bg-red-500/90' : 'bg-white/10'} |
| opacity-0 transition-opacity duration-300 z-50 |
| backdrop-blur-sm shadow-lg`; |
| notification.textContent = message; |
| |
| document.body.appendChild(notification); |
| requestAnimationFrame(() => notification.classList.add('opacity-100')); |
| setTimeout(() => { |
| notification.classList.remove('opacity-100'); |
| setTimeout(() => notification.remove(), 300); |
| }, 2000); |
| } |
| function closeDeveloperInfo() { |
| const popup = document.getElementById('developerInfo'); |
| popup.style.opacity = '0'; |
| popup.style.transform = 'scale(0.95)'; |
| setTimeout(() => { |
| popup.style.display = 'none'; |
| }, 300); |
| |
| localStorage.setItem('developerInfoShown', 'true'); |
| } |
| |
| window.addEventListener('DOMContentLoaded', () => { |
| const popup = document.getElementById('developerInfo'); |
| if (!localStorage.getItem('developerInfoShown')) { |
| popup.style.display = 'flex'; |
| popup.style.opacity = '0'; |
| popup.style.transform = 'scale(0.95)'; |
| |
| |
| setTimeout(() => { |
| popup.style.opacity = '1'; |
| popup.style.transform = 'scale(1)'; |
| }, 100); |
| } else { |
| popup.style.display = 'none'; |
| } |
| |
| console.log(`%c |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| β Aditya Devarshi β |
| β AI Engineer & Full Stack Developer β |
| β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£ |
| β β |
| β π Portfolio: https://www.adityadevarshi.online β |
| β πΌ LinkedIn: https://www.linkedin.com/in/aditya-devarshi β |
| β π¦ GitHub: https://github.com/devarshiadi β |
| β π Medium: https://medium.com/@devarshia5 β |
| β β |
| β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ£ |
| β β |
| β π Skills: AI/ML, Full Stack Development, Cloud Computing β |
| β π§ Contact: devarshia5@gmail.com β |
| β β |
| β π‘ "Building the future with AI and innovation" β |
| β β |
| ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| `, 'font-family: monospace; color: #00ff00; font-size: 12px;'); |
| console.log('%cWelcome to URL Shortener!', 'color: #FF3B30; font-size: 20px; font-weight: bold;'); |
| console.log('%cDeveloped with β€οΈ by Aditya Devarshi', 'color: #888; font-size: 14px;'); |
| |
| console.log('%cTech Stack:', 'color: #00ff00; font-size: 16px; font-weight: bold;'); |
| console.table({ |
| 'Frontend': ['HTML5', 'TailwindCSS', 'JavaScript'], |
| 'Backend': ['Python', 'FastAPI', 'SQLite'], |
| 'Deployment': ['Docker', 'Cloud Ready'], |
| 'Design': ['Nothing OS Inspired', 'Modern Minimal UI'] |
| }); |
| |
| console.log('%cContact Information:', 'color: #00ff00; font-size: 16px; font-weight: bold;'); |
| console.table({ |
| 'Email': 'devarshia5@gmail.com', |
| 'LinkedIn': 'https://www.linkedin.com/in/aditya-devarshi', |
| 'Portfolio': 'https://www.adityadevarshi.online', |
| 'GitHub': 'https://github.com/devarshiadi', |
| 'Medium': 'https://medium.com/@devarshia5' |
| }); |
| |
| console.log('%cProject Information:', 'color: #00ff00; font-size: 16px; font-weight: bold;'); |
| console.table({ |
| 'Name': 'Modern URL Shortener', |
| 'Version': '1.0.0', |
| 'License': 'MIT', |
| 'Repository': 'https://github.com/devarshiadi/url-shortener', |
| 'Design Inspiration': 'Nothing OS', |
| 'Features': [ |
| 'Modern UI/UX', |
| 'URL Shortening', |
| 'History Management', |
| 'Responsive Design', |
| 'Dark Mode', |
| 'Local Storage' |
| ] |
| }); |
| |
| console.log('%cπ Hey there, curious developer!', 'color: #FF3B30; font-size: 16px; font-weight: bold;'); |
| console.log('%cLooking for opportunities or collaboration? Feel free to reach out!', 'color: #888; font-size: 14px;'); |
| }); |
| </script> |
| </body> |
| </html> |