From 4627f4eb29f235602961b8767451366f2a7e6f7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D1=81=D0=B8=D0=BD=D0=B5=D1=87=D0=BA=D0=B0=20=E2=99=A1?=
<63897347+ktnk-dev@users.noreply.github.com>
Date: Fri, 15 May 2026 23:08:54 +0300
Subject: [PATCH] better settings support
---
src/css/global.css | 19 ++--
src/css/settings.css | 162 ++++++++++++++++++++++++++++----
src/js/builtin.js | 4 -
src/js/main.js | 6 +-
src/js/settings.js | 214 ++++++++++++++++++++++++++++++++-----------
src/js/storage.js | 48 ++++++----
6 files changed, 346 insertions(+), 107 deletions(-)
diff --git a/src/css/global.css b/src/css/global.css
index 022f59d..cb61818 100644
--- a/src/css/global.css
+++ b/src/css/global.css
@@ -1,21 +1,16 @@
-:root {
- /* --base_bg_color: 10;
+
+body {
+ --base_bg_color: 10;
--base_bg_color_range: 10;
--base_bg_saturation: 10%;
--color_text_white: #fff;
--color_text_gray: #999;
- --surface-000: #000;
- --surface-100: #222;
- --surface-200: #333;
- --surface-300: #444;
+ --surface-000: var(--s000);
+ --surface-100: var(--s100);
+ --surface-200: color-mix(in srgb, var(--s200), transparent 95%);
+ --surface-300: color-mix(in srgb, var(--s300), transparent 85%);
- --sans: 'Nunito';
- --serif: 'Playfair Display';
- --monospace: monospace; */
-}
-
-body {
display: flex;
height: 100vh;
width: 100vw;
diff --git a/src/css/settings.css b/src/css/settings.css
index b6fd470..bf7f612 100644
--- a/src/css/settings.css
+++ b/src/css/settings.css
@@ -20,7 +20,7 @@
display: flex;
align-items: center;
justify-content: center;
- background: var(--surface-100);
+ background: var(--surface-200);
padding: 5px 10px;
border-radius: 10px 10px 0 0;
border: solid 1px var(--surface-300);
@@ -28,7 +28,7 @@
user-select: none;
}
>div:hover {
- background: var(--surface-200);
+ background: var(--surface-300);
}
>div:active {
background: var(--surface-300);
@@ -45,29 +45,155 @@
}
}
-#settingsui {
- z-index: 12;
+#settings.ov {
+ transform: translateX(34vw);
+}
+#settings {
+ overflow-y: auto;
+ overflow-x: hidden;
+ z-index: 1001;
position: fixed;
- display: flex;
- left: 5px;
- bottom: 50px;
- background: rgba(255,255,255,.1);
+ right: 0;
+ top: 0;
display: flex;
flex-direction: column;
- padding: 10px;
- border-radius: 10px;
- border: solid 1px rgba(255,255,255,.1);
+ padding: 20px;
+ gap: 10px;
+ height: calc(100dvh - 40px);
+ width: 30vw;
+ /* background: var(--surface-100); */
+ /* border-left: solid 1px var(--surface-300); */
+ border: none;
+ scrollbar-color: var(--surface-300) transparent;
- h2 {
+ >* {
+ transition: transform .3s, opacity .3s;
+ }
+ >.ov {
+ opacity: 0;
+ transform: translateX(34vw);
+ }
+ >.back {
+ margin-bottom: 10px;
+ cursor: pointer;
+ display: flex;
+ gap: 10px;
+ align-items: center;
+ h2 {
+ font-family: var(--serif);
+ font-weight: normal;
+ font-size: 25px;
+ }
+ span {
+ border-radius: 100px;
+ font-size: 25px !important;
+ }
+ }
+ >.back:hover {
+ span {
+ background: var(--surface-300);
+ }
+ }
+ >h2, >h3 {
font-family: var(--serif);
font-weight: normal;
+ /* margin-bottom: 5px; */
}
- br {
- height: 0px !important;
- }
- h4 {
- margin-top: 10px;
+ >p {
font-family: var(--sans);
- /* font-weight: normal; */
+ /* margin-bottom: 10px; */
+ }
+
+ .list {
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ flex-wrap: wrap;
+ }
+ .action {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: fit-content;
+ background: var(--surface-200);
+ border: solid 1px var(--surface-300);
+ backdrop-filter: blur(10px);
+ border-radius: 100px;
+ padding: 5px 5px;
+ gap: 3px;
+ .icon {
+ --size: 17px;
+ font-size: var(--size) !important;
+ height: var(--size) !important;
+ width: var(--size) !important;
+ }
+ :not(.icon) {
+ font-family: var(--sans);
+ font-size: 14px;
+ padding: 0 4px;
+ }
+ }
+ code {
+ padding: 2px 3px 3px;
+ background: var(--surface-200);
+ border: solid 1px var(--surface-300);
+ border-radius: 5px;
+ }
+ input, textarea {
+ border-radius: 20px;
+ background: var(--surface-200);
+ padding: 10px;
+ border: solid 1px var(--surface-300);
+ outline: none;
+ resize: vertical;
+ font-family: var(--monospace);
+ }
+ textarea {
+ border-radius: 15px !important;
+ height: 100dvh;
+ min-height: 180px;
+ scrollbar-color: var(--color_text_white) transparent;
+ text-wrap-mode: wrap;
+ word-wrap: break-word;
+ word-break: break-all;
+ }
+ input::placeholder, textarea::placeholder {
+ color: var(--color_text_gray);
+ }
+
+ .action:hover {
+ cursor: pointer;
+ background: var(--surface-300)
+ }
+ .surface {
+ background: var(--surface-200);
+ border: solid 1px var(--surface-300);
+ backdrop-filter: blur(10px);
+ /* padding: 10px; */
+ border-radius: 15px;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+
+ .setting_entry:nth-child(n+2) {
+ border-top: solid 1px var(--surface-300);
+ }
+ }
+ .setting_entry {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 6px 11px;
+ padding-right: 6px;
+ cursor: pointer;
+ .icon {
+ color: var(--color_text_gray);
+ }
+ :not(.icon) {
+ font-family: var(--sans);
+ }
+ }
+ .setting_entry:hover {
+ background: var(--surface-200);
}
}
\ No newline at end of file
diff --git a/src/js/builtin.js b/src/js/builtin.js
index 7aebd4a..e92ba13 100644
--- a/src/js/builtin.js
+++ b/src/js/builtin.js
@@ -68,8 +68,4 @@ searcher = () => {
}
document.querySelector('#search_ddd').addEventListener('change', find)
document.querySelector('#search_ddd').addEventListener('input', (e) => {proceed(e.target.value.trim())})
- setInterval(() => {
- document.querySelector('#search_ddd').focus()
- proceed(document.querySelector('#search_ddd').value.trim())
- }, 100);
}
\ No newline at end of file
diff --git a/src/js/main.js b/src/js/main.js
index 50df5e8..51c9119 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -1,11 +1,13 @@
window.addEventListener('DOMContentLoaded', async () => {
+ search_focus.enable()
select('.settings_visual_help').overwrite(
$.div(
- {onclick: () => settings.open()},
+ {onclick: () => settings.start()},
$.span('Open settings'),
Icons.settings({fill: true, size: 15}),
)
)
- if (getFromStorage('active')?.hide_clock == true) clock()
+ if (storage.get('active')?.hide_clock == true) clock()
+ preset.load(storage.get('_active') || 'Default')
searcher()
})
diff --git a/src/js/settings.js b/src/js/settings.js
index 8647fae..25ad111 100644
--- a/src/js/settings.js
+++ b/src/js/settings.js
@@ -1,55 +1,167 @@
-const settings = {
- icons: {
- open: ``,
- close: ``
+const search_focus = {
+ id: null,
+ paused: false,
+ enable: () => {
+ search_focus.paused = false
+ if (search_focus.id) return
+ search_focus.id = setInterval(() => {
+ if (search_focus.paused) return
+ if (!search_focus.id) return
+ document.querySelector('#search_ddd').focus()
+ console.log(search_focus.id)
+ // proceed(document.querySelector('#search_ddd').value.trim())
+ }, 100)
},
- styles: {
- 'base_bg_color': [0, 360, 'Color', ''],
- 'base_bg_saturation': [0, 100, 'Saturation', '%'],
- 'base_bg_color_range': [0, 180, 'Range', ''],
- },
- set_style: (name, value) => {
- setToStorage(`theme_${name}`, value)
- const bg = document.querySelector('#background')
- bg.style.setProperty(`--${name}`, value)
- bg.style.display = 'none'
- setTimeout(async () => {
- bg.style.display = 'block'
- },)
- },
- init: async () => {
- document.querySelector('#settingsbutton').innerHTML = settings.icons.open
- document.querySelector('#settingsbutton').onclick = () => settings.open()
-
- Object.keys(settings.styles).map(
- key => {
- const v = getFromStorage(`theme_${key}`)
- if (!v) settings.set_style(key, `11${settings.styles[key][3]}`)
- else settings.set_style(key, v)
- }
- )
- },
- open: async () => {
- const ret = name => parseInt(document.querySelector('#background').style.getPropertyValue(`--${name}`).trim().replace('%', ''))
- const setter = (name, trail = '') => (e) => settings.set_style(name, `${e.target.value}${trail}`)
- const state = (name, trail = '') => {return {value: ret(name), oninput: setter(name, trail)}}
- document.body.appendChild($.div(
- {id: 'settingsui'},
- $.h2('Settings'),
- ...Object.keys(settings.styles).map(key => {
- const d = settings.styles[key]
- return $.div(
- $.h4(d[2]),
- $.input({type: 'range', min: d[0], max: d[1], ...state(key, d[3])})
- )
- })
- ))
- document.querySelector('#settingsbutton').innerHTML = settings.icons.close
- document.querySelector('#settingsbutton').onclick = () => {
- document.querySelector('#settingsui').remove()
- settings.init()
- }
+ disable: () => {
+ search_focus.paused = true
}
+}
+const settings = {
+ views: [],
+ current: null,
+
+ back: async () => {
+ settings.current = settings.views.pop()
+ if (!settings.current) {
+ selectAll('#settings > *').map(e => e.element.classList.add('ov'))
+ await sleep(.2)
+ settings.stop()
+ } else {
+ settings._open(settings.current())
+ }
+ },
+ _open: async elements => {
+ selectAll('#settings > *').map(e => e.element.classList.add('ov'))
+ await sleep(.2)
+ select('#settings').overwrite(...elements)
+ selectAll('#settings > *').map(e => e.element.classList.add('ov'))
+ requestAnimationFrame(() => selectAll('#settings > *').map(e => e.element.classList.remove('ov')))
+ },
-}
\ No newline at end of file
+ next: async fn => {
+ settings.views.push(settings.current)
+ settings.current = fn
+ settings._open(fn())
+ },
+
+ back_button: (title, icon = Icons.arrow_back) => $.div(
+ {onclick: settings.back, class: 'back'},
+ icon(),
+ $.h2(title),
+ ),
+ start: () => {
+ settings.views = []
+ settings.current = null
+ select('body').append(
+ $.div({id: 'settings'})
+ )
+ search_focus.disable()
+ settings.next(settings.main)
+ },
+ stop: () => {
+ select('#settings').remove()
+ search_focus.enable()
+ },
+ main: () => [
+ settings.back_button('Settings', Icons.close),
+ $.div(
+ {class: 'surface'},
+ $.div(
+ {class: 'setting_entry', onclick: () => settings.next(settings.basic)},
+ $.span('Basic settings'),
+ Icons.arrow_right()
+ ),
+ $.div(
+ {class: 'setting_entry', onclick: () => settings.next(settings.bangs)},
+ $.span('Bangs editor'),
+ Icons.arrow_right()
+ ),
+ $.div(
+ {class: 'setting_entry', onclick: () => settings.next(settings.customization)},
+ $.span('Customization'),
+ Icons.arrow_right()
+ ),
+ )
+ ],
+ basic: () => [
+ settings.back_button('Basic Settings'),
+ ],
+ bangs: () => [
+ settings.back_button('Bangs editor'),
+ $.p('Here are your custom bangs, that will override default one from DuckDuckGo'),
+ $.div(
+ {class: 'list'},
+ $.div(
+ {class: 'action', onclick: () => settings.next(() => settings.edit_bang(false))},
+ Icons.add_2(),
+ ),
+ ),
+ $.div(
+ {class: 'surface'},
+ ...bangs_override.toReversed().map(b => $.div(
+ {class: 'setting_entry', onclick: () => settings.next(() => settings.edit_bang(b))},
+ $.span(b.name),
+ Icons.arrow_right()
+ ))
+
+ )
+ ],
+ edit_bang: bang_data => {
+ const index = bangs_override.findIndex(b => b === bang_data)
+ /** @type {Bang} */
+ state.bang = bang_data || {
+ name: '',
+ keyword: '',
+ url: ''
+ }
+ return [
+ settings.back_button('Edit Bang'),
+ $.h3('Name'),
+ $.input({value: state.bang.name, placeholder: 'Bang name', onchange: (e) => state.bang.name = e.target.value.trim()}),
+ $.div({style: 'height: 10px'}),
+ $.h3('Keyword'),
+ $.p('Add a short keyword for your bang, e.g. ', $.code('google')),
+ $.input({value: state.bang.keyword, placeholder: 'Bang keyword', onchange: (e) => state.bang.keyword = e.target.value.trim()}),
+ $.div({style: 'height: 10px'}),
+ $.h3('URL'),
+ $.p('Add a url for your bang with ', $.code('{{{s}}}'), ' string, that will be replaced with the search query'),
+ $.textarea({value: state.bang.url, placeholder: 'Bang URL', onchange: (e) => state.bang.url = e.target.value.trim()}, state.bang.url),
+ $.div(
+ {class: 'list'},
+ $.div(
+ {class: 'action', onclick: () => {
+ if (state.bang.name.length && state.bang.keyword.length && state.bang.url.length) {
+ index != -1 ? bangs_override[index] = state.bang : bangs_override.push(state.bang)
+ storage.set('_bangs', bangs_override)
+ settings.back()
+ } else {
+ alert('Please fill in all fields')
+ }
+ }},
+ $.span('Save'),
+ ),
+ bang_data ? $.div(
+ {class: 'action', onclick: () => {
+ if (confirm('Are you sure you want to delete this bang?')) {
+ bangs_override.splice(index, 1)
+ storage.set('_bangs', bangs_override)
+ settings.back()
+ }
+ }},
+ $.span('Delete'),
+ ) : null
+ ),
+ ]
+ }
+}
+
+const state = {
+ /** @type {Bang} */
+ bang: null,
+ /** @type {string} */
+ search_url: null,
+}
+
+// debug
+sleep(.3).then(() => settings.start())
diff --git a/src/js/storage.js b/src/js/storage.js
index 55c07c5..d231911 100644
--- a/src/js/storage.js
+++ b/src/js/storage.js
@@ -34,18 +34,16 @@ const storage = {
* @typedef {Object} Preset
* @prop {number} version
* @prop {string} name
- * @prop {bool} disable_clock
- * @prop {bool} disable_animations
* @prop {"default" | "url" | "color" | "gradient"} background_type // TODO: gradient
* @prop {number} base_bg_color
* @prop {number} base_bg_saturation
* @prop {number} base_bg_color_range
- * @prop {string} search_url
- * @prop {bool} display_suggestions
- * @prop {bool} display_history
* @prop {string} font_sans
+// * @prop {string} font_sans_size
* @prop {string} font_serif
+// * @prop {string} font_serif_size
* @prop {string} font_monospace
+// * @prop {string} font_monospace_size
* @prop {string} color_text_white
* @prop {string} color_text_gray
* @prop {string} s000
@@ -114,10 +112,10 @@ const preset = {
)
}
const style_override = {
- s000: 'surface-000',
- s100: 'surface-100',
- s200: 'surface-200',
- s300: 'surface-300',
+ // s000: 'surface-000',
+ // s100: 'surface-100',
+ // s200: 'surface-200',
+ // s300: 'surface-300',
font_sans: 'sans',
font_serif: 'serif',
font_monospace: 'monospace',
@@ -145,34 +143,44 @@ const preset = {
default: () => { return {
name: 'Defualt',
version: preset.current_version,
- disable_clock: false,
- disable_animations: false,
+ // disable_clock: false,
+ // disable_animations: false,
background_type: 'default',
base_bg_color: 10,
base_bg_saturation: 10,
base_bg_brightness: 10,
base_bg_color_range: 10,
- search_url: 'https://www.google.com/search?q={{{s}}}',
- display_suggestions: false,
- display_history: false,
+ // search_url: 'https://www.google.com/search?q={{{s}}}',
+ // display_suggestions: false,
+ // display_history: false,
font_sans: 'Nunito',
font_serif: 'Playfair Display',
font_monospace: 'monospace',
color_text_white: '#fff',
color_text_gray: '#999',
s000: '#000',
- s100: '#222',
- s200: '#333',
- s300: '#444',
+ s100: '#fff',
+ s200: '#fff',
+ s300: '#fff',
}}
}
+const defaults = {
+ bangs: [
+ {name: 'Test bang', keyword: 'test_bang', url: 'https://www.google.com/search?q={{{s}}}'},
+ {name: 'Google Search AI', keyword: 'ai', url: 'https://www.google.com/search?q={{{s}}}&sourceid=chrome&ie=UTF-8&amc=1&oq={{{s}}}&udm=50&aep=109&cud='}
+ ],
+ search_engine: 'https://www.google.com/search?q={{{s}}}'
+}
+
/**
* @typedef {Object} Bang
* @prop {string} keyword
* @prop {string} name
* @prop {string} url
*/
-var bangs_override = [
- {name: 'Google Search AI', keyword: 'ai', url: 'https://www.google.com/search?q={{{s}}}&sourceid=chrome&ie=UTF-8&amc=1&oq={{{s}}}&udm=50&aep=109&cud='}
-]
\ No newline at end of file
+/** @type {Bang[]} */
+var bangs_override = storage.get('_bangs') || defaults.bangs
+
+/** @type {string} */
+var search_engine = storage.get('_search') || defaults.search_engine
\ No newline at end of file