Compare commits

..

1 Commits

Author SHA1 Message Date
0427300abe Remove search svg 2025-02-27 16:50:18 -06:00
16 changed files with 1308 additions and 1797 deletions

View File

@@ -1,10 +0,0 @@
.env.example
.git
.gitignore
dist
docker/Dockerfile
docker/compose.yml
LICENSE
node_modules
npm-debug.log
README.md

View File

@@ -1,29 +0,0 @@
### Docker Environment Variables ###
DOMAIN="example.com"
### Vite Environment Variables ###
# All environment variables must be prefixed with VITE_ to be seen by client.
# Self-hosted Services URLs. Do not include the trailing slash.
VITE_BANG_URL="https://bang."
VITE_GITEA_URL="https://git."
VITE_SEARXNG_URL="https://search."
VITE_OPENWEBUI_URL="https://chat."
VITE_NEXTCLOUD_URL="https://apps.cloud."
VITE_PLEX_URL="https://plex."
VITE_OVERSEERR_URL="https://overseerr."
VITE_SONARR_URL="https://sonarr."
VITE_RADARR_URL="https://radarr."
VITE_LIDARR_URL="https://lidarr."
# Set default bang here. For example "g" would be Google.
# Google is the default if you don't set this.
# 's' is for your searxng instance, replacing startpage
VITE_DEFAULT_BANG="s"
# SPECIFIC MODELS FOR OPENWEBUI
# Local
VITE_LLAMA_MODEL="llama3.1" # !llama
VITE_DEEPSEEK_MODEL="deepseek-r1:8b" # !r1
# Remote
VITE_OPENAI_MODEL="gpt-5.2"
VITE_CLAUDE_MODEL="anthropic.claude-sonnet-4-5-20250929"

8
.gitignore vendored
View File

@@ -6,7 +6,6 @@ yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
public/opensearch.xml
node_modules
dist
@@ -23,10 +22,3 @@ dist-ssr
*.njsproj
*.sln
*.sw?
# ENV
docker/.env
!docker/.env.example
.env
.env*
!.env.example

View File

@@ -1,43 +1,14 @@
# 💣Bang
# Bang
*This is a fork of [unduck](https://unduck.link) by [Theo](https://github.com/t3dotgg/unduck).*
## Why fork?
This fork allows anyone to easily self host this service & add bangs for their own self-hosted websites. Bang comes pre-configured to work with
- Searxng *!s*
- Gitea *!tea*
- OpenWebUI *!ai*
- Plex *!plex*
- Sonarr *!tv*
- Radarr *!mv*
- Lidarr *!mp3*
- ~Overseerr *!ov*~ *Overseerr doesn't seem to work correctly*
*Note: We have replaced some of the default bangs from duckduckgo with our own bangs in order to have more simple bangs for the self-hosted websites.*
All you have to do is fill out the environment variables in the .env.example files located in the root directory & the docker directory & rename them to .env.
You can also easily add your own bangs by adding a new entry in the bangs.ts file.
## How to self host
1. Clone the repo & fill out the .env.example files in the root directory & the docker directory & rename them to .env.
- *Note: Our docker compose assumes you plan to select an external network.*
2. Run the bash script `host-bang` in the docker directory with the root directory of the project as an argument.
- *Note: You can also simply run it from the root or docker directory without an argument.*
### How to update the website
1. Run the bash script `update-bang` in the docker directory with the root directory of the project as an argument.
- *Note: You can also simply run it from the root or docker directory without an argument.*
## Theo's words
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.
### How is it that much faster?
```
https://bang.gbrown.org?q=%s
```
## 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.

View File

@@ -1,5 +1,5 @@
# Stage 1: Build the project
FROM node:20 AS builder
FROM node:18 AS builder
WORKDIR /app
# Copy package.json and pnpm-lock.yaml to the working directory
@@ -13,21 +13,20 @@ RUN pnpm install
COPY . .
# Build the project
#RUN pnpm run prebuild
RUN pnpm build
RUN pnpm run build
# Stage 2: Serve the app using the same version of Node
FROM node:20-alpine
FROM node:18-alpine
WORKDIR /app
# Install a simple http server
RUN npm install -g serve
# Copy built assets from the builder stage
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/dist ./
# Expose port 5000 for the server
EXPOSE 5000
# Start the server using the `serve` package
CMD ["serve", "-s", "dist", "-l", "5000"]
CMD ["serve", "-s", ".", "-l", "5000"]

View File

@@ -1,18 +0,0 @@
networks:
nginx-bridge:
external: true
services:
bang:
build:
context: ..
dockerfile: docker/Dockerfile
image: gib/bang:latest
container_name: bang
env_file: [../.env]
hostname: bang
domainname: bang.${DOMAIN}
networks: [nginx-bridge]
stdin_open: true
tty: true
restart: unless-stopped
command: ['serve', '-s', 'dist', '-l', '5000']

23
docker/docker-compose.yml Normal file
View File

@@ -0,0 +1,23 @@
version: '3.8'
services:
bang-web-server:
build:
context: ../ # point to the parent directory where package.json and source code reside
dockerfile: docker/Dockerfile # specific path to the Dockerfile
container_name: bang
domainname: bang.gbrown.org
hostname: bang
networks:
- node_apps
ports:
- "5000:5000"
tty: true
restart: unless-stopped
volumes:
- ../:/app # mount the parent directory to /app in the container
command: serve -s /app/dist -l 5000
networks:
node_apps:
external: true

View File

@@ -1,15 +0,0 @@
[Unit]
Description=Podman Compose: Bang
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
WorkingDirectory=/home/gib/Server/Search/Bang/docker
ExecStart=/usr/bin/podman compose up -d
ExecStop=/usr/bin/podman compose down
RemainAfterExit=yes
TimeoutStopSec=60
[Install]
WantedBy=multi-user.target

View File

@@ -24,12 +24,6 @@
media="print"
onload="this.media='all'"
/>
<link
rel="search"
type="application/opensearchdescription+xml"
title="Bang!"
href="/opensearch.xml"
/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bang!</title>
<meta

View File

@@ -1,25 +1,21 @@
{
"name": "bang",
"name": "unduck",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"generate-opensearch": "ts-node scripts/generateOpenSearch.ts",
"build": "tsc && vite build",
"preview": "vite preview"
},
"devDependencies": {
"@types/node": "^22.19.3",
"dotenv": "^17.2.3",
"ts-node": "^10.9.2",
"typescript": "~5.7.3",
"vite": "^6.4.1"
"typescript": "~5.7.2",
"vite": "^6.1.0"
},
"dependencies": {
"vite-plugin-pwa": "^0.21.2"
"vite-plugin-pwa": "^0.21.1"
},
"packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402",
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b",
"pnpm": {
"onlyBuiltDependencies": [
"esbuild"

2475
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Bang!</ShortName>
<Description>A better default search engine with bangs</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16" type="image/svg+xml">https://bang.gbrown.org/bang.svg</Image>
<Url type="text/html" method="get" template="https://bang.gbrown.org?q={searchTerms}"/>
<Url type="application/opensearchdescription+xml" rel="self" template="https://bang.gbrown.org/opensearch.xml"/>
<moz:SearchForm xmlns:moz="http://www.mozilla.org/2006/browser/search/">https://bang.gbrown.org</moz:SearchForm>
</OpenSearchDescription>

View File

@@ -1,38 +0,0 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import 'dotenv/config';
// Get the current file's directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
if (!process.env.VITE_BANG_URL) {
console.error('VITE_BANG_URL environment variable must be set.');
}
// Get the Bang URL from environment or use a default for local development
const bangUrl = process.env.VITE_BANG_URL;
// Create the OpenSearch XML content
const openSearchXml = `<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>Bang!</ShortName>
<Description>A better default search engine with bangs</Description>
<InputEncoding>UTF-8</InputEncoding>
<Image width="16" height="16" type="image/svg+xml">${bangUrl}/bang.svg</Image>
<Url type="text/html" method="get" template="${bangUrl}?q={searchTerms}"/>
<Url type="application/opensearchdescription+xml" rel="self" template="${bangUrl}/opensearch.xml"/>
<moz:SearchForm xmlns:moz="http://www.mozilla.org/2006/browser/search/">${bangUrl}</moz:SearchForm>
</OpenSearchDescription>`;
// Ensure the public directory exists
const publicDir = path.resolve(__dirname, '../public');
if (!fs.existsSync(publicDir)) {
fs.mkdirSync(publicDir, { recursive: true });
}
// Write the OpenSearch XML file
fs.writeFileSync(path.join(publicDir, 'opensearch.xml'), openSearchXml);
console.log('OpenSearch XML file generated successfully!');

View File

@@ -1,190 +1,113 @@
// This file was (mostly) ripped from https://duckduckgo.com/bang.js
const SEARXNG_URL = import.meta.env.VITE_SEARXNG_URL as string;
const GITEA_URL = import.meta.env.VITE_GITEA_URL as string;
const OPENWEBUI_URL = import.meta.env.VITE_OPENWEBUI_URL as string;
const PLEX_URL = import.meta.env.VITE_PLEX_URL as string;
const OVERSEERR_URL = import.meta.env.VITE_OVERSEERR_URL as string;
const SONARR_URL = import.meta.env.VITE_SONARR_URL as string;
const RADARR_URL = import.meta.env.VITE_RADARR_URL as string;
const LIDARR_URL = import.meta.env.VITE_LIDARR_URL as string;
const LLAMA_MODEL = import.meta.env.VITE_LLAMA_MODEL.replace(":", "%3A") as string;
const DEEPSEEK_MODEL = import.meta.env.VITE_DEEPSEEK_MODEL.replace(":", "%3A") as string;
const OPENAI_MODEL = import.meta.env.VITE_OPENAI_MODEL as string;
const CLAUDE_MODEL = import.meta.env.VITE_CLAUDE_MODEL.replace("/", "%2F") as string;
export const bangs = [
{
c: "Online Services",
d: SEARXNG_URL,
d: "search.gibbyb.com",
r: 0,
s: "Searxng",
s: "GibbyB",
sc: "Search",
t: "s",
u: `${SEARXNG_URL}/?q={{{s}}}`,
t: "sg",
u: "https://search.gibbyb.com/?q={{{s}}}",
},
{
c: "Tech",
d: GITEA_URL,
d: "git.gibbyb.com",
r: 0,
s: "Gitea",
sc: "Git",
sc: "Cryptocurrency",
t: "tea",
u: `${GITEA_URL}/?repo-search-query={{{s}}}`,
},
{
c: "Entertainment",
d: PLEX_URL,
r: 0,
s: "Plex",
sc: "Movies",
t: "plex",
u: `${PLEX_URL}/web/index.html#!/search?pivot=top&query={{{s}}}`,
},
{
c: "Entertainment",
d: OVERSEERR_URL,
r: 0,
s: "Overseerr",
sc: "Movies",
t: "ov",
u: `${OVERSEERR_URL}/search?query={{{s}}}`,
},
{
c: "Entertainment",
d: LIDARR_URL,
r: 0,
s: "Lidarr",
sc: "Music",
t: "mp3",
u: `${LIDARR_URL}/add/new?term={{{s}}}`,
},
{
c: "Entertainment",
d: SONARR_URL,
r: 0,
s: "Sonarr",
sc: "TV",
t: "tv",
u: `${SONARR_URL}/add/new?term={{{s}}}`,
},
{
c: "Entertainment",
d: RADARR_URL,
r: 0,
s: "Radarr",
sc: "Movies",
t: "mv",
u: `${RADARR_URL}/add/new?term={{{s}}}`,
u: "https://git.gibbyb.com/?repo-search-query={{{s}}}",
},
{
c: "AI",
d: OPENWEBUI_URL,
d: "chat.gibbyb.com",
r: 0,
s: "OpenWebUI",
s: "GibbyB",
sc: "AI",
t: "ai",
u: `${OPENWEBUI_URL}/?q={{{s}}}`,
u: "https://chat.gibbyb.com/?q={{{s}}}",
},
{
c: "AI",
d: OPENWEBUI_URL,
d: "chat.gibbyb.com",
r: 0,
s: "OpenWebUI",
sc: "AI",
t: "c",
u: `${OPENWEBUI_URL}/?q={{{s}}}`,
},
{
c: "AI",
d: OPENWEBUI_URL,
r: 0,
s: "OpenWebUI",
sc: "AI",
t: "llama",
u: `${OPENWEBUI_URL}/?models=${LLAMA_MODEL}&q={{{s}}}`,
},
{
c: "AI",
d: OPENWEBUI_URL,
r: 0,
s: "OpenWebUI",
sc: "AI",
t: "r1",
u: `${OPENWEBUI_URL}/?models=${DEEPSEEK_MODEL}&q={{{s}}}`,
},
{
c: "AI",
d: OPENWEBUI_URL,
r: 0,
s: "OpenWebUI",
sc: "AI",
t: "r1-7b",
u: `${OPENWEBUI_URL}/?models=deepseek-r1%3A7b&q={{{s}}}`,
},
{
c: "AI",
d: OPENWEBUI_URL,
r: 0,
s: "OpenWebUI",
sc: "AI",
t: "r1-32b",
u: `${OPENWEBUI_URL}/?models=deepseek-r1%3A32b&q={{{s}}}`,
},
{
c: "AI",
d: OPENWEBUI_URL,
r: 0,
s: "OpenWebUI",
s: "GibbyB",
sc: "AI",
t: "claude",
u: `${OPENWEBUI_URL}/?models=${CLAUDE_MODEL}&q={{{s}}}`,
u: "https://chat.gibbyb.com/?models=anthropic%2Fclaude-3.5-sonnet&q={{{s}}}",
},
{
c: "AI",
d: OPENWEBUI_URL,
d: "chat.gibbyb.com",
r: 0,
s: "OpenWebUI",
s: "GibbyB",
sc: "AI",
t: "openai",
u: `${OPENWEBUI_URL}/?models=${OPENAI_MODEL}&q={{{s}}}`,
t: "llama",
u: "https://chat.gibbyb.com/?models=llama3.1&q={{{s}}}",
},
{
c: "AI",
d: OPENWEBUI_URL,
d: "chat.gibbyb.com",
r: 0,
s: "OpenWebUI",
s: "GibbyB",
sc: "AI",
t: "r17b",
u: "https://chat.gibbyb.com/?models=deepseek-r1%3A7b&q={{{s}}}",
},
{
c: "AI",
d: "chat.gibbyb.com",
r: 0,
s: "GibbyB",
sc: "AI",
t: "r1",
u: "https://chat.gibbyb.com/?models=deepseek-r1%3A8b&q={{{s}}}",
},
{
c: "AI",
d: "chat.gibbyb.com",
r: 0,
s: "GibbyB",
sc: "AI",
t: "r132b",
u: "https://chat.gibbyb.com/?models=deepseek-r1%3A32b&q={{{s}}}",
},
{
c: "AI",
d: "chat.gibbyb.com",
r: 0,
s: "GibbyB",
sc: "AI",
t: "o3-mini",
u: `${OPENWEBUI_URL}/?models=o3-mini&q={{{s}}}`,
u: "https://chat.gibbyb.com/?models=o3-mini&q={{{s}}}",
},
{
c: "AI",
d: OPENWEBUI_URL,
d: "chat.gibbyb.com",
r: 0,
s: "OpenWebUI",
s: "GibbyB",
sc: "AI",
t: "o1",
u: `${OPENWEBUI_URL}/?models=o1&q={{{s}}}`,
u: "https://chat.gibbyb.com/?models=o1&q={{{s}}}",
},
{
c: "AI",
d: OPENWEBUI_URL,
d: "chat.gibbyb.com",
r: 0,
s: "OpenWebUI",
s: "GibbyB",
sc: "AI",
t: "4o-mini",
u: `${OPENWEBUI_URL}/?models=gpt-4o-mini&q={{{s}}}`,
u: "https://chat.gibbyb.com/?models=gpt-4o-mini&q={{{s}}}",
},
{
c: "AI",
d: OPENWEBUI_URL,
d: "chat.gibbyb.com",
r: 0,
s: "OpenWebUI",
s: "GibbyB",
sc: "AI",
t: "4o",
u: `${OPENWEBUI_URL}/?models=gpt-4o&q={{{s}}}`,
u: "https://chat.gibbyb.com/?models=gpt-4o&q={{{s}}}",
},
{
c: "AI",
@@ -71473,7 +71396,7 @@ export const bangs = [
r: 10,
s: "Myvideo",
sc: "Video",
t: "myvideo",
t: "mv",
u: "http://www.myvideo.de/Videos_A-Z?searchWord={{{s}}}",
},
{
@@ -73586,8 +73509,8 @@ export const bangs = [
r: 0,
s: "Nextcloud App Store",
sc: "Sysadmin",
t: "cloudapp",
u: "https://apps.nextcloud.com/search/?q={{{s}}}",
t: "nextcloudapp",
u: "https://apps.nextcloud.com/?search={{{s}}}",
},
{
c: "Entertainment",
@@ -78912,7 +78835,7 @@ export const bangs = [
r: 0,
s: "StackOverflow",
sc: "Programming",
t: "ovfl",
t: "ov",
u: "http://stackoverflow.com/search?q={{{s}}}",
},
{
@@ -98492,7 +98415,7 @@ export const bangs = [
r: 12469,
s: "startpage.com",
sc: "Search",
t: "startpage",
t: "s",
u: "http://startpage.com/do/metasearch.pl?query={{{s}}}",
},
{
@@ -106865,6 +106788,15 @@ export const bangs = [
t: "tva",
u: "https://tweakers.net/aanbod/zoeken/?keyword={{{s}}}",
},
{
c: "Entertainment",
d: "www.tv.com",
r: 0,
s: "TV.com",
sc: "TV",
t: "tvcom",
u: "http://www.tv.com/search?q={{{s}}}",
},
{
c: "Entertainment",
d: "www.thetvdb.com",
@@ -107024,8 +106956,8 @@ export const bangs = [
r: 31,
s: "tv.com",
sc: "TV",
t: "tvcom",
u: "http://www.tv.com/search?q={{{s}}}",
t: "tv",
u: "http://www.tv.com/search?q= {{{s}}}",
},
{
c: "Shopping",

View File

@@ -93,25 +93,17 @@ textarea {
}
/* --- Container layout unchanged --- */
.main-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 20px 0px;
}
.content-container {
max-width: 36rem;
text-align: center;
padding: 0 8px;
}
.url-container {
display: flex;
align-items: center;
gap: 8px;
margin-top: 16px;
}
.content-container {
max-width: 36rem;
text-align: center;
padding: 0 8px;
}
/* --- URL input element styling adjustments --- */
.url-input {
@@ -125,50 +117,33 @@ textarea {
}
/* --- Copy-button style adjustments --- */
.copy-button, .copy-firefox, .copy-chrome {
padding: 6px;
.copy-button {
padding: 8px;
color: var(--accent-color); /* Changed: Accent color instead of gray */
border-radius: 4px;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.copy-button:hover, .copy-firefox:hover, .copy-chrome:hover {
.copy-button:hover {
background: var(--hover-bg-color); /* Changed: Hover bg darker */
border-radius: 8px;
}
.copy-button:active, .copy-firefox:active, .copy-chrome:active {
.copy-button:active {
background: var(--active-bg-color); /* Changed: Active state even darker */
}
.copy-button img, .copy-firefox img, .copy-chrome img {
.copy-button img {
width: 20px;
height: 20px;
}
/* Copy feedback unchanged (Green stands out in dark mode as well) */
.copy-button.copied, .copy-firefox.copied, .copy-chrome.copied {
.copy-button.copied {
background: var(--success-color);
color: white; /* Ensures contrast */
}
.copy-textbox, .firefox-textbox, .chrome-textbox {
padding: 6px 10px;
border: 1px solid var(--input-border-color);
border-radius: 4px;
background: var(--input-bg-color);
color: var(--text-color);
font-size: 14px;
width: 220px;
cursor: pointer;
}
.copy-textbox:focus, .firefox-textbox:focus, .chrome-textbox:focus {
outline: none;
border-color: var(--accent-color);
}
.settings-title {
margin-top: 36px;
margin-bottom: 16px;
margin-bottom: 24px;
font-size: 20px;
}
.settings-links-container {
@@ -176,49 +151,5 @@ textarea {
flex-direction: row;
justify-content: center;
align-items: center;
gap: 24px;
}
.browser-link-container {
display: flex;
align-items: center;
gap: 8px;
}
.browser-instruction {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: normal;
}
.form-table {
font-size: 14px;
margin: 16px auto;
text-align: left;
}
.form-table td {
padding: 8px 6px;
border: 1px solid var(--input-border-color);
}
.form-table td:first-child {
background-color: var(--input-bg-color);
width: 100px;
}
/* --- Mobile --- */
@media (max-width: 500px) {
.settings-links-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 32px;
}
.main-container {
justify-content: flex-start;
padding: 40px;
}
}

View File

@@ -4,7 +4,7 @@ import "./global.css";
function noSearchDefaultPageRender() {
const app = document.querySelector<HTMLDivElement>("#app")!;
app.innerHTML = `
<main class="main-container">
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh;">
<div class="content-container">
<h1 class="bang-title">💣 Bang!</h1>
<p>
@@ -18,103 +18,53 @@ function noSearchDefaultPageRender() {
<input
type="text"
class="url-input"
value="${import.meta.env.VITE_BANG_URL}?q=%s"
value="https://bang.gbrown.org?q=%s"
readonly
/>
<button class="copy-button">
<img src="/clipboard.svg" alt="Copy" />
</button>
</div>
<h3 class="settings-title">How to add Bang! Search Engine</h3>
<p style="margin-bottom: 8px; font-size: 14px;">
Below are links to the search engine settings in your browser.
<br />
Copy the URL for the browser engine you are using & paste it into a new tab.
<br />
From there you should see an option to add a search engine. Copy the link above
& fill out the form as follows.
</p>
<table class="form-table">
<tr>
<td><b>Name:</b></td>
<td>Bang!</td>
</tr>
<tr>
<td><b>Engine URL:</b></td>
<td>${import.meta.env.VITE_BANG_URL}?q=%s</td>
</tr>
<tr>
<td><b>Alias:</b></td>
<td>@bang</td>
</tr>
</table>
<h3 class="settings-title">Search Settings Links</h3>
<div class="settings-links-container">
<div class="browser-link-container">
<img src="/firefox.svg" alt="Firefox" width="24" />
<input
type="text"
readonly value="about:preferences#search"
class="firefox-textbox"
title="Click to copy Firefox settings URL"
<h3>
<a href="about:preferences#search" target="_blank">
<img
src="/firefox.svg"
alt="Firefox"
width="30"
/>
<button class="copy-firefox">
<img src="/clipboard.svg" alt="Copy" />
</button>
</div>
<div class="browser-link-container">
<img src="/chrome.svg" alt="Chrome" width="24" />
<input
type="text"
readonly value="chrome://settings/searchEngines"
class="chrome-textbox"
title="Click to copy Chrome settings URL"
</a>
</h3>
<h3>
<a href="chrome://settings/searchEngines" target="_blank">
<img
src="/chrome.svg"
alt="Chrome"
width="30"
/>
<button class="copy-chrome">
<img src="/clipboard.svg" alt="Copy" />
</button>
</a>
</h3>
</div>
</div>
</div>
</main>
`;
const copyButton = app.querySelector<HTMLButtonElement>(".copy-button")!;
const copyIcon = copyButton.querySelector("img")!;
const urlInput = app.querySelector<HTMLInputElement>(".url-input")!;
const copyFirefox = app.querySelector<HTMLInputElement>(".copy-firefox")!;
const copyFirefoxIcon = copyFirefox.querySelector("img")!;
const firefoxInput = app.querySelector<HTMLInputElement>(".firefox-textbox")!;
const copyChrome = app.querySelector<HTMLInputElement>(".copy-chrome")!;
const copyChromeIcon = copyChrome.querySelector("img")!;
const chromeInput = app.querySelector<HTMLInputElement>(".chrome-textbox")!;
copyButton.addEventListener("click", async () => {
await navigator.clipboard.writeText(urlInput.value);
copyIcon.src = "/clipboard-check.svg";
setTimeout(() => {
copyIcon.src = "/clipboard.svg";
}, 2000);
});
copyFirefox.addEventListener("click", async () => {
await navigator.clipboard.writeText(firefoxInput.value);
copyFirefoxIcon.src = "/clipboard-check.svg";
setTimeout(() => {
copyFirefoxIcon.src = "/clipboard.svg";
}, 2000);
});
copyChrome.addEventListener("click", async () => {
await navigator.clipboard.writeText(chromeInput.value);
copyChromeIcon.src = "/clipboard-check.svg";
setTimeout(() => {
copyChromeIcon.src = "/clipboard.svg";
}, 2000);
});
}
const envDefaultBang = import.meta.env.VITE_DEFAULT_BANG ?? "g";
const LS_DEFAULT_BANG = localStorage.getItem("default-bang") ?? envDefaultBang;
const LS_DEFAULT_BANG = localStorage.getItem("default-bang") ?? "sg";
const defaultBang = bangs.find((b) => b.t === LS_DEFAULT_BANG);
function getBangredirectUrl() {