diff --git a/web/assets/css/about.css b/web/assets/css/about.css new file mode 100755 index 0000000..8d95e00 --- /dev/null +++ b/web/assets/css/about.css @@ -0,0 +1,51 @@ +.service-up::after { content: '●'; } + +.service-down::after { content: '○'; } + +h3:is(.service-up, .service-down)::after { + font-size: 1rem; + margin-right: -.9rem; + vertical-align: text-top; + padding-left: .2rem; } + +.columns { + column-count: 3; + column-fill: balance-all; + column-gap: 1rem; + column-span: none; } + +.columns.figs { column-count: 2; } + +.columns > div { + display: inline-block; + width: 100%; } + +.columns h3 { color: var(--primary-color); } + +figure figcaption { font-size: .8rem; } + +figure.center, +figure figcaption { text-align: center; } + +figure img { width: 100%; } + +figure.center img { width: 60%; } + +#contacts, +#donation { + text-align: center; + width: 100%; + word-wrap: break-word; } + +:is(#contacts, #donation) :is(a, span) { margin: .3rem; } + +#contacts span a { margin: 0; } + +.banners { + display: flex; + flex-wrap: wrap; + justify-content: center; } + +@media screen and (max-width: 640px) { + .columns, + .columns.figs { column-count: 1; } } \ No newline at end of file diff --git a/web/assets/css/articles.css b/web/assets/css/articles.css new file mode 100755 index 0000000..7261a68 --- /dev/null +++ b/web/assets/css/articles.css @@ -0,0 +1,64 @@ +@font-face { + font-family: 'Share Tech Mono'; + font-style: normal; + font-weight: 400; + src: local('ShareTechMono'), local('ShareTechMono-Regular'), + url(/assets/fonts/ShareTechMono-Regular.ttf) format('truetype'); } + + +h3 { + font-size: 1.1rem; + font-variant: normal; + text-align: left; } + +h4, +h5 { + text-indent: 1.5rem; + margin: 1rem 0 1rem 0; } + +:is(h3, h4, h5) a { color: var(--text-color); } + +:is(h3, h4, h5) a:hover { color: var(--primary-color); } + +code, +pre { + font-family: 'Share Tech Mono'; + font-size: 1.1rem; + letter-spacing: -1px; } + +pre { + border-left: 1px solid var(--primary-color); + padding-left: .25rem; + margin: .5rem 0; + overflow: scroll; + white-space: pre-wrap; + word-break: break-all; } + +figure { margin: 1rem 0; } + +figure.center { text-align: center; } + +article { + margin-top: 1rem; + max-width: 100%; } + +article:last-child { margin-bottom: 1rem; } + +article header { + display: flex; + flex-direction: column; } + +article header .menu { + display: flex; + font-size: .8rem; + justify-content: space-between; } + +article header nav a { font-variant: normal; } + +article header nav ol { + counter-reset: item; + list-style-type: none; } + +article header nav ol > li { counter-increment: item; } + +article header nav ol > li:before { content: counters(item, '.') '. '; } \ No newline at end of file diff --git a/web/assets/css/guestbook.css b/web/assets/css/guestbook.css new file mode 100755 index 0000000..6503a67 --- /dev/null +++ b/web/assets/css/guestbook.css @@ -0,0 +1,90 @@ +::placeholder { color: var(--primary-color); } + +small { font-size: .8rem; } + +form { + display: grid; + gap: .5rem; + grid-template-areas: + "n w w" + "m m m" + "c a s"; + grid-template-columns: 1fr 1fr 1fr; } + +:is(input, textarea):focus { + outline-color: var(--primary-color); + outline-style: solid; + outline-width: 1px; } + +input[name="name"] { grid-area: n; } + +input[name="website"] { grid-area: w; } + +textarea { + grid-area: m; + height: 5.5rem; + max-width: 100%; + min-width: 100%; } + +input[type="text"], +textarea { + background-color: var(--background-color); + border: none; + border-bottom: 1px solid var(--primary-color); + color: var(--text-color); + font: inherit; } + +.checkboxes { grid-area: c; } + +label { padding-left: .5rem; } + +button.refresh:disabled { + color: gray; + cursor: progress; } + +.captcha { + align-items: center; + column-gap: .5rem; + display: flex; + grid-area: a; + justify-content: space-evenly; } + +.captcha img { + height: 40px; + width: 160px; } + +.captcha input[type="text"] { + height: 27px; + text-align: center; + width: 4.5rem; } + +input[type="submit"] { + background-color: var(--primary-color); + border: none; + color: #f5f5f5; + font: inherit; + grid-area: s; + height: 2rem; } + +input[type="submit"]:hover { + background-color: var(--secondary-color); + cursor: pointer; } + +article:not(:last-child) { margin-bottom: 1rem; } + +article header { + display: inline; + font-size: .85rem; } + +article > *, +article div.reply > * { margin-left: .5rem; } + +article p.quote { font-style: italic; } + +#pagination a:not(:first-child) { margin-left: .5rem; } + +@media screen and (max-width: 641px) { + form { + gap: 1.5rem; + display: flex; + flex-direction: column; } } \ No newline at end of file diff --git a/web/assets/css/index.css b/web/assets/css/index.css new file mode 100755 index 0000000..60250a7 --- /dev/null +++ b/web/assets/css/index.css @@ -0,0 +1,22 @@ +body { + height: 75vh; + margin-top: 25vh; } + +header { position: relative; } + +#logo { width: 100%; } + +nav, +#services { + text-align: center; + width: 100%; } + +nav a:last-child { margin-left: .6rem; } + + +#services :is(a, span) { margin: .3rem; } + +#services span a { margin: 0; } + +@media screen and (max-width: 640px) { + #services { display: flex; flex-direction: column; } } \ No newline at end of file diff --git a/web/assets/css/main.css b/web/assets/css/main.css new file mode 100755 index 0000000..a962ce0 --- /dev/null +++ b/web/assets/css/main.css @@ -0,0 +1,156 @@ +@font-face { + font-family: 'Roboto Condensed'; + font-style: normal; + font-weight: 400; + src: local('RobotoCondensed'), local('RobotoCondensed-Regular'), + url(/assets/fonts/RobotoCondensed-Regular.ttf); } + +:root { + --background-color: #0a0a0a; + --background-image: url('/assets/img/alpha1918_z.webp'); + --primary-color: #cd2682; + --secondary-color: #9f2b68; + --text-color: #f5f5f5; + --text-indent: 1.6rem; + scrollbar-color: var(--primary-color) var(--background-color); } + +@media (prefers-color-scheme: light) { + :root { + --background-color: #f5f5f5; + --primary-color: #9f2b68; + --secondary-color: #cd2682; + --text-color: #0a0a0a; } } + +* { margin: 0; } + +::selection { + background-color: var(--secondary-color); + color: var(--background-color); } + +a, +button { + color: var(--primary-color); + text-decoration: none; + word-wrap: break-word; } + +:is(a, button):hover { + color: var(--secondary-color); + text-decoration: underline dotted; + cursor: pointer; + transition: .5s; } + +button { + background: none; + border: none; + font: inherit; + padding: 0; } + +p { + text-align: justify; + line-height: var(--text-indent); + text-indent: var(--text-indent); } + +p:not(:last-child) { margin-bottom: .1rem; } + +p.center { + text-align: center; + text-indent: 0; } + +h1 { + font-size: 1.8rem; + text-align: center; + color: var(--secondary-color); + margin: 0; } + +h1, +h2 { font-variant: small-caps; } + +h2 { font-size: 1.4rem; } + +h2, +h3 { + text-align: center; + margin: 1rem; } + +h3 { font-size: 1.05rem; } + + +table { + border-collapse: collapse; + margin-bottom: .5rem; } + +tr :is(th, td) { + text-align: left; + vertical-align: top; } + +tr :is(th, td):not(:last-child) { padding-right: .5rem; } + +.highlighted { color: var(--primary-color); } + +html { + margin-left: calc(100vw - 100%); + margin-right: 0; } + +body { + background-color: var(--background-color); + color: var(--text-color); + font-family: 'Roboto Condensed', Roboto, sans-serif; + font-size: 1.1rem; + margin: 0 auto; + max-width: 960px; + width: 98%; } + +@media screen and (min-width: 1280px) { + body { + background-image: var(--background-image); + background-size: min(calc((100vw - 960px)/2), 25vw); + background-repeat: no-repeat; + background-position: bottom left; + } +} + +header { + display: flex; + flex-wrap: wrap; + justify-content: space-between; } + +#logo { + display: block; + width: 360px; } + +#logo text { fill: var(--text-color); } + +#logo .logo { + font-size: 2rem; + font-variant-caps: small-caps; + font-weight: bold; } + +@media screen and (-webkit-min-device-pixel-ratio:0) { + #logo .logo { font-size: 2.082rem; } } + +@-moz-document url-prefix() { + #logo .logo { font-size: 2rem; } } + +#logo .under { font-size: .88rem; } + +nav { margin-top: .5rem; } + +nav a { font-variant: small-caps; } + +nav a:not(:first-child) { margin-left: .6rem; } + +section { + margin-top: 1rem; + max-width: 100%; } + +footer { + font-size: .8rem; + text-align: center; + padding: 1rem 0; } + +@media screen and (max-width: 640px) { + body > header { display: block; } + + #logo { width: 100%; } + + body > header nav { text-align: center; } } \ No newline at end of file diff --git a/web/assets/css/mindflow.css b/web/assets/css/mindflow.css new file mode 100755 index 0000000..fc61cf3 --- /dev/null +++ b/web/assets/css/mindflow.css @@ -0,0 +1,9 @@ +.hidden { display: none; } + +button:not(:last-child) { padding-right: 1rem; } + +article header a { color: var(--text-color); } + +article footer { + text-align: right; + padding: 0; } \ No newline at end of file diff --git a/web/assets/fonts/ShareTechMono-Regular.ttf b/web/assets/fonts/ShareTechMono-Regular.ttf new file mode 100755 index 0000000..c8e530f Binary files /dev/null and b/web/assets/fonts/ShareTechMono-Regular.ttf differ diff --git a/web/assets/img/acer.webp b/web/assets/img/acer.webp new file mode 100755 index 0000000..e83f1f2 Binary files /dev/null and b/web/assets/img/acer.webp differ diff --git a/web/assets/img/acer_thumb.webp b/web/assets/img/acer_thumb.webp new file mode 100755 index 0000000..5702c16 Binary files /dev/null and b/web/assets/img/acer_thumb.webp differ diff --git a/web/assets/img/alpha1918_z.webp b/web/assets/img/alpha1918_z.webp new file mode 100755 index 0000000..b3fa176 Binary files /dev/null and b/web/assets/img/alpha1918_z.webp differ diff --git a/web/assets/img/articles/mikrotik_please_just_dont.jpg b/web/assets/img/articles/mikrotik_please_just_dont.jpg new file mode 100755 index 0000000..c948efb Binary files /dev/null and b/web/assets/img/articles/mikrotik_please_just_dont.jpg differ diff --git a/web/assets/img/banner_240x60.gif b/web/assets/img/banner_240x60.gif new file mode 100755 index 0000000..86420f8 Binary files /dev/null and b/web/assets/img/banner_240x60.gif differ diff --git a/web/assets/img/banner_88x31.gif b/web/assets/img/banner_88x31.gif new file mode 100755 index 0000000..31bd091 Binary files /dev/null and b/web/assets/img/banner_88x31.gif differ diff --git a/web/assets/img/banner_dark_240x60.gif b/web/assets/img/banner_dark_240x60.gif new file mode 100755 index 0000000..4e7f9d6 Binary files /dev/null and b/web/assets/img/banner_dark_240x60.gif differ diff --git a/web/assets/img/banner_dark_88x31.gif b/web/assets/img/banner_dark_88x31.gif new file mode 100755 index 0000000..7211523 Binary files /dev/null and b/web/assets/img/banner_dark_88x31.gif differ diff --git a/web/assets/img/favicon.ico b/web/assets/img/favicon.ico new file mode 100755 index 0000000..2fef958 Binary files /dev/null and b/web/assets/img/favicon.ico differ diff --git a/web/assets/img/favicon.svg b/web/assets/img/favicon.svg new file mode 100755 index 0000000..3386c7f --- /dev/null +++ b/web/assets/img/favicon.svg @@ -0,0 +1,2 @@ + + diff --git a/web/assets/img/favicon_128.png b/web/assets/img/favicon_128.png new file mode 100755 index 0000000..c7d066e Binary files /dev/null and b/web/assets/img/favicon_128.png differ diff --git a/web/assets/img/my_cat.webp b/web/assets/img/my_cat.webp new file mode 100755 index 0000000..3874fba Binary files /dev/null and b/web/assets/img/my_cat.webp differ diff --git a/web/assets/img/my_cat_2.webp b/web/assets/img/my_cat_2.webp new file mode 100755 index 0000000..5158bb6 Binary files /dev/null and b/web/assets/img/my_cat_2.webp differ diff --git a/web/assets/img/my_cat_2_thumb.webp b/web/assets/img/my_cat_2_thumb.webp new file mode 100755 index 0000000..ff19f19 Binary files /dev/null and b/web/assets/img/my_cat_2_thumb.webp differ diff --git a/web/assets/img/my_cat_thumb.webp b/web/assets/img/my_cat_thumb.webp new file mode 100755 index 0000000..f42049f Binary files /dev/null and b/web/assets/img/my_cat_thumb.webp differ diff --git a/web/assets/img/raspi.webp b/web/assets/img/raspi.webp new file mode 100755 index 0000000..dc114b6 Binary files /dev/null and b/web/assets/img/raspi.webp differ diff --git a/web/assets/img/raspi_thumb.webp b/web/assets/img/raspi_thumb.webp new file mode 100755 index 0000000..ceef936 Binary files /dev/null and b/web/assets/img/raspi_thumb.webp differ diff --git a/web/assets/img/ts3_banner_960.png b/web/assets/img/ts3_banner_960.png new file mode 100755 index 0000000..51a1355 Binary files /dev/null and b/web/assets/img/ts3_banner_960.png differ diff --git a/web/assets/js/captcha_refresh.js b/web/assets/js/captcha_refresh.js new file mode 100755 index 0000000..a7cb54a --- /dev/null +++ b/web/assets/js/captcha_refresh.js @@ -0,0 +1,40 @@ +const g_captcha_timeout_seconds = 600; + +const e_captcha = document.getElementsByClassName("captcha")[0]; + +let g_captcha_timeout_remain = g_captcha_timeout_seconds; +let g_current_captcha_id = e_captcha.children[0].value; + +async function getNewCaptcha() { + const id = await fetch("/api/captcha/", { method: "POST"}).then(r => r.text()); + g_current_captcha_id = e_captcha.children[0].value = id; + const style = window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"; + setTimeout(() => { e_captcha.children[1].src = `/api/captcha/${id}/image?style=${style}` }, 600); + g_captcha_timeout_remain = g_captcha_timeout_seconds; +} + +e_captcha.children[3].innerHTML = + `ed in 600 seconds.`; + +const captcha_refresh = document.getElementById("refresh"); +const captcha_remain = document.getElementById("remain"); + +captcha_refresh.classList.toggle("refresh"); +captcha_refresh.addEventListener("click", async e => { + e.preventDefault(); + e.target.disabled = true; + setTimeout(async () => { + e.target.disabled = false; + await fetch(`/api/captcha/${g_current_captcha_id}?remove`); }, 3000); + e_captcha.children[2].value = ""; + await getNewCaptcha(); +}); + +// Remove unused CAPTCHA on a server. +window.addEventListener("unload", () => fetch(`/api/captcha/${g_current_captcha_id}?remove`)); + +setInterval(async () => { + captcha_remain.innerText = --g_captcha_timeout_remain; + if (g_captcha_timeout_remain == 0) + await getNewCaptcha(); +}, 1000); \ No newline at end of file diff --git a/web/assets/js/mindflow.js b/web/assets/js/mindflow.js new file mode 100755 index 0000000..9d33f8d --- /dev/null +++ b/web/assets/js/mindflow.js @@ -0,0 +1,12 @@ +const articles = document.getElementsByTagName("article"); + +document.getElementById("filter").classList.remove("hidden"); + +function filter(e) { + for (const a of articles) + a.classList.toggle("hidden", + !(e.target.name === "all" || a.id.startsWith(e.target.name))); +} + +for (const b of document.getElementsByTagName("button")) + b.addEventListener("click", filter); \ No newline at end of file