public release

This commit is contained in:
синечка ♡ 2025-05-13 19:14:38 +03:00
parent 80172f0a17
commit b2fe50fd4f
12 changed files with 533 additions and 1 deletions

View file

@ -1 +1,9 @@
# LittleHome
![](./media/logo.png) <br>
# How to install
1. Install [`this`](https://fonts.google.com/share?selection.family=Fira+Code:wght@300..700|Nunito:ital,wght@0,200..1000;1,200..1000|Playfair+Display:ital,wght@0,400..900;1,400..900) fonts
2. Clone repository
3. Go to `chrome://extensions`
4. Enable `Developer mode`
5. Click `Load unpacked` > select repo's folder
6. Enjoy your new tab/home page

32
manifest.json Normal file
View file

@ -0,0 +1,32 @@
{
"manifest_version": 3,
"name": "LittleHome",
"version": "0.2",
"description": "My personal homepage with bangs support",
"permissions": [
"activeTab",
"storage"
],
"icons": {
"48": "src/icon.svg"
},
"action": {
"default_icon": "src/icon.svg"
},
"chrome_url_overrides": {
"newtab": "src/index.html"
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"src/js/abstract.js",
"src/js/storage.js",
"src/js/bang.js",
"src/js/main.js"
]
}
]
}

BIN
media/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

28
src/css/clock.css Normal file
View file

@ -0,0 +1,28 @@
#clock {
position: fixed;
display: flex;
flex-direction: row;
gap: 10px;
overflow-y: hidden;
align-items: start;
justify-content: start;
height: 100px;
transition: opacity .2s;
/* opacity: 0; */
mask-image: linear-gradient(transparent, #000 10%, #000 90%, transparent );
} #clock > .digit {
display: flex;
flex-direction: column;
align-items: start;
justify-content: start;
transition: transform .4s;
gap: 0;
} #clock > .digit > span {
font-weight: 800;
font-size: 80px;
font-family: 'Nunito';
margin: 0;
padding: 0;
} #clock > .digit.l {
transform: translateY(-7px);
}

209
src/css/global.css Normal file
View file

@ -0,0 +1,209 @@
body {
display: flex;
height: 100vh;
width: 100vw;
overflow-y: 0;
align-items: center;
justify-content: center;
background: #000;
}
* {
margin: 0;
padding: 0;
color: #fff;
}
#background {
height: 100vh;
width: 100vw;
opacity: 1;
}
/* https://www.mshr.app/mesh/1727910717365 */
@keyframes hero-gradient-animation {
0% {
--x-0: 85%;
--s-start-0: 9%;
--s-end-0: 55%;
--y-0: 80%;
--c-0: hsla(266.99999999999983, 1%, 12%, 1);
--c-1: hsla(335.9999999999997, 2%, 22%, 1);
--s-start-1: 5%;
--s-end-1: 72%;
--y-1: 24%;
--x-1: 60%;
--s-start-2: 5%;
--s-end-2: 52%;
--y-2: 82%;
--c-2: hsla(53.999999999999886, 0%, 0%, 0.49);
--x-2: 13%;
--s-start-3: 13%;
--s-end-3: 68%;
--c-3: hsla(299, 4%, 36%, 1);
--x-3: 24%;
--y-3: 7%;
}
100% {
--x-0: 31%;
--s-start-0: 9%;
--s-end-0: 55%;
--y-0: 94%;
--c-0: hsla(266.99999999999943, 0%, 12%, 1);
--c-1: hsla(0, 4%, 19%, 1);
--s-start-1: 5%;
--s-end-1: 72%;
--y-1: 25%;
--x-1: 2%;
--s-start-2: 5%;
--s-end-2: 52%;
--y-2: 20%;
--c-2: hsla(54.000000000000036, 0%, 0%, 0.49);
--x-2: 98%;
--s-start-3: 13%;
--s-end-3: 68%;
--c-3: hsla(298.99999999999994, 3%, 41%, 1);
--x-3: 95%;
--y-3: 92%;
}
}
@property --x-0 {
syntax: '<percentage>';
inherits: false;
initial-value: 85%
}
@property --s-start-0 {
syntax: '<percentage>';
inherits: false;
initial-value: 9%
}
@property --s-end-0 {
syntax: '<percentage>';
inherits: false;
initial-value: 55%
}
@property --y-0 {
syntax: '<percentage>';
inherits: false;
initial-value: 80%
}
@property --c-0 {
syntax: '<color>';
inherits: false;
initial-value: hsla(266.99999999999983, 1%, 12%, 1)
}
@property --c-1 {
syntax: '<color>';
inherits: false;
initial-value: hsla(335.9999999999997, 2%, 22%, 1)
}
@property --s-start-1 {
syntax: '<percentage>';
inherits: false;
initial-value: 5%
}
@property --s-end-1 {
syntax: '<percentage>';
inherits: false;
initial-value: 72%
}
@property --y-1 {
syntax: '<percentage>';
inherits: false;
initial-value: 24%
}
@property --x-1 {
syntax: '<percentage>';
inherits: false;
initial-value: 60%
}
@property --s-start-2 {
syntax: '<percentage>';
inherits: false;
initial-value: 5%
}
@property --s-end-2 {
syntax: '<percentage>';
inherits: false;
initial-value: 52%
}
@property --y-2 {
syntax: '<percentage>';
inherits: false;
initial-value: 82%
}
@property --c-2 {
syntax: '<color>';
inherits: false;
initial-value: hsla(53.999999999999886, 0%, 0%, 0.49)
}
@property --x-2 {
syntax: '<percentage>';
inherits: false;
initial-value: 13%
}
@property --s-start-3 {
syntax: '<percentage>';
inherits: false;
initial-value: 13%
}
@property --s-end-3 {
syntax: '<percentage>';
inherits: false;
initial-value: 68%
}
@property --c-3 {
syntax: '<color>';
inherits: false;
initial-value: hsla(299, 4%, 36%, 1)
}
@property --x-3 {
syntax: '<percentage>';
inherits: false;
initial-value: 24%
}
@property --y-3 {
syntax: '<percentage>';
inherits: false;
initial-value: 7%
}
#background {
--x-0: 85%;
--y-0: 80%;
--c-0: hsla(266.99999999999983, 1%, 12%, 1);
--c-1: hsla(335.9999999999997, 2%, 22%, 1);
--y-1: 24%;
--x-1: 60%;
--y-2: 82%;
--c-2: hsla(53.999999999999886, 0%, 0%, 0.49);
--x-2: 13%;
--c-3: hsla(299, 4%, 36%, 1);
--x-3: 24%;
--y-3: 7%;
;
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;
background-blend-mode: normal, normal, normal, normal;
}

44
src/css/search.css Normal file
View file

@ -0,0 +1,44 @@
#search_ddd {
z-index: 10;
position: fixed;
color: #000;
/* visibility: hidden */
opacity: 0;
}
#searcher {
background: rgba(0,0,0,.2);
transition: opacity .2s;
opacity: 0;
position: fixed;
top: 0;
left: 0;
z-index: 5;
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 15px;
}
#searcher > h1 {
font-family: 'Playfair Display';
font-weight: normal;
font-size: 70px;
text-align: center;
} #searcher > h1 > span {
transition: background .2s;
font-size: 33px;
font-family: 'Fira Code';
background: rgba(255,255,255,.1);
padding: 0 5px 0;
border-radius: 10px;
vertical-align:middle;
} #searcher > h1 > span.error {
background: rgba(255,100,100,.2);
} #searcher > p {
font-family: 'Nunito';
}

1
src/icon.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>

After

Width:  |  Height:  |  Size: 243 B

26
src/index.html Normal file
View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
<link rel="shortcut icon" href="icon.svg" type="image/x-icon">
<link rel="stylesheet" href="css/global.css">
<link rel="stylesheet" href="css/clock.css">
<link rel="stylesheet" href="css/search.css">
</head>
<body>
<div id="background"></div>
<div id="searcher">
<h1></h1>
<p>Search via Google</p>
</div>
<input type="text" id="search_ddd" autocorrect="off" autocomplete="off" autofocus>
<script src="js/abstract.js"></script>
<script src="js/storage.js"></script>
<script src="js/bang.js"></script>
<script src="js/main.js"></script>
</body>
</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)
}
}
}
);

1
src/js/bang.js Normal file

File diff suppressed because one or more lines are too long

83
src/js/main.js Normal file
View file

@ -0,0 +1,83 @@
const clock = async () => {
const render = async () => {
const baseOffset = 109.12
const tsp = number => (number < 10 ? `0${number}` : `${number}`).split('').map(e => parseInt(e)*baseOffset)
const time = new Date()
var ft = [tsp(time.getHours()), tsp(time.getMinutes()), tsp(time.getSeconds())].flat()
var fe = [
document.querySelectorAll('.digit.h')[0],
document.querySelectorAll('.digit.h')[1],
document.querySelectorAll('.digit.m')[0],
document.querySelectorAll('.digit.m')[1],
document.querySelectorAll('.digit.s')[0],
document.querySelectorAll('.digit.s')[1]
]
var _ = [...Array(6).keys()].map(i => {
const value = ft[i]
const element = fe[i]
element.style = `transform: translateY(-${value}px)`
})
}
document.querySelector('body').appendChild(
$.div(
{id: 'clock'},
$.div({class: 'digit h'}, [...Array(3).keys()].map(_ => $.span(_.toString()))),
$.div({class: 'digit h'}, [...Array(11).keys()].map(_ => $.span(_ < 10 ? _.toString() : '0'))),
$.div({class: 'digit l'}, $.span(':')),
$.div({class: 'digit m'}, [...Array(7).keys()].map(_ => $.span(_ < 10 ? _.toString() : '0'))),
$.div({class: 'digit m'}, [...Array(11).keys()].map(_ => $.span(_ < 10 ? _.toString() : '0'))),
$.div({class: 'digit l'}, $.span(':')),
$.div({class: 'digit s'}, [...Array(7).keys()].map(_ => $.span(_ < 10 ? _.toString() : '0'))),
$.div({class: 'digit s'}, [...Array(11).keys()].map(_ => $.span(_ < 10 ? _.toString() : '0'))),
)
)
render()
setInterval(() => render(), 1000);
}
searcher = () => {
const findbang = query => {
if (query.indexOf('!') == -1) return {found: false, valid: false}
const inputed = query.split('!')[1]
for (const bang of bangs) {
if (bang.t == inputed) return {found: true, valid: true, inputed: inputed, data: bang}
}
return {found: true, valid: false, inputed: inputed}
}
var lastquery = ''
const proceed = query => {
if (query == lastquery) return
lastquery = query
document.querySelector('#clock').style.opacity = query == '' ? 1 : 0
document.querySelector('#searcher').style.opacity = query == '' ? 0 : 1
const fb = findbang(query)
if (fb.found) query = query.replace('!'+fb.inputed, `<span class="${fb.valid? '' : 'error'}">!${fb.inputed}</span>`)
console.log(query)
document.querySelector('#searcher > h1').innerHTML = query
document.querySelector('#searcher > p').innerHTML = `Search via ${fb.valid? fb.data.s : 'Google'}`
}
const find = element => {
const query = element.target.value
const fb = findbang(query)
const url = fb.valid ? fb.data.u : 'https://www.google.com/search?q={{{s}}}'
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('input', (e) => {proceed(e.target.value.trim())})
setInterval(() => {
document.querySelector('#search_ddd').focus()
proceed(document.querySelector('#search_ddd').value.trim())
}, 100);
}
window.addEventListener('DOMContentLoaded', async () => {
clock()
searcher()
})
setInterval()

33
src/js/storage.js Normal file
View file

@ -0,0 +1,33 @@
const CookieManager = {
get: (cname) => {
let name = cname + "=";
let decodedCookie = decodeURIComponent(document.cookie);
let ca = decodedCookie.split(';');
for(let i = 0; i <ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "";
},
set: (cname, cvalue) => {
const d = new Date();
d.setTime(d.getTime() + (999*24*60*60*1000));
let expires = "expires="+ d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
}
function getFromStorage(variable_name) {
const c = CookieManager.get(variable_name)
return c === '' ? null : c
}
function setToStorage(variable_name, value) {
CookieManager.set(variable_name, value)
}