Skip to content

Instantly share code, notes, and snippets.

@Taytay
Created March 17, 2026 12:30
Show Gist options
  • Select an option

  • Save Taytay/f90391b0ea1bb5a9a16fcd67c7625844 to your computer and use it in GitHub Desktop.

Select an option

Save Taytay/f90391b0ea1bb5a9a16fcd67c7625844 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Cleaner</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #1a1a2e;
color: #e0e0e0;
min-height: 100vh;
display: flex;
justify-content: center;
padding: 2rem 1rem;
}
.container {
width: 100%;
max-width: 720px;
}
h1 {
font-size: 1.4rem;
font-weight: 600;
margin-bottom: 0.25rem;
color: #fff;
}
.subtitle {
font-size: 0.85rem;
color: #888;
margin-bottom: 1.5rem;
}
label {
display: block;
font-size: 0.8rem;
font-weight: 500;
color: #aaa;
margin-bottom: 0.4rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
textarea {
width: 100%;
padding: 0.75rem;
font-family: "SF Mono", "Fira Code", "Fira Mono", Menlo, Consolas, monospace;
font-size: 0.85rem;
line-height: 1.5;
border: 1px solid #333;
border-radius: 6px;
background: #16213e;
color: #e0e0e0;
resize: vertical;
outline: none;
transition: border-color 0.15s;
}
textarea:focus { border-color: #4361ee; }
#input { height: 160px; margin-bottom: 1rem; }
#output {
height: 80px;
background: #0f3460;
border-color: #1a4a7a;
cursor: pointer;
}
.output-group { margin-bottom: 1rem; }
.stats {
font-size: 0.75rem;
color: #666;
margin-top: 0.4rem;
min-height: 1.1em;
}
.stats .removed { color: #e07a5f; }
.btn-row {
display: flex;
gap: 0.5rem;
margin-bottom: 1.25rem;
}
button {
padding: 0.5rem 1rem;
font-size: 0.8rem;
font-weight: 500;
border: 1px solid #333;
border-radius: 6px;
background: #16213e;
color: #e0e0e0;
cursor: pointer;
transition: background 0.15s, border-color 0.15s;
}
button:hover { background: #1a2a4e; border-color: #4361ee; }
button.primary {
background: #4361ee;
border-color: #4361ee;
color: #fff;
}
button.primary:hover { background: #3a56d4; }
.toast {
position: fixed;
bottom: 1.5rem;
left: 50%;
transform: translateX(-50%) translateY(1rem);
background: #4361ee;
color: #fff;
padding: 0.5rem 1.25rem;
border-radius: 6px;
font-size: 0.8rem;
opacity: 0;
transition: opacity 0.2s, transform 0.2s;
pointer-events: none;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
</style>
</head>
<body>
<div class="container">
<h1>URL Cleaner</h1>
<p class="subtitle">Paste a URL copied from Termius (or anywhere) to strip unprintable characters and line-wrapping whitespace.</p>
<label for="input">Dirty URL</label>
<textarea id="input" placeholder="Paste your URL here..." autofocus></textarea>
<div class="btn-row">
<button class="primary" onclick="copyOutput()">Copy Clean URL</button>
<button onclick="clearAll()">Clear</button>
</div>
<div class="output-group">
<label for="output">Clean URL</label>
<textarea id="output" readonly onclick="copyOutput()"></textarea>
<div class="stats" id="stats"></div>
</div>
</div>
<div class="toast" id="toast">Copied!</div>
<script>
const inputEl = document.getElementById('input');
const outputEl = document.getElementById('output');
const statsEl = document.getElementById('stats');
const toastEl = document.getElementById('toast');
function stripUrl(raw) {
let cleaned = '';
let removed = 0;
for (const ch of raw) {
const code = ch.codePointAt(0);
// Keep: ASCII printable (0x21-0x7E) excluding space,
// plus common URL-safe extended chars
// Drop: whitespace, control chars, zero-width chars, soft hyphens,
// BOM, direction marks, and other non-printable Unicode
if (
(code >= 0x21 && code <= 0x7E) // ASCII printable (no space)
) {
cleaned += ch;
} else if (
code > 0x7E
&& !isNonPrintable(code)
) {
// Non-ASCII printable (e.g. percent-encoded bytes already decoded,
// international domain names). Keep them.
cleaned += ch;
} else {
removed++;
}
}
return { cleaned, removed };
}
function isNonPrintable(code) {
return (
code <= 0x1F // C0 controls
|| (code >= 0x7F && code <= 0x9F) // C1 controls
|| code === 0x00AD // soft hyphen
|| code === 0x200B // zero-width space
|| code === 0x200C // zero-width non-joiner
|| code === 0x200D // zero-width joiner
|| code === 0x200E // LTR mark
|| code === 0x200F // RTL mark
|| code === 0x2028 // line separator
|| code === 0x2029 // paragraph separator
|| code === 0x202A // LTR embedding
|| code === 0x202B // RTL embedding
|| code === 0x202C // pop directional
|| code === 0x202D // LTR override
|| code === 0x202E // RTL override
|| code === 0x2060 // word joiner
|| code === 0x2061 // function application
|| code === 0x2062 // invisible times
|| code === 0x2063 // invisible separator
|| code === 0x2064 // invisible plus
|| code === 0xFEFF // BOM / zero-width no-break space
|| (code >= 0xFFF0 && code <= 0xFFFF) // specials block
|| (code >= 0xE0000 && code <= 0xE007F) // tags block
);
}
function update() {
const raw = inputEl.value;
if (!raw.trim()) {
outputEl.value = '';
statsEl.textContent = '';
return;
}
const { cleaned, removed } = stripUrl(raw);
outputEl.value = cleaned;
if (removed > 0) {
statsEl.innerHTML = `<span class="removed">${removed} character${removed !== 1 ? 's' : ''} removed</span> · ${cleaned.length} chars`;
} else {
statsEl.textContent = `URL is clean · ${cleaned.length} chars`;
}
}
function copyOutput() {
const text = outputEl.value;
if (!text) return;
navigator.clipboard.writeText(text).then(() => {
toastEl.classList.add('show');
setTimeout(() => toastEl.classList.remove('show'), 1200);
});
}
function clearAll() {
inputEl.value = '';
outputEl.value = '';
statsEl.textContent = '';
inputEl.focus();
}
inputEl.addEventListener('input', update);
// Also clean on paste with a slight delay to catch the pasted value
inputEl.addEventListener('paste', () => setTimeout(update, 0));
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment