1- import { bangs } from "./bang" ;
1+ import { bangs } from "./bang" ;
22import "./global.css" ;
33
4+ // Default search engines for when no bang is used
5+ const DEFAULT_SEARCH_ENGINES = [
6+ { name : "Google" , value : "google" , url : "https://www.google.com/search?q={{{s}}}" } ,
7+ { name : "DuckDuckGo" , value : "duckduckgo" , url : "https://duckduckgo.com/?q={{{s}}}" } ,
8+ { name : "DuckDuckGo HTML" , value : "duckduckgo-html" , url : "https://html.duckduckgo.com/html/?q={{{s}}}" } ,
9+ { name : "Google No AI (udm=14)" , value : "google-no-ai" , url : "https://www.google.com/search?udm=14&q={{{s}}}" } ,
10+ { name : "Kagi" , value : "kagi" , url : "https://kagi.com/search?q={{{s}}}" } ,
11+ { name : "Swisscows" , value : "swisscows" , url : "https://swisscows.com/web?query={{{s}}}" } ,
12+ { name : "Startpage" , value : "startpage" , url : "https://www.startpage.com/sp/search?query={{{s}}}" } ,
13+ { name : "Qwant" , value : "qwant" , url : "https://www.qwant.com/?q={{{s}}}" } ,
14+ { name : "T3 Chat" , value : "t3chat" , url : "https://www.t3.chat/new?q={{{s}}}" }
15+ ] ;
16+
417function noSearchDefaultPageRender ( ) {
518 const app = document . querySelector < HTMLDivElement > ( "#app" ) ! ;
619 app . innerHTML = `
720 <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh;">
821 <div class="content-container">
9- <h1>BangBrave </h1>
10- <p>A fork of Und*ck to set the main search engine to Brave Search </a> </p>
22+ <h1>FastBangs </h1>
23+ <p>A fork of Und*ck to set the main search engine to Brave Search</p>
1124 <div class="url-container">
1225 <input
1326 type="text"
1427 class="url-input"
15- value="https://bangbrave .xdpxi.dev?q=%s"
28+ value="https://fastbangs .xdpxi.dev?q=%s"
1629 readonly
1730 />
1831 <button class="copy-button">
1932 <img src="/clipboard.svg" alt="Copy" />
2033 </button>
2134 </div>
35+ <div class="button-container">
36+ <button class="customize-button">
37+ <img src="/gear.svg" alt="Settings" />
38+ Customize
39+ </button>
40+ </div>
41+
42+ <div class="settings-panel">
43+ <div class="settings-content">
44+ <h3>Search Customization</h3>
45+ <div class="setting-group">
46+ <label for="default-search-select">Default search engine (when no bang is used):</label>
47+ <select id="default-search-select" class="search-select">
48+ ${ DEFAULT_SEARCH_ENGINES . map ( engine =>
49+ `<option value="${ engine . value } ">${ engine . name } </option>`
50+ ) . join ( '' ) }
51+ </select>
52+ <p class="setting-description">This search engine will be used when you search without a bang (like "!g" or "!gh")</p>
53+ </div>
54+ </div>
55+ </div>
2256 </div>
57+ <footer class="footer">
58+ <a href="https://t3.chat" target="_blank">t3.chat</a>
59+ •
60+ <a href="https://x.com/theo" target="_blank">theo</a>
61+ •
62+ <a href="https://github.com/t3dotgg/unduck" target="_blank">github</a>
63+ </footer>
2364 </div>
2465 ` ;
2566
2667 const copyButton = app . querySelector < HTMLButtonElement > ( ".copy-button" ) ! ;
2768 const copyIcon = copyButton . querySelector ( "img" ) ! ;
2869 const urlInput = app . querySelector < HTMLInputElement > ( ".url-input" ) ! ;
70+ const customizeButton = app . querySelector < HTMLButtonElement > ( ".customize-button" ) ! ;
71+ const settingsPanel = app . querySelector < HTMLDivElement > ( ".settings-panel" ) ! ;
72+ const defaultSearchSelect = app . querySelector < HTMLSelectElement > ( "#default-search-select" ) ! ;
73+
74+ // Load saved default search engine
75+ const savedDefaultSearch = localStorage . getItem ( "default-search-engine" ) || "google" ;
76+ defaultSearchSelect . value = savedDefaultSearch ;
2977
3078 copyButton . addEventListener ( "click" , async ( ) => {
3179 await navigator . clipboard . writeText ( urlInput . value ) ;
@@ -35,6 +83,16 @@ function noSearchDefaultPageRender() {
3583 copyIcon . src = "/clipboard.svg" ;
3684 } , 2000 ) ;
3785 } ) ;
86+
87+ customizeButton . addEventListener ( "click" , ( ) => {
88+ settingsPanel . classList . toggle ( "open" ) ;
89+ customizeButton . classList . toggle ( "active" ) ;
90+ } ) ;
91+
92+ defaultSearchSelect . addEventListener ( "change" , ( e ) => {
93+ const target = e . target as HTMLSelectElement ;
94+ localStorage . setItem ( "default-search-engine" , target . value ) ;
95+ } ) ;
3896}
3997
4098const LS_DEFAULT_BANG = localStorage . getItem ( "default-bang" ) ?? "brave" ;
@@ -49,27 +107,38 @@ function getBangredirectUrl() {
49107 }
50108
51109 const match = query . match ( / ! ( \S + ) / i) ;
52-
53110 const bangCandidate = match ?. [ 1 ] ?. toLowerCase ( ) ;
54- const selectedBang = bangs . find ( ( b ) => b . t === bangCandidate ) ?? defaultBang ;
55111
56- // Remove the first bang from the query
57- const cleanQuery = query . replace ( / ! \S + \s * / i, "" ) . trim ( ) ;
112+ if ( bangCandidate ) {
113+ // Bang detected - use existing logic
114+ const selectedBang = bangs . find ( ( b ) => b . t === bangCandidate ) ?? defaultBang ;
115+
116+ // Remove the first bang from the query
117+ const cleanQuery = query . replace ( / ! \S + \s * / i, "" ) . trim ( ) ;
118+
119+ // If the query is just `!gh`, use `github.com` instead of `github.com/search?q=`
120+ if ( cleanQuery === "" )
121+ return selectedBang ? `https://${ selectedBang . d } ` : null ;
122+
123+ // Format of the url is:
124+ // https://www.google.com/search?q={{{s}}}
125+ const searchUrl = selectedBang ?. u . replace (
126+ "{{{s}}}" ,
127+ // Replace %2F with / to fix formats like "!ghr+t3dotgg/unduck"
128+ encodeURIComponent ( cleanQuery ) . replace ( / % 2 F / g, "/" ) ,
129+ ) ;
130+ if ( ! searchUrl ) return null ;
58131
59- // If the query is just `!gh`, use `github.com` instead of `github.com/search?q=`
60- if ( cleanQuery === "" )
61- return selectedBang ? `https://${ selectedBang . d } ` : null ;
132+ return searchUrl ;
133+ } else {
134+ // No bang detected - use default search engine
135+ const defaultSearchEngine = localStorage . getItem ( "default-search-engine" ) || "brave" ;
136+ const engine = DEFAULT_SEARCH_ENGINES . find ( e => e . value === defaultSearchEngine ) ;
62137
63- // Format of the url is:
64- // https://www.google.com/search?q={{{s}}}
65- const searchUrl = selectedBang ?. u . replace (
66- "{{{s}}}" ,
67- // Replace %2F with / to fix formats like "!ghr+t3dotgg/unduck"
68- encodeURIComponent ( cleanQuery ) . replace ( / % 2 F / g, "/" ) ,
69- ) ;
70- if ( ! searchUrl ) return null ;
138+ if ( ! engine ) return null ;
71139
72- return searchUrl ;
140+ return engine . url . replace ( "{{{s}}}" , encodeURIComponent ( query ) ) ;
141+ }
73142}
74143
75144function doRedirect ( ) {
@@ -78,4 +147,4 @@ function doRedirect() {
78147 window . location . replace ( searchUrl ) ;
79148}
80149
81- doRedirect ( ) ;
150+ doRedirect ( ) ;
0 commit comments