A fast, local-first "redirection engine" for !bang users with a few extra features ^-^
1import { bangs } from "./bang"; 2import "./global.css"; 3 4const defaultBang = bangs.find((b) => b.t === "g"); 5 6function noSearchDefaultPageRender() { 7 const app = document.querySelector<HTMLDivElement>("#app")!; 8 app.innerHTML = ` 9 <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh;"> 10 <div class="content-container"> 11 <h1>Unduck</h1> 12 <p>DuckDuckGo's bang redirects are too slow. Add the following URL as a custom search engine to your browser. Enables <a href="https://duckduckgo.com/bang.html" target="_blank">all of DuckDuckGo's bangs.</a></p> 13 <div class="url-container"> 14 <input 15 type="text" 16 class="url-input" 17 value="https://unduck.link?q=%s" 18 readonly 19 /> 20 <button class="copy-button"> 21 <img src="/clipboard.svg" alt="Copy" /> 22 </button> 23 </div> 24 </div> 25 <footer class="footer"> 26 created by <a href="https://x.com/theo" target="_blank">theo</a> with help from <a href="https://t3.chat" target="_blank">t3.chat</a> 27 </footer> 28 </div> 29 `; 30 31 const copyButton = app.querySelector<HTMLButtonElement>(".copy-button")!; 32 const copyIcon = copyButton.querySelector("img")!; 33 const urlInput = app.querySelector<HTMLInputElement>(".url-input")!; 34 35 copyButton.addEventListener("click", async () => { 36 await navigator.clipboard.writeText(urlInput.value); 37 copyIcon.src = "/clipboard-check.svg"; 38 39 setTimeout(() => { 40 copyIcon.src = "/clipboard.svg"; 41 }, 2000); 42 }); 43} 44 45function getBangredirectUrl() { 46 const url = new URL(window.location.href); 47 const query = url.searchParams.get("q")?.trim() ?? ""; 48 if (!query) { 49 noSearchDefaultPageRender(); 50 return null; 51 } 52 53 const match = query.match(/!([a-z0-9]+)/i); 54 55 const bangCandidate = match?.[1]?.toLowerCase(); 56 const selectedBang = bangs.find((b) => b.t === bangCandidate) ?? defaultBang; 57 58 // Remove the first bang from the query 59 const cleanQuery = query.replace(/![a-z0-9]+\s*/i, "").trim(); 60 61 // Format of the url is: 62 // https://www.google.com/search?q={{{s}}} 63 const searchUrl = selectedBang?.u.replace( 64 "{{{s}}}", 65 encodeURIComponent(cleanQuery) 66 ); 67 if (!searchUrl) return null; 68 69 return searchUrl; 70} 71 72function doRedirect() { 73 const searchUrl = getBangredirectUrl(); 74 if (!searchUrl) return; 75 window.location.replace(searchUrl); 76} 77 78doRedirect();