Compare commits

..

No commits in common. "4627f4eb29f235602961b8767451366f2a7e6f7b" and "6e13ed40dafdd980361227462abb2f8c97369400" have entirely different histories.

15 changed files with 145 additions and 931 deletions

View file

@ -22,7 +22,6 @@
"<all_urls>" "<all_urls>"
], ],
"js": [ "js": [
"src/js/main.js"
] ]
} }
] ]

View file

@ -1,47 +1,23 @@
body { body {
--base_bg_color: 10;
--base_bg_color_range: 10;
--base_bg_saturation: 10%;
--color_text_white: #fff;
--color_text_gray: #999;
--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%);
display: flex; display: flex;
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
overflow-y: 0; overflow-y: 0;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: var(--surface-000) !important; background: #000;
} }
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
color: #fff; color: #fff;
/* background: #f002 !important; */
} }
#background { #background {
transition: opacity .15s;
--base_bg_color_tint_p: calc(var(--base_bg_color) + var(--base_bg_color_range));
--base_bg_color_tint_n: calc(var(--base_bg_color) - var(--base_bg_color_range));
--c-0-base: hsla(var(--base_bg_color), calc(var(--base_bg_saturation) - 9%), calc(var(--base_bg_brightness) + 2%), 1);
--c-1-base: hsla(var(--base_bg_color_tint_p), calc(var(--base_bg_saturation) - 8%), calc(var(--base_bg_brightness) + 2%), 1);
--c-3-base: hsla(var(--base_bg_color_tint_n), calc(var(--base_bg_saturation) - 6%), calc(var(--base_bg_brightness) + 26%), 1);
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
opacity: 1; opacity: 1;
} }
#background.changing {
opacity: 0;
}
/* https://www.mshr.app/mesh/1727910717365 */ /* https://www.mshr.app/mesh/1727910717365 */
@keyframes hero-gradient-animation { @keyframes hero-gradient-animation {
@ -50,10 +26,8 @@ body {
--s-start-0: 9%; --s-start-0: 9%;
--s-end-0: 55%; --s-end-0: 55%;
--y-0: 80%; --y-0: 80%;
--c-0: var(--c-0-base); --c-0: hsla(266.99999999999983, 1%, 12%, 1);
--c-1: var(--c-1-base); --c-1: hsla(335.9999999999997, 2%, 22%, 1);
--c-2: hsla(0,0%, 0%, 0.49);
--c-3: var(--c-3-base);
--s-start-1: 5%; --s-start-1: 5%;
--s-end-1: 72%; --s-end-1: 72%;
--y-1: 24%; --y-1: 24%;
@ -61,9 +35,11 @@ body {
--s-start-2: 5%; --s-start-2: 5%;
--s-end-2: 52%; --s-end-2: 52%;
--y-2: 82%; --y-2: 82%;
--c-2: hsla(53.999999999999886, 0%, 0%, 0.49);
--x-2: 13%; --x-2: 13%;
--s-start-3: 13%; --s-start-3: 13%;
--s-end-3: 68%; --s-end-3: 68%;
--c-3: hsla(299, 4%, 36%, 1);
--x-3: 24%; --x-3: 24%;
--y-3: 7%; --y-3: 7%;
} }
@ -73,10 +49,8 @@ body {
--s-start-0: 9%; --s-start-0: 9%;
--s-end-0: 55%; --s-end-0: 55%;
--y-0: 94%; --y-0: 94%;
--c-0: var(--c-0-base); --c-0: hsla(266.99999999999943, 0%, 12%, 1);
--c-1: var(--c-1-base); --c-1: hsla(0, 4%, 19%, 1);
--c-2: hsla(0,0%, 0%, 0.49);
--c-3: var(--c-3-base);
--s-start-1: 5%; --s-start-1: 5%;
--s-end-1: 72%; --s-end-1: 72%;
--y-1: 25%; --y-1: 25%;
@ -84,9 +58,11 @@ body {
--s-start-2: 5%; --s-start-2: 5%;
--s-end-2: 52%; --s-end-2: 52%;
--y-2: 20%; --y-2: 20%;
--c-2: hsla(54.000000000000036, 0%, 0%, 0.49);
--x-2: 98%; --x-2: 98%;
--s-start-3: 13%; --s-start-3: 13%;
--s-end-3: 68%; --s-end-3: 68%;
--c-3: hsla(298.99999999999994, 3%, 41%, 1);
--x-3: 95%; --x-3: 95%;
--y-3: 92%; --y-3: 92%;
} }
@ -119,13 +95,13 @@ body {
@property --c-0 { @property --c-0 {
syntax: '<color>'; syntax: '<color>';
inherits: false; inherits: false;
initial-value: hsla(0,0,0,0) initial-value: hsla(266.99999999999983, 1%, 12%, 1)
} }
@property --c-1 { @property --c-1 {
syntax: '<color>'; syntax: '<color>';
inherits: false; inherits: false;
initial-value: hsla(0,0,0,0) initial-value: hsla(335.9999999999997, 2%, 22%, 1)
} }
@property --s-start-1 { @property --s-start-1 {
@ -173,7 +149,7 @@ body {
@property --c-2 { @property --c-2 {
syntax: '<color>'; syntax: '<color>';
inherits: false; inherits: false;
initial-value: hsla(0,0,0,0) initial-value: hsla(53.999999999999886, 0%, 0%, 0.49)
} }
@property --x-2 { @property --x-2 {
@ -197,7 +173,7 @@ body {
@property --c-3 { @property --c-3 {
syntax: '<color>'; syntax: '<color>';
inherits: false; inherits: false;
initial-value: hsla(0,0,0,0) initial-value: hsla(299, 4%, 36%, 1)
} }
@property --x-3 { @property --x-3 {
@ -213,40 +189,21 @@ body {
} }
#background { #background {
background-color: var(--surface-000);
}
#background.default {
--x-0: 85%; --x-0: 85%;
--y-0: 80%; --y-0: 80%;
--c-0: hsla(var(--base_bg_color), calc(var(--base_bg_saturation) - 9%), 12%, 1); --c-0: hsla(266.99999999999983, 1%, 12%, 1);
--c-1: hsla(var(--base_bg_color_tint_p), calc(var(--base_bg_saturation) - 8%), 22%, 1); --c-1: hsla(335.9999999999997, 2%, 22%, 1);
--y-1: 24%; --y-1: 24%;
--x-1: 60%; --x-1: 60%;
--y-2: 82%; --y-2: 82%;
--c-2: hsla(0,0%, 0%, 0.49); --c-2: hsla(53.999999999999886, 0%, 0%, 0.49);
--x-2: 13%; --x-2: 13%;
--c-3: hsla(var(--base_bg_color_tint_n), calc(var(--base_bg_saturation) - 6%), 36%, 1); --c-3: hsla(299, 4%, 36%, 1);
--x-3: 24%; --x-3: 24%;
--y-3: 7%; --y-3: 7%;
background-color: var(--surface-000);
background-image:
radial-gradient(
circle at var(--x-0) var(--y-0), var(--c-0) var(--s-start-0),
transparent var(--s-end-0)
),
radial-gradient(
circle at var(--x-1) var(--y-1), var(--c-1) var(--s-start-1),
transparent var(--s-end-1)
),
radial-gradient(
circle at var(--x-2) var(--y-2), var(--c-2) var(--s-start-2),
transparent var(--s-end-2)
),
radial-gradient(
circle at var(--x-3) var(--y-3), var(--c-3) var(--s-start-3),
transparent var(--s-end-3)
)
; ;
background-color: hsla(0, 0%, 0%, 1);
background-image: radial-gradient(circle at var(--x-0) var(--y-0), var(--c-0) var(--s-start-0), transparent var(--s-end-0)), radial-gradient(circle at var(--x-1) var(--y-1), var(--c-1) var(--s-start-1), transparent var(--s-end-1)), radial-gradient(circle at var(--x-2) var(--y-2), var(--c-2) var(--s-start-2), transparent var(--s-end-2)), radial-gradient(circle at var(--x-3) var(--y-3), var(--c-3) var(--s-start-3), transparent var(--s-end-3));
animation: hero-gradient-animation 10s linear infinite alternate; animation: hero-gradient-animation 10s linear infinite alternate;
background-blend-mode: normal, normal, normal, normal; background-blend-mode: normal, normal, normal, normal;
} }

View file

@ -1,23 +0,0 @@
@font-face {
font-family: 'Material Symbols Rounded';
font-style: normal;
font-weight: 100 700;
src: url(./icons.woff2) format('woff2');
}
.material-symbols-rounded {
font-family: 'Material Symbols Rounded';
font-weight: normal;
font-style: normal;
font-size: 24px;
overflow: hidden;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
}

Binary file not shown.

View file

@ -26,11 +26,11 @@
#searcher > h1 { #searcher > h1 {
font-family: 'Playfair Display'; font-family: 'Playfair Display';
font-weight: normal; font-weight: normal;
font-size: 60px; font-size: 70px;
text-align: center; text-align: center;
} #searcher > h1 > span { } #searcher > h1 > span {
transition: background .2s; transition: background .2s;
font-size: 23px; font-size: 33px;
font-family: 'Fira Code'; font-family: 'Fira Code';
background: rgba(255,255,255,.1); background: rgba(255,255,255,.1);
padding: 0 5px 0; padding: 0 5px 0;

View file

@ -1,199 +0,0 @@
.settings_visual_help {
z-index: 11;
position: fixed;
top: 0px;
right: 0px;
cursor: pointer;
height: 100dvh;
width: 40px;
display: flex;
align-items: center;
justify-content: end;
padding-left: 50px;
cursor: default;
>div {
cursor: pointer;
rotate: -90deg;
transform: translateY(98px) scale(.9);
transition: transform .2s;
gap: 7px;
display: flex;
align-items: center;
justify-content: center;
background: var(--surface-200);
padding: 5px 10px;
border-radius: 10px 10px 0 0;
border: solid 1px var(--surface-300);
border-bottom: none;
user-select: none;
}
>div:hover {
background: var(--surface-300);
}
>div:active {
background: var(--surface-300);
}
span:not(.icon) {
text-wrap: nowrap;
font-family: var(--sans);
}
}
.settings_visual_help:hover {
>div {
transform: translateY(58px);
}
}
#settings.ov {
transform: translateX(34vw);
}
#settings {
overflow-y: auto;
overflow-x: hidden;
z-index: 1001;
position: fixed;
right: 0;
top: 0;
display: flex;
flex-direction: column;
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;
>* {
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; */
}
>p {
font-family: var(--sans);
/* 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);
}
}

24
src/css/update.css Normal file
View file

@ -0,0 +1,24 @@
#update {
position: fixed;
bottom: 0;
right: 0;
font-size: 13px;
font-family: 'Nunito';
background: rgba(255,255,255,.1);
border-radius: 100px;
margin: 10px;
display: flex;
z-index: 20;
transition: transform .5s;
} #update > * {
padding: 5px 10px;
} #update > a {
text-decoration: none;
background: rgba(255,255,255,.1);
border-radius: 100px;
cursor: pointer;
}
#update.hidden {
transform: translateY(40px);
}

View file

@ -5,9 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title> <title>Home</title>
<link rel="shortcut icon" href="icon.svg" type="image/x-icon"> <link rel="shortcut icon" href="icon.svg" type="image/x-icon">
<link rel="stylesheet" href="css/icons.css">
<link rel="stylesheet" href="css/global.css"> <link rel="stylesheet" href="css/global.css">
<link rel="stylesheet" href="css/settings.css">
<link rel="stylesheet" href="css/clock.css"> <link rel="stylesheet" href="css/clock.css">
<link rel="stylesheet" href="css/search.css"> <link rel="stylesheet" href="css/search.css">
<link rel="stylesheet" href="css/update.css"> <link rel="stylesheet" href="css/update.css">
@ -19,15 +17,12 @@
<p>Search via Google</p> <p>Search via Google</p>
</div> </div>
<input type="text" id="search_ddd" autocorrect="off" autocomplete="off" autofocus> <input type="text" id="search_ddd" autocorrect="off" autocomplete="off" autofocus>
<div class="settings_visual_help"> <div id="update" class="hidden"><span>update available</span> <a href="https://github.com/ktnk-dev/LittleHome">download</a></div>
</div> <script src="js/abstract.js"></script>
<script src="lib/sinya.lib.js"></script>
<script src="lib/gicons.lib.js"></script>
<script src="js/storage.js"></script> <script src="js/storage.js"></script>
<script src="js/bang.js"></script> <script src="js/bang.js"></script>
<script src="js/builtin.js"></script> <script src="js/builtin.js"></script>
<script src="js/update.js"></script> <script src="js/update.js"></script>
<script src="js/settings.js"></script>
<script src="js/main.js"></script> <script src="js/main.js"></script>
</body> </body>
</html> </html>

67
src/js/abstract.js Normal file
View file

@ -0,0 +1,67 @@
/**
* @typedef {Object} $
* @property {function(object=, ...HTMLElement): HTMLElement} DOCS - creates element with this tagname
*/
/**
* ### Create element
*
* @type {$}
*/
const $ = new Proxy(
class {
/**
* ### Creates `$` element
* @param {string} type element type
* @param {object} props element properties (onclick, class, ...)
* @param {...HTMLElement} children
* @returns {HTMLElement}
*/
static createElement(type, props, ...children) {
const element = document.createElement(type)
if (props) {
try {
if (
props instanceof Node
|| typeof props === 'string'
|| typeof props === 'number'
) children = [props, ...children]
else {throw Error()}
} catch {
for (const [key, value] of Object.entries(props)) {
if (key.startsWith('on') && typeof value === 'function') {
element.addEventListener(key.slice(2).toLowerCase(), value)
} else if (key === 'className') {
element.className = value
} else {
element.setAttribute(key, value)
}
}
}
}
children.flat().forEach(child => {
if (typeof child === 'string' || typeof child === 'number') {
element.innerHTML += child
} else if (child instanceof Node) {
element.appendChild(child)
}
});
element[Symbol.toPrimitive] = function(hint) {
if (hint === 'string') return this.outerHTML
return this
};
return element
}
},
{
get(target, element) {
return function(props, ...children) {
return target.createElement(element, props, ...children)
}
}
}
);

View file

@ -64,8 +64,12 @@ searcher = () => {
const query = element.target.value.trim() const query = element.target.value.trim()
const fb = findbang(query) const fb = findbang(query)
const url = fb.valid ? fb.data.u : 'https://www.google.com/search?q={{{s}}}' const url = fb.valid ? fb.data.u : 'https://www.google.com/search?q={{{s}}}'
window.location.href = url.replace('{{{s}}}', encodeURIComponent(fb.found ? query.replace('!'+fb.inputed, '').trim() : query.trim())) window.location.href = url.replace('{{{s}}}', encodeURI(fb.found ? query.replace('!'+fb.inputed, '').trim() : query.trim()))
} }
document.querySelector('#search_ddd').addEventListener('change', find) document.querySelector('#search_ddd').addEventListener('change', find)
document.querySelector('#search_ddd').addEventListener('input', (e) => {proceed(e.target.value.trim())}) 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);
} }

View file

@ -1,13 +1,5 @@
window.addEventListener('DOMContentLoaded', async () => { window.addEventListener('DOMContentLoaded', async () => {
search_focus.enable() clock()
select('.settings_visual_help').overwrite(
$.div(
{onclick: () => settings.start()},
$.span('Open settings'),
Icons.settings({fill: true, size: 15}),
)
)
if (storage.get('active')?.hide_clock == true) clock()
preset.load(storage.get('_active') || 'Default')
searcher() searcher()
check_update()
}) })

View file

@ -1,167 +0,0 @@
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)
},
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')))
},
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())

View file

@ -1,8 +1,7 @@
const storage = { const CookieManager = {
get: (variable_name) => { get: (cname) => {
const worker = (cname) => {
let name = cname + "="; let name = cname + "=";
let decodedCookie = document.cookie; let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';'); let ca = decodedCookie.split(';');
for(let i = 0; i <ca.length; i++) { for(let i = 0; i <ca.length; i++) {
let c = ca[i]; let c = ca[i];
@ -14,173 +13,21 @@ const storage = {
} }
} }
return ""; return "";
}
const c = worker(variable_name)
return c === '' ? null : JSON.parse(c).data
}, },
set: (variable_name, value) => { set: (cname, cvalue) => {
const worker = (cname, cvalue) => {
const d = new Date(); const d = new Date();
d.setTime(d.getTime() + (999*24*60*60*1000)); d.setTime(d.getTime() + (999*24*60*60*1000));
let expires = "expires="+ d.toUTCString(); let expires = "expires="+ d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
} }
worker(variable_name, JSON.stringify({data: value}))
}
} }
/** function getFromStorage(variable_name) {
* @typedef {Object} Preset const c = CookieManager.get(variable_name)
* @prop {number} version return c === '' ? null : JSON.parse(c).data
* @prop {string} name
* @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} 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
* @prop {string} s100
* @prop {string} s200
* @prop {string} s300
* @prop {bool} m
*/
const preset = {
/** @type {Preset} */
active: {},
current_version: 1,
migration: () => ([
['version', preset.current_version, 1],
['m', true, 1],
]).forEach(q => preset.active[q[0]] = q.length>2 ? q[1] : (preset.active[q[0]] || q[1])),
save: () => {
preset.migration()
storage.set(preset.active.name, preset.active)
storage.set('_active', preset.active.name)
var list = storage.get('_presets') ? storage.get('_presets') : []
if (!list.includes(preset.active.name)) {
list.push(preset.active.name)
storage.set('_presets', list)
}
preset.render()
},
delete: () => {
/**@type {string[]} */
var list = storage.get('_presets') ? storage.get('_presets') : []
list[list.indexOf(preset.active.name)] = false
list = list.filter(q => q != null)
storage.set('_presets', list)
preset.load(list.length > 0 ? list[0] : 'Default')
},
load: preset_name => {
if (!preset_name) preset_name = storage.get('_active') ? storage.get('_active') : 'Default'
var list = storage.get('_presets') ? storage.get('_presets') : []
if (!list.includes(preset_name)) {
preset.active = preset.default()
preset.active.name = preset_name
preset.save()
} else {
preset.active = storage.get(preset_name)
preset.save()
}
},
render: () => {
const force_redraw = (element) => {
if (!element) return
element.classList.toggle('twt')
var n = document.createTextNode(' ')
var disp = element.style.display || 'block'
element.appendChild(n)
element.style.display = 'none'
requestAnimationFrame(
() => {
element.style.display = disp
n.parentNode.removeChild(n)
}
)
}
const style_override = {
// s000: 'surface-000',
// s100: 'surface-100',
// s200: 'surface-200',
// s300: 'surface-300',
font_sans: 'sans',
font_serif: 'serif',
font_monospace: 'monospace',
}
const set_style = (element, s) => {
s = s.replaceAll('-', '_')
const prop_name = Object.keys(style_override).includes(s) ? style_override[s] : s
element.style.setProperty('--'+prop_name, ['base_bg_saturation','base_bg_brightness'].includes(s) ? (preset.active[s]+'%') : preset.active[s])
}
select('#background').element.setAttribute('class', preset.active.background_type);
(['s000', 's100', 's200', 's300', 'font_sans', 'font_serif', 'font_monospace', 'color_text_white', 'color_text_gray',])
.forEach(s => set_style(select('body').element, s));
(['base_bg_color', 'base_bg_saturation', 'base_bg_color_range', 'base_bg_brightness'])
.forEach(s => set_style(select('#background').element, s));
force_redraw(select('#background').element)
},
/**
* @returns {Preset}
*/
default: () => { return {
name: 'Defualt',
version: preset.current_version,
// 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,
font_sans: 'Nunito',
font_serif: 'Playfair Display',
font_monospace: 'monospace',
color_text_white: '#fff',
color_text_gray: '#999',
s000: '#000',
s100: '#fff',
s200: '#fff',
s300: '#fff',
}}
} }
const defaults = { function setToStorage(variable_name, value) {
bangs: [ CookieManager.set(variable_name, JSON.stringify({data: value}))
{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
*/
/** @type {Bang[]} */
var bangs_override = storage.get('_bangs') || defaults.bangs
/** @type {string} */
var search_engine = storage.get('_search') || defaults.search_engine

View file

@ -1,56 +0,0 @@
/**
* @typedef {Object} IconOptions
* @prop {100|200|300|400|500|600|700} weight Symbol weight
* @prop {bool} fill
* @prop {'low' | 'default' | 'high'} grade Symbol thickness
* @prop {20|48} optical_size (from 20 to 48) For the image to look the same at different sizes in dp
* @prop {number} size Icon size in px
* @prop {string} color Any css-supported color variant (names, rgb, hex)
*/
var DEFAULT_ICON_COLOR = '#000'
var DEFAULT_ICON_SIZE = 24
/** @type {IconOptions} */
var DEFAULT_ICON_OPTIONS = {}
/** @type {Record<string, (options: IconOptions) => HTMLSpanElement} */
const Icons = new Proxy(
class {
/**
* @param {string} name
* @param {IconOptions} options
* @returns {HTMLSpanElement}
*/
static getIcon(name, {fill, weight, grade, optical_size, size, color}) {
const gr = ({
'low': -25,
'default': 0,
'high': 200
})[grade]
const data = `
'FILL' ${fill !== undefined ? fill?1:0 : 1},
'wdth' ${weight || 400},
'GRAD' ${gr || 0},
'opsz' ${optical_size || 20}`
const fz = size ? typeof size != Number ? `${size}px`:size : (DEFAULT_ICON_SIZE+'px')
return $.span(
{class: 'material-symbols-rounded icon', style: `font-size: ${fz}; width: ${fz}; height: ${fz}; ${color ? `color: ${color};` : ''}font-variation-settings: ${data}`},
name
)
}
},
{
get(target, name) {
/**
* @argument {IconOptions} options
*/
return function(options) {
return target.getIcon(name, options || DEFAULT_ICON_OPTIONS)
}
}
}
)
const gIconsInit = () => document.head.innerHTML += `
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200">
`

View file

@ -1,226 +0,0 @@
/**
* @typedef {Object} $
* @property {function(object=, ...HTMLElement): HTMLElement} div - creates div element
* @property {function(object=, ...HTMLElement): HTMLElement} p - creates p element
* @property {function(object=, ...HTMLElement): HTMLElement} span - creates span element
* @property {function(object=, ...HTMLElement): HTMLElement} button - creates button element
* @property {function(object=, ...HTMLElement): HTMLElement} input - creates input element
* @property {function(object=, ...HTMLElement): HTMLElement} a - creates a element
* @property {function(object=, ...HTMLElement): HTMLElement} img - creates img element
* @property {function(object=, ...HTMLElement): HTMLElement} h1 - creates h1 element
* @property {function(object=, ...HTMLElement): HTMLElement} h2 - creates h2 element
* @property {function(object=, ...HTMLElement): HTMLElement} h3 - creates h3 element
* @property {function(object=, ...HTMLElement): HTMLElement} h4 - creates h4 element
* @property {function(object=, ...HTMLElement): HTMLElement} h5 - creates h5 element
* @property {function(object=, ...HTMLElement): HTMLElement} h6 - creates h6 element
* @property {function(object=, ...HTMLElement): HTMLElement} ul - creates ul element
* @property {function(object=, ...HTMLElement): HTMLElement} li - creates li element
* @property {function(object=, ...HTMLElement): HTMLElement} form - creates form element
* @property {function(object=, ...HTMLElement): HTMLElement} table - creates table element
* @property {function(object=, ...HTMLElement): HTMLElement} tr - creates tr element
* @property {function(object=, ...HTMLElement): HTMLElement} td - creates td element
*/
/**
* ### Create element
*
* @type {$}
*/
const $ = new Proxy(
class {
/**
* ### Creates `$` element
* @param {string} type element type
* @param {object} props element properties (onclick, class, ...)
* @param {...HTMLElement} children
* @returns {HTMLElement}
*/
static createElement(type, props, ...children) {
const element = document.createElement(type)
if (props) {
try {
if (
props instanceof Node
|| typeof props === 'string'
|| typeof props === 'number'
) children = [props, ...children]
else {throw Error()}
} catch {
for (const [key, value] of Object.entries(props)) {
if (key.startsWith('on') && typeof value === 'function') {
element.addEventListener(key.slice(2).toLowerCase(), value)
} else if (key === 'className') {
element.className = value
} else {
element.setAttribute(key, value)
}
}
}
}
children.flat().forEach(child => {
if (typeof child === 'string' || typeof child === 'number') {
element.innerHTML += child
} else if (child instanceof Node) {
element.appendChild(child)
}
});
element[Symbol.toPrimitive] = function(hint) {
if (hint === 'string') return this.outerHTML
return this
};
return element
}
},
{
get(target, element) {
return function(props, ...children) {
return target.createElement(element, props, ...children)
}
}
}
);
/**
* Select single DOM element and wrap them
* @param {string} selector
* @returns {WrappedElement}
*/
const select = (selector, _target = document) => wrap(_target.querySelector(selector))
/**
* Select multiple DOM element and wrap them
* @param {string} selector
* @returns {Array.<WrappedElement>}
*/
const selectAll = (selector, _target = document) => [..._target.querySelectorAll(selector)].map(_ => wrap(_))
/**
* Wrap existing DOM element
* @param {HTMLElement} element
* @returns {WrappedElement}
*/
const wrap = element => new WrappedElement(element)
const sleep = s => new Promise(r => setTimeout(r, s*1000))
class WrappedElement {
/**
* @type {HTMLElement}
*/
element
/**
* Create element wrapper
* @param {HTMLElement} element
*/
constructor(element) {
this.element = element
}
/**
* Override element classes
* @param {...string} list
* @returns {WrappedElement} this element for chaining
*/
classes(...list) {
this.element.classList = list.join(' ')
return this
}
/**
* Get elements styles to edit them
* @returns {CSSStyleDeclaration}
*/
styles () {
return this.element.style
}
/**
* Get element nodes
* @returns {Array.<WrappedElement>}
*/
nodes () {
return [...this.element.childNodes].map(e => wrap(e))
}
/**
* Apply animation to element (async)
* @param {string} animation_name
* @param {number} animation_duration
* @param {string} animation_function_name
* @returns {WrappedElement} this element for chaining
*/
async animate (animation_name, animation_duration, animation_function_name = '') {
this.styles().animation = `${animation_name} ${animation_duration}s ${animation_function_name}`
await sleep(animation_duration - (animation_duration / 20))
this.styles().animation = ``
return this
}
/**
* Clear element content (async)
* @returns {WrappedElement} this element for chaining
*/
async clear () {
this.element.innerHTML = ''
return this
}
/**
* Overwrite element content (async)
* @param {...HTMLElement} children
* @returns {WrappedElement} this element for chaining
*/
async overwrite (...children) {
this.clear()
this.append(...children)
return this
}
/**
* Add content to element (async)
* @param {...HTMLElement} children
* @returns {WrappedElement} this element for chaining
*/
async append (...children) {
children.forEach(child => {
if (typeof child === 'string' || typeof child === 'number') {
this.element.innerHTML += child
} else if (child instanceof Node) {
this.element.appendChild(child)
} else if (child instanceof WrappedElement) {
this.element.appendChild(child.element)
}
})
return this
}
/**
* Removes element (async)
*/
async remove() {
this.element.remove()
}
/**
* Return element node by selector
* @param {string} selector
* @returns {WrappedElement}
*/
select (selector) {
return select(selector, this.element)
}
/**
* Return element nodes by selector
* @param {string} selector
* @returns {Array.<WrappedElement>}
*/
selectAll (selector) {
return selectAll(selector, this.element)
}
}
const Body = select('body')