feat: custom

This commit is contained in:
2026-05-02 16:57:21 -04:00
Verified
parent c1b821de0f
commit 57b928cb19
9 changed files with 139 additions and 26 deletions

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
node_modules
dist
.git
.cursor
.DS_Store
npm-debug.log*

19
Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
# syntax=docker/dockerfile:1
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginxinc/nginx-unprivileged:stable-alpine AS runtime
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -3,11 +3,13 @@
DuckDuckGo's bang redirects are too slow. Add the following URL as a custom search engine to your browser. Enables all of DuckDuckGo's bangs to work, but much faster. DuckDuckGo's bang redirects are too slow. Add the following URL as a custom search engine to your browser. Enables all of DuckDuckGo's bangs to work, but much faster.
``` ```
https://unduck.link?q=%s https://unduck.abunchofknowitalls.com?q=%s
``` ```
## How is it that much faster? ## How is it that much faster?
DuckDuckGo does their redirects server side. Their DNS is...not always great. Result is that it often takes ages. DuckDuckGo does their redirects server side. Their DNS is...not always great. Result is that it often takes ages.
I solved this by doing all of the work client side. Once you've went to https://unduck.link once, the JS is all cache'd and will never need to be downloaded again. Your device does the redirects, not me. I solved this by doing all of the work client side. Once you've went to https://unduck.abunchofknowitalls.com once, the JS is all cache'd and will never need to be downloaded again. Your device does the redirects, not me.
Forked from Theo Browne's [Unduck](https://github.com/T3-Content/unduck)

View File

@@ -18,17 +18,18 @@
as="style" as="style"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
/> />
<link
rel="search"
type="application/opensearchdescription+xml"
title="Unduck"
href="/opensearch.xml"
/>
<link <link
rel="stylesheet" rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
media="print" media="print"
onload="this.media = 'all'" onload="this.media = 'all'"
/> />
<script
defer
data-domain="unduck.link"
src="https://plausible.io/js/script.js"
></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Unduck</title> <title>Unduck</title>
<meta <meta

17
nginx.conf Normal file
View File

@@ -0,0 +1,17 @@
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location = /health {
access_log off;
add_header Content-Type text/plain;
return 200 "ok\n";
}
}

13
public/opensearch.xml Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription
xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/addons/opensearch/">
<ShortName>Unduck</ShortName>
<Description>A better default search engine (with bangs!)</Description>
<Tags>unduck bangs</Tags>
<Url type="text/html" method="GET" template="https://unduck.abunchofknowitalls.com/?q={searchTerms}"/>
<Image height="16" width="16" type="image/svg+xml">https://unduck.abunchofknowitalls.com/search.svg</Image>
<InputEncoding>UTF-8</InputEncoding>
<OutputEncoding>UTF-8</OutputEncoding>
<Query role="example" searchTerms="!g cats"/>
</OpenSearchDescription>

View File

@@ -8,16 +8,25 @@ export const bangs = [
s: "T3 Chat", s: "T3 Chat",
sc: "AI", sc: "AI",
t: "t3", t: "t3",
u: "https://www.t3.chat/new?q={{{s}}}", u: "https://t3.chat/new?model=gpt-5.4&q={{{s}}}",
}, },
{ {
c: "Tech", c: "AI",
d: "npmx", d: "t3.chat",
r: 147, r: 0,
s: "npmx", s: "T3 Chat",
sc: "Programming", sc: "AI",
t: "npmx", t: "ai",
u: "https://npmx.dev/search?q={{{s}}}", u: "https://t3.chat/new?model=gpt-5.4&q={{{s}}}",
},
{
c: "SearXNG",
d: "search.abunchofknowitalls.com",
r: 0,
s: "PrivSearch",
sc: "SearXNG",
t: "xng",
u: "https://search.abunchofknowitalls.com/?q={{{s}}}",
}, },
{ {
c: "Tech", c: "Tech",
@@ -3187,15 +3196,6 @@ export const bangs = [
t: "aitopics", t: "aitopics",
u: "https://aitopics.org/search?q={{{s}}}", u: "https://aitopics.org/search?q={{{s}}}",
}, },
{
c: "Research",
d: "www.duckduckgo.com",
r: 8351,
s: "Duck.ai",
sc: "Reference",
t: "ai",
u: "https://www.duckduckgo.com/?q={{{s}}}&ia=chat&bang=true ",
},
{ {
c: "News", c: "News",
d: "america.aljazeera.com", d: "america.aljazeera.com",

View File

@@ -167,6 +167,31 @@ textarea {
color: #888; color: #888;
} }
.setting {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
margin-top: 16px;
}
#default-browser-select {
font-size: 14px;
padding: 4px 8px;
border-radius: 4px;
border: 1px solid #ddd;
background: #f5f5f5;
color: #1a1a1a;
}
@media (prefers-color-scheme: dark) {
#default-browser-select {
border-color: #3d3d3d;
background-color: #191919;
color: #fff;
}
}
.footer, .footer,
.footer a { .footer a {
color: #999; color: #999;

View File

@@ -12,13 +12,17 @@ function noSearchDefaultPageRender() {
<input <input
type="text" type="text"
class="url-input" class="url-input"
value="https://unduck.link?q=%s" value="https://unduck.abunchofknowitalls.com?q=%s"
readonly readonly
/> />
<button class="copy-button"> <button class="copy-button">
<img src="/clipboard.svg" alt="Copy" /> <img src="/clipboard.svg" alt="Copy" />
</button> </button>
</div> </div>
<div class="setting">
<span>Default Browser:</span>
<select id="default-browser-select"></select>
</div>
</div> </div>
<footer class="footer"> <footer class="footer">
<a href="https://t3.chat" target="_blank">t3.chat</a> <a href="https://t3.chat" target="_blank">t3.chat</a>
@@ -42,6 +46,32 @@ function noSearchDefaultPageRender() {
copyIcon.src = "/clipboard.svg"; copyIcon.src = "/clipboard.svg";
}, 2000); }, 2000);
}); });
// Setup default browser select
const defaultBrowserSelect = document.querySelector<HTMLSelectElement>("#default-browser-select")!;
const defaultBangs = [
{ name: "Google", bang: "g" },
{ name: "T3 Chat", bang: "t3" },
{ name: "SearXNG", bang: "xng" },
{ name: "DuckDuckGo", bang: "ddg" },
{ name: "Bing", bang: "b" },
{ name: "Yahoo", bang: "y" },
{ name: "Brave", bang: "brave" },
];
for (const bang of defaultBangs) {
const option = document.createElement("option");
option.value = bang.bang;
option.textContent = bang.name;
if (bang.bang === LS_DEFAULT_BANG) {
option.selected = true;
}
defaultBrowserSelect.appendChild(option);
}
defaultBrowserSelect.addEventListener("change", () => {
localStorage.setItem("default-bang", defaultBrowserSelect.value);
});
} }
const LS_DEFAULT_BANG = localStorage.getItem("default-bang") ?? "g"; const LS_DEFAULT_BANG = localStorage.getItem("default-bang") ?? "g";