Files
4ev.link/index.html

62 lines
3.2 KiB
HTML

<!doctype html><html lang=en>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>4ev.link — Shorten URLs</title>
<meta name=color-scheme content=light>
<link rel="icon" href="data:,">
<style>
*{box-sizing:border-box}html,body{height:100%}body{margin:0;font:16px/1.35 system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;background:#fff;color:#111;-webkit-font-smoothing:antialiased}
a{color:inherit}
header{padding:16px}
.brand{font-weight:700;text-decoration:none}
main{max-width:720px;margin:14vh auto 0;padding:0 16px}
form{display:flex;gap:8px}
input{flex:1;padding:14px 12px;border:1px solid #e5e7eb;border-radius:0;outline-offset:2px}
button{padding:0 14px;border:1px solid #e5e7eb;border-radius:0;background:#111;color:#fff;display:inline-grid;place-items:center;min-width:48px}
button:disabled{opacity:.7;cursor:not-allowed}
i[data-lucide]{width:22px;height:22px}
#o{margin-top:12px}
.msg{margin-top:8px;color:#666;display:flex;gap:8px;align-items:center}
.err{color:#b00020}
.result{display:flex;align-items:center;gap:8px;margin-top:12px;flex-wrap:wrap}
.tag{padding:8px 10px;border:1px solid #e5e7eb;background:#fafafa}
.copy{border:1px solid #e5e7eb;background:#fff;color:#111;padding:8px 10px}
.spin{animation:sp 1s linear infinite}@keyframes sp{to{transform:rotate(1turn)}}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
</style>
</head>
<body>
<header><a class=brand href="/">4ev.link</a></header>
<main>
<form id=f autocomplete=off>
<input id=i type=url inputmode=url placeholder="Paste a long URL" aria-label="Long URL" required>
<button id=b type=submit title=Shorten aria-label=Shorten><i data-lucide=link-2></i><span class=sr-only>Shorten</span></button>
</form>
<output id=o role=status aria-live=polite></output>
</main>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
(()=>{const $=q=>document.querySelector(q),f=$('#f'),I=$('#i'),B=$('#b'),O=$('#o'),ico=()=>{try{lucide.createIcons()}catch{}},set=(h)=>{O.innerHTML=h,ico()}
ico();
f.onsubmit=async e=>{
e.preventDefault();
let v=I.value.trim(); if(!v)return;
if(!/^https?:\/\//i.test(v))v='https://'+v;
B.disabled=1;const i=B.querySelector('i');i.setAttribute('data-lucide','loader-2');i.classList.add('spin');ico();
try{
const r=await fetch('/api/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url:v})});
if(!r.ok)throw 0;const j=await r.json(),u=j.shortUrl;
set(`<div class=result><a class=tag href="${u}" target=_blank rel=noopener>${u}</a><button id=c class=copy type=button title=Copy><i data-lucide=copy></i></button></div><p class=msg>Tap to open, or copy to share.</p>`);
$('#c').onclick=async()=>{try{await navigator.clipboard.writeText(u);const k=$('#c i');k.setAttribute('data-lucide','check');ico();setTimeout(()=>{k.setAttribute('data-lucide','copy');ico()},1200)}catch{}}
}catch{
set(`<p class="msg err"><i data-lucide=alert-circle></i>Invalid URL</p>`)
}finally{
B.disabled=0;i.classList.remove('spin');i.setAttribute('data-lucide','link-2');ico()
}
};
})();
</script>
</body>
</html>