Diffstat (limited to 'views')
| -rw-r--r-- | views/index.html | 145 | ||||
| -rw-r--r-- | views/layouts/general-mail.html | 124 | ||||
| -rw-r--r-- | views/layouts/general-page.html | 948 | ||||
| -rw-r--r-- | views/messages/new-post.html | 19 | ||||
| -rw-r--r-- | views/messages/verify-email.html | 10 | ||||
| -rw-r--r-- | views/pages/blog-catalogue.html | 193 | ||||
| -rw-r--r-- | views/pages/blog-page.html | 31 | ||||
| -rw-r--r-- | views/pages/global-map.html | 603 | ||||
| -rw-r--r-- | views/pages/home-page.html | 62 | ||||
| -rw-r--r-- | views/pages/language-pick.html | 16 | ||||
| -rw-r--r-- | views/pages/robots.txt | 7 | ||||
| -rw-r--r-- | views/pages/sitemap.xml | 10 | ||||
| -rw-r--r-- | views/pages/unsubscribe-page.html | 77 | ||||
| -rw-r--r-- | views/pages/user-page.html | 206 | ||||
| -rw-r--r-- | views/partials/blog-page-like-button.html | 9 | ||||
| -rw-r--r-- | views/partials/catalogue-blog-cards.html | 42 | ||||
| -rw-r--r-- | views/partials/general-page-body.html | 1 | ||||
| -rw-r--r-- | views/partials/general-page-footer.html | 26 | ||||
| -rw-r--r-- | views/partials/general-page-header.html | 17 | ||||
| -rw-r--r-- | views/partials/personal-page-status.html | 12 |
20 files changed, 1747 insertions, 811 deletions
diff --git a/views/index.html b/views/index.html deleted file mode 100644 index 2633894..0000000 --- a/views/index.html +++ /dev/null @@ -1,145 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>Choose Language // SAYA TODAY</title> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"> - <link rel="preconnect" href="https://fonts.googleapis.com"> - <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> - <link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,200;1,300;1,400;1,500;1,600;1,700;1,800&family=Patua+One&display=swap" rel="stylesheet"> - <link href="/output.css" rel="stylesheet"> - <style> - .menu-outer-body { - border-radius: 10%; - width: 40dvh; - height: 30dvh; - } - - .menu-inner-body { - width: 85%; - } - - .flex-adaptive { - flex-direction: row; - } - - .language-icon { - max-width: calc(80% / {{ .AvailableLanguages | len }}); - max-height: 80%; - } - - @media (max-aspect-ratio: 0.8/1) { - .menu-outer-body { - border-radius: 5%; - } - - .menu-outer-body { - width: min(40dvh, 100dvw); - height: 40dvh; - border-radius: 0; - } - - .language-icon { - max-width: 40%; - max-height: calc(80% / {{ .AvailableLanguages | len }}); - } - - .menu-inner-body { - width: 92.5%; - } - - .flex-adaptive { - flex-direction: column; - } - } - </style> -</head> - -<body data-theme="night" class="font-spectral text-main-dark text-2xl overflow-hidden bg-interlocked-hexagons h-[100dvh] flex flex-col"> - - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - <div class="absolute star z-0">*</div> - - <script> - function switchTheme(theme) { - if (theme == "" || typeof theme == "undefined") { - const currentTheme = document.body.getAttribute('data-theme'); - switch (currentTheme) { - case "olive": theme = "lettuce"; break; - case "lettuce": theme = "night"; break; - case "night": theme = "olive"; break; - } - } - // localStorage.setItem('theme', theme); - document.body.setAttribute('data-theme', theme); - } - - const savedTheme = 'night'; - switchTheme(savedTheme); - </script> - - <div class="menu-outer-body p-2 z-1 bg-background-dark flex flex-col mx-auto my-auto opacity-70"> - <p class="font-patua mx-auto text-4xl mt-1 mb-2">saya.today</p> - <div class="menu-inner-body grow bg-background-light flex flex-adaptive mx-auto opacity-100"> - {{- range .AvailableLanguages }} - <img onclick="location.href='/{{ .Name }}';" class="object-scale-down cursor-pointer flex-1 rounded-[20%] language-icon mx-auto my-auto relative" src="{{ .Flag }}" alt="{{ .Alt }}"> - {{- end }} - </div> - <p class="font-spectral mx-auto text-2xl text-secondary mt-1 mb-2">Choose your language</p> - </div> - - <script> - function generateStars() { - var clWidth = document.documentElement.clientWidth; - var clHeight = document.documentElement.clientHeight; - - stars = document.getElementsByClassName("star"); - for (let starIndex = 0; starIndex < stars.length; starIndex++) { - const star = stars[starIndex]; - const x = Math.floor(Math.random() * clWidth); - const y = Math.floor(Math.random() * clHeight * 0.8); - const rot = Math.random() * 90; - const animDuration = (Math.random() * 10) + 3; - const sz = Math.floor(Math.random() * 8) + 12; - star.style.transform = `translateX(${x}px) translateY(${y}px) rotate(${rot}deg)`; - star.style.animationDuration = `${animDuration}s`; - star.style.fontSize = `${sz}px`; - } - } - - document.addEventListener('DOMContentLoaded', generateStars); - - let generateStarsTimer; - window.addEventListener("resize", () => { - clearTimeout(generateStarsTimer); - generateStarsTimer = setTimeout(() => { - generateStars(); - }, 1000); - }); - </script> -</body> -</html> diff --git a/views/layouts/general-mail.html b/views/layouts/general-mail.html new file mode 100644 index 0000000..452982a --- /dev/null +++ b/views/layouts/general-mail.html @@ -0,0 +1,124 @@ +<!doctype html> +<html lang="en"> + <meta charset="UTF-8" /> + <head> + <style> + @font-face { + font-family: "Gentium Plus"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: + local("Gentium Plus"), + url(https://fonts.gstatic.com/s/gentiumplus/v2/Iurd6Ytw-oSPaZ00r2bNe8VZj5FNym499r7e.woff2) + format("woff2"); + unicode-range: + U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; + } + @font-face { + font-family: "Gentium Plus"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: + local("Gentium Plus"), + url(https://fonts.gstatic.com/s/gentiumplus/v2/Iurd6Ytw-oSPaZ00r2bNe8VZi5FNym499g.woff2) + format("woff2"); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, + U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, + U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + } + html { + font-family: "Gentium Plus", sans-serif; + --bg-paper: url(data:image/webp;base64,UklGRi4BAABXRUJQVlA4ICIBAABQCQCdASqAAIAAPm02lUmkIqIkIImIgA2JaW3rMFmGDAOKLAoJtP591ByFR74Tun2DP+Lygpfsz5FH7+cKgxi2Islfd03i5AJdXUfU0SDc8SXYAAD++hlzeYAJWQEF8ZdkJdlcA+5btZg0FMGaso8SHoBph0qAOQysElJ5C5SJFNoKIV1J550UyL7Y9Gh0/c/uzLnBmr8lvz7k1WeA81H4S5ViU/wBqa7aM1m8D9rDtrk8cSTqx+xWF6MS8UfZGSsks6CT/igtgv6syFN9uNA3LjnjiCJKYOvFcMkX+jB2578GWVo3nyLaM1QNPYCxn3TiUbnf7U0MdD2NsBpg1+Jd1wu6yW/U+goN+IQS6Nt4RMWltVcZHlxrneKJi0SobO0AAA==); + } + body { + background-color: #e3e6ff; + } + a { + text-decoration: underline; + text-shadow: none !important; + } + .header { + width: 100%; + padding: 8px 8px 8px 32px; + font-size: 48px; + color: white; + text-shadow: + -2px 0 #343b58, + 0 2px #343b58, + 2px 0 #343b58, + 0 -2px #343b58; + background: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='40' height='80'><path fill='#e5e8ff' d='M0 0h40v80H0z'/><g fill='none' stroke-linecap='square' stroke-width='4.5'><path stroke='#8d4453' d='m-10 5 20 10L30 5l20 10'/><path stroke='#375e0d' d='m-10 25 20 10 20-10 20 10'/><path stroke='#006a85' d='m-10 45 20 10 20-10 20 10'/><path stroke='#5a3e8e' d='m-10 65 20 10 20-10 20 10'/></g></svg>"); + background-color: #e3e6ff; + background-size: contain; + background-repeat: repeat-x; + } + .content { + margin: 0 32px 0 32px; + padding: 16px 16px 32px 16px; + font-size: 24px; + text-shadow: + -1px 0 #343b58, + 0 1px #343b58, + 1px 0 #343b58, + 0 -1px #343b58; + color: white; + background-color: #c3caff; + background-image: var(--bg-paper); + background-blend-mode: multiply; + background-repeat: repeat; + background-size: 128px; + } + .footer { + margin: 0; + padding: 1px 8px 1px 8px; + font-size: 16px; + color: white; + } + .darkened { + background-color: #9aa5f4; + background-image: var(--bg-paper); + background-blend-mode: multiply; + background-repeat: repeat; + background-size: 128px; + } + .outlier { + padding-inline: 4px; + background-color: #6c6e75; + background-image: var(--bg-paper); + background-blend-mode: multiply; + background-repeat: repeat; + background-size: 128px; + } + table, + th, + td { + border: 1px solid black; + } + td { + padding: 5px; + } + img { + width: 200px; + } + </style> + </head> + + <body> + <div id="header" class="header">saya.uz</div> + + <div + id="content" + class="content" + style="background-color: #e3e6ff !important" + > + {{ block "body" . }}{{ end }} + </div> + + <div id="footer" class="footer darkened"> + {{ block "footer" . }}{{ end }} + </div> + </body> +</html> diff --git a/views/layouts/general-page.html b/views/layouts/general-page.html index d874ecf..0bd3454 100644 --- a/views/layouts/general-page.html +++ b/views/layouts/general-page.html @@ -1,375 +1,725 @@ -<!DOCTYPE html> +<!doctype html> <html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"> - <link rel="preconnect" href="https://fonts.googleapis.com"> - <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> - <link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,200;1,300;1,400;1,500;1,600;1,700;1,800&family=Patua+One&display=swap" rel="stylesheet"> - <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.css"> - <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/glightbox/3.3.0/css/glightbox.min.css"> - <link href="/output.css" rel="stylesheet"> -</head> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + {{- range .Meta }} + <meta + {{with + .Name}}name="{{.}}" + {{end}}{{with + .Property}}property="{{.}}" + {{end}}{{with + .Content}}content="{{.}}" + {{end}} + /> + {{- if eq .Property "og:description" }} + <meta name="description" content="{{.Content}}" /> + {{- end }} {{- if eq .Property "og:url" }} + <link rel="canonical" href="{{.Content}}" /> + {{- end }} {{- end }} + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/fonts/fonts.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/fontawesome.min.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/brands.min.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/solid.min.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/glightbox/3.3.0/css/glightbox.min.css" + /> + <link rel="stylesheet" href="/output.css" /> + <link + rel="icon" + href="/favicon-light.svg" + type="image/svg+xml" + media="(prefers-color-scheme: light)" + /> + <link + rel="icon" + href="/favicon-dark.svg" + type="image/svg+xml" + media="(prefers-color-scheme: dark)" + /> + {{- with .LinkedData }} + <script type="application/ld+json"> + {{ . }} + </script> + {{- end }} + </head> -<body data-theme="ram" class="font-spectral text-main-dark overflow-hidden bg-interlocked-hexagons h-[100dvh] flex flex-row ar-lt-0.8:flex-col"> + <body + data-theme="ram" + class="font-m-plus text-main-hard overflow-hidden bg-interlocked-hexagons h-dvh flex flex-row" + > + <script> + let map = null; + </script> - <script> - let map = null; - </script> + <script> + function changeUrl(path, toAppend = false) { + const urlParams = new URLSearchParams(window.location.search); + newPathname = location.pathname.endsWith("/") + ? location.pathname.slice(0, -1) + : location.pathname; + while (path.slice(-3) == "/.." && toAppend) { + newPathname = newPathname.replace(/\/[^\/]+$/, ""); + path = path.slice(0, -3); + } + const newPath = toAppend ? `${newPathname}${path}` : `${path}`; - <script> - function changeUrl(path, toAppend = false) { - const urlParams = new URLSearchParams(window.location.search); - const newPathname = location.pathname.endsWith('/') ? location.pathname.slice(0, -1) : location.pathname; - const newPath = toAppend ? `${newPathname}${path}` : `${path}`; - - history.pushState({}, '', `${newPath}${urlParams.entries.length == 0 ? '' : '?' + urlParams.toString()}`); - if (location.pathname == "" || location.pathname == "/") { - return true; + history.pushState( + {}, + "", + `${newPath}${urlParams.entries.length == 0 ? "" : "?" + urlParams.toString()}`, + ); + window.dispatchEvent(new Event("popstate")); + return false; } - window.dispatchEvent(new Event('popstate')); - return false; - }; - function convertRemToPixels(rem) { - return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); - } - </script> + function convertRemToPixels(rem) { + return ( + rem * + parseFloat( + getComputedStyle(document.documentElement).fontSize, + ) + ); + } + </script> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/glightbox/3.3.0/js/glightbox.min.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/glightbox/3.3.0/js/glightbox.min.js"></script> - <script> - function sfc32(a, b, c, d) { - return function() { - a |= 0; b |= 0; c |= 0; d |= 0; - let t = (a + b | 0) + d | 0; - d = d + 1 | 0; - a = b ^ b >>> 9; - b = c + (c << 3) | 0; - c = (c << 21 | c >>> 11); - c = c + t | 0; - return (t >>> 0) / 4294967296; + <script> + function sfc32(a, b, c, d) { + return function () { + a |= 0; + b |= 0; + c |= 0; + d |= 0; + let t = (((a + b) | 0) + d) | 0; + d = (d + 1) | 0; + a = b ^ (b >>> 9); + b = (c + (c << 3)) | 0; + c = (c << 21) | (c >>> 11); + c = (c + t) | 0; + return (t >>> 0) / 4294967296; + }; } - } - function cyrb128(str) { - let h1 = 1779033703, h2 = 3144134277, - h3 = 1013904242, h4 = 2773480762; - for (let i = 0, k; i < str.length; i++) { - k = str.charCodeAt(i); - h1 = h2 ^ Math.imul(h1 ^ k, 597399067); - h2 = h3 ^ Math.imul(h2 ^ k, 2869860233); - h3 = h4 ^ Math.imul(h3 ^ k, 951274213); - h4 = h1 ^ Math.imul(h4 ^ k, 2716044179); + function cyrb128(str) { + let h1 = 1779033703, + h2 = 3144134277, + h3 = 1013904242, + h4 = 2773480762; + for (let i = 0, k; i < str.length; i++) { + k = str.charCodeAt(i); + h1 = h2 ^ Math.imul(h1 ^ k, 597399067); + h2 = h3 ^ Math.imul(h2 ^ k, 2869860233); + h3 = h4 ^ Math.imul(h3 ^ k, 951274213); + h4 = h1 ^ Math.imul(h4 ^ k, 2716044179); + } + h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067); + h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233); + h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213); + h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179); + ((h1 ^= h2 ^ h3 ^ h4), (h2 ^= h1), (h3 ^= h1), (h4 ^= h1)); + return [h1 >>> 0, h2 >>> 0, h3 >>> 0, h4 >>> 0]; } - h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067); - h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233); - h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213); - h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179); - h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1; - return [h1>>>0, h2>>>0, h3>>>0, h4>>>0]; - } - var seed = cyrb128("{{ .Title }}"); - var rand = sfc32(seed[0], seed[1], seed[2], seed[3]); + var seed = cyrb128("{{ .Title }}"); + var rand = sfc32(seed[0], seed[1], seed[2], seed[3]); - function calculateVmin(percent) { - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; + function calculateVmin(percent) { + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; - const smallerDimension = Math.min(viewportWidth, viewportHeight); + const smallerDimension = Math.min( + viewportWidth, + viewportHeight, + ); - const vminValue = (smallerDimension / 100) * percent; + const vminValue = (smallerDimension / 100) * percent; - return vminValue; - } + return vminValue; + } - function switchThemeDecor(theme) { - document.querySelectorAll('.theme-decor').forEach((e) => { - e.remove(); - }); + function switchThemeDecor(theme) { + document.querySelectorAll(".theme-decor").forEach((e) => { + e.remove(); + }); - if (theme == "" || typeof theme == "undefined") { - theme = document.body.getAttribute('data-theme'); - } + if (theme == "" || typeof theme == "undefined") { + theme = document.body.getAttribute("data-theme"); + } - vmin = calculateVmin(1); - var clWidth = document.documentElement.clientWidth; - var clHeight = document.documentElement.clientHeight; + vmin = calculateVmin(1); + var clWidth = document.documentElement.clientWidth; + var clHeight = document.documentElement.clientHeight; - switch (theme) { - case "olive": break; - case "lettuce": break; - case "night": - for (i = 0; i < 25; i++) { - let star = document.createElement("div"); - star.append("*"); - star.classList.add("night-star", "theme-decor", "z-0", "absolute"); - const x = Math.floor(rand() * clWidth); - const y = Math.floor(rand() * clHeight * 0.8); - const rot = rand() * 90; - const animDuration = (rand() * 10) + 3; - const sz = Math.floor(rand() * 8) + 12; - star.style.transform = `translateX(${x}px) translateY(${y}px) rotate(${rot}deg)`; - star.style.animationDuration = `${animDuration}s`; - star.style.fontSize = `${sz}px`; - document.body.append(star); - } - break; - case "ram": - const frameCenters = new Array(4); - for (i = 1; i <= 4; i++) { - let frame = document.createElement("img"); - frame.src = `https://f003.backblazeb2.com/file/sayana-static/themes/ram/ram-frame${i}.webp`; - frame.classList.add("ram-frame", "theme-decor", "z-0", "absolute"); - let x, y; - outerLoop: - while (true) { - x = Math.floor(rand() * clWidth - 20*vmin); - y = Math.floor(rand() * clHeight - 20*vmin); - for (j = 0; j < i-1; j++) { - const xd = frameCenters[j].x - x; - const yd = frameCenters[j].y - y; - if (Math.sqrt(xd*xd + yd*yd) < 30*vmin) { - continue outerLoop; + switch (theme) { + case "olive": + break; + case "lettuce": + break; + case "night": + for (i = 0; i < 25; i++) { + let star = document.createElement("div"); + star.append("*"); + star.classList.add( + "night-star", + "theme-decor", + "z-0", + "absolute", + ); + const x = Math.floor(rand() * clWidth); + const y = Math.floor(rand() * clHeight * 0.8); + const rot = rand() * 90; + const animDuration = rand() * 10 + 3; + const sz = Math.floor(rand() * 8) + 12; + star.style.transform = `translateX(${x}px) translateY(${y}px) rotate(${rot}deg)`; + star.style.animationDuration = `${animDuration}s`; + star.style.fontSize = `${sz}px`; + document.body.append(star); + } + break; + case "ram": + const frameCenters = new Array(4); + for (i = 1; i <= 4; i++) { + let frame = document.createElement("img"); + frame.src = `{{ .StaticStorageBaseUrl }}/themes/ram/ram-frame${i}.webp`; + frame.classList.add( + "ram-frame", + "theme-decor", + "z-0", + "absolute", + ); + let x, y; + outerLoop: while (true) { + x = Math.floor(rand() * clWidth - 20 * vmin); + y = Math.floor(rand() * clHeight - 20 * vmin); + for (j = 0; j < i - 1; j++) { + const xd = frameCenters[j].x - x; + const yd = frameCenters[j].y - y; + if ( + Math.sqrt(xd * xd + yd * yd) < + 30 * vmin + ) { + continue outerLoop; + } } + break; } - break; + frameCenters[i - 1] = { x: x, y: y }; + const rot = rand() * 90 - 45; + frame.style.transform = `translateX(${x}px) translateY(${y}px) rotate(${rot}deg)`; + document.body.append(frame); } - frameCenters[i-1] = {x: x, y: y}; - const rot = rand() * 90 - 45; - frame.style.transform = `translateX(${x}px) translateY(${y}px) rotate(${rot}deg)`; - document.body.append(frame); - } - break; + break; + case "lilac": + break; + } } - } - function switchTheme(theme) { - if (theme == "" || typeof theme == "undefined") { - const currentTheme = document.body.getAttribute('data-theme'); - switch (currentTheme) { - case "olive": theme = "lettuce"; break; - case "lettuce": theme = "night"; break; - case "night": theme = "ram"; break; - case "ram": theme = "olive"; break; + function switchTheme(theme) { + if (theme == "" || typeof theme == "undefined") { + const currentTheme = + document.body.getAttribute("data-theme"); + switch (currentTheme) { + case "olive": + theme = "lettuce"; + break; + case "lettuce": + theme = "night"; + break; + case "night": + theme = "ram"; + break; + case "ram": + theme = "lilac"; + break; + case "lilac": + theme = "olive"; + break; + } } + localStorage.setItem("theme", theme); + document.body.setAttribute("data-theme", theme); + switchThemeDecor(theme); } - localStorage.setItem('theme', theme); - document.body.setAttribute('data-theme', theme); - switchThemeDecor(theme); - } - const savedTheme = localStorage.getItem('theme') || 'ram'; - switchTheme(savedTheme); - </script> + const savedTheme = localStorage.getItem("theme") || "lilac"; + switchTheme(savedTheme); + </script> - <div id="general-page-top-embeds" class="flex" - hx-get="/api/v1/general-page/top-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"> - </div> - <div id="general-page-top-embeds-trigger" hx-get="/api/v1/general-page/top-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="popstate from:window" hx-target="#general-page-top-embeds" hx-swap="innerHTML"></div> + <div + id="general-page-top-embeds" + class="flex" + hx-get="/api/v1/general-page/top-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></div> + <div + id="general-page-top-embeds-trigger" + hx-get="/api/v1/general-page/top-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="popstate from:window" + hx-target="#general-page-top-embeds" + hx-swap="innerHTML" + ></div> - <script> - const clientTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; - </script> + <script> + const clientTimeZone = + Intl.DateTimeFormat().resolvedOptions().timeZone; + </script> - <div id="sidebar" class="bg-sidebar flex sticky flex-col ar-gt-0.8:items-center shadow-2xl z-20 w-[5rem] ar-lt-0.8:w-[100dvw] h-[100dvh] ar-lt-0.8:h-[8dvh]"> - <div class="logo-custom block ar-lt-0.8:hidden [@media(max-height:32rem)]:hidden text-sidebar font-patua font-extrabold text-center relative z-20"> - saya.today - </div> - <div class="text-[3rem] ar-lt-0.8:text-[5dvh] text-stroke-(--sidebar-stroke-color) text-stroke-[0.1dvw] flex flex-col ar-lt-0.8:flex-row gap-0 relative z-20"> - <a href="/{{ .Lang }}" class="w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh]"> - <i onclick="return changeUrl('/{{ .Lang }}');" class="fas fa-home flex w-full h-full content-center text-center text-sidebar hover:bg-background-dark transition-colors duration-300 rounded-lg"></i> - </a> - <a href="/{{ .Lang }}/blog" class="w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh]"> - <i onclick="return changeUrl('/{{ .Lang }}/blog');" class="fas fa-search flex w-full h-full content-center text-center text-sidebar hover:bg-background-dark transition-colors duration-300 rounded-lg"></i> - </a> - <a href="/{{ .Lang }}/map" class="w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh]"> - <i class="fas fa-map flex w-full h-full content-center text-center text-sidebar hover:bg-background-dark transition-colors duration-300 rounded-lg"></i> - </a> - <i onclick="toggleThemeWheel();" id="theme-button" class="fas fa-swatchbook flex w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 rounded-lg"></i> - <a href="./" class="hidden max-xs:block [@media(max-height:32rem)]:block w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh]"> - <i onclick="return changeUrl('/../', true);" class="fas fa-circle-left flex w-full h-full content-center text-center text-sidebar hover:bg-background-dark transition-colors duration-300 rounded-lg"></i> - </a> - </div> - <div class="theme-wheel" id="theme-wheel"> - <div class="theme-icon" data-theme="olive"></div> - <div class="theme-icon" data-theme="lettuce"></div> - <div class="theme-icon" data-theme="night"></div> - <div class="theme-icon" data-theme="ram"></div> - <div class="theme-pin"></div> - </div> - </div> + <div class="grow bg-split-left"></div> - <div class="bg-background-dark @container/main flex z-10 ar-lt-0.8:w-[100dvw] w-[90dvw] xs:w-[85dvw] sm:w-[80dvw] md:w-[75dvw] lg:w-[70dvw] xl:w-[65dvw] 2xl:w-[60dvw] grow-0 h-[100dvh] ar-lt-0.8:max-h-[92dvh]"> - <div class="bg-background-dark border-r-2 border-dashed border-main-dark"></div> + <div class="flex flex-col sm:flex-row"> + <div + id="sidebar" + class="bg-sidebar flex flex-row sticky w-dvw h-fit xs:w-[90dvw] sm:left-0 sm:flex-col sm:fixed sm:w-fit z-20 shadow-[0_0_1rem_black]" + > + <nav + aria-label="Main navigation" + class="text-[3rem] flex flex-row sm:flex-col gap-0 relative z-20" + > + <a + onclick="return changeUrl('/..', true);" + href="../" + id="sidebar-back-button" + class="themed-button themed-button-sidebar w-fit h-fit hidden" + draggable="false" + > + <i + class="fas fa-circle-left flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + onclick="return changeUrl('/{{ .Lang }}');" + href="/{{ .Lang }}" + id="sidebar-home-button" + class="themed-button themed-button-sidebar w-fit h-fit hidden" + draggable="false" + > + <i + class="fas fa-home flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + onclick="return changeUrl('/{{ .Lang }}/blog');" + href="/{{ .Lang }}/blog" + id="sidebar-blog-button" + class="themed-button themed-button-sidebar w-fit h-fit hidden" + draggable="false" + > + <i + class="fas fa-search flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + href="/{{ .Lang }}/map" + id="sidebar-map-button" + class="themed-button themed-button-sidebar w-fit h-fit hidden" + draggable="false" + > + <i + class="fas fa-map flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <div + id="toolbar-theme-button" + onclick="toggleThemeWheel()" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + id="toolbar-theme-button-content" + class="fas fa-swatchbook flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </div> + <a + onclick="return changeUrl('/{{ .Lang }}/user');" + href="/{{ .Lang }}/user" + id="sidebar-user-button" + class="themed-button themed-button-sidebar w-fit h-fit hidden" + draggable="false" + > + <i + class="fas fa-user flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + </nav> + <div class="theme-wheel" id="theme-wheel"> + <div class="theme-icon" data-theme="olive"></div> + <div class="theme-icon" data-theme="lettuce"></div> + <div class="theme-icon" data-theme="night"></div> + <div class="theme-icon" data-theme="ram"></div> + <div class="theme-icon" data-theme="lilac"></div> + <div class="theme-pin"></div> + </div> + <script> + sidebarHomeButton = document.getElementById( + "sidebar-home-button", + ); + sidebarMapButton = + document.getElementById("sidebar-map-button"); + sidebarBlogButton = document.getElementById( + "sidebar-blog-button", + ); + sidebarBackButton = document.getElementById( + "sidebar-back-button", + ); + sidebarUserButton = document.getElementById( + "sidebar-user-button", + ); - <div class="max-h-100% flex flex-col grow bg-background-dark relative pt-4 ml-8 pr-4"> - <div id="general-page-header" class="max-xs:hidden [@media(max-height:32rem)]:hidden flex flex-col basis-8" - hx-get="/api/v1/general-page/header{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"> + function updateSidebar() { + if ( + location.pathname == "" || + location.pathname == "/" + ) { + sidebarHomeButton.classList.add("hidden"); + sidebarMapButton.classList.add("hidden"); + sidebarBlogButton.classList.add("hidden"); + sidebarBackButton.classList.add("hidden"); + sidebarUserButton.classList.add("hidden"); + } else { + sidebarHomeButton.classList.remove("hidden"); + sidebarMapButton.classList.remove("hidden"); + sidebarBlogButton.classList.remove("hidden"); + sidebarBackButton.classList.remove("hidden"); + sidebarUserButton.classList.remove("hidden"); + lang = location.pathname.split("/")[1]; + sidebarHomeButton.setAttribute("href", `/${lang}`); + sidebarHomeButton.setAttribute( + "onclick", + `return changeUrl('/${lang}', false);`, + ); + sidebarMapButton.setAttribute( + "href", + `/${lang}/map`, + ); + sidebarBlogButton.setAttribute( + "href", + `/${lang}/blog`, + ); + sidebarBlogButton.setAttribute( + "onclick", + `return changeUrl('/${lang}/blog', false);`, + ); + sidebarUserButton.setAttribute( + "href", + `/${lang}/user`, + ); + sidebarUserButton.setAttribute( + "onclick", + `return changeUrl('/${lang}/user', false);`, + ); + } + } + window.addEventListener("popstate", updateSidebar); + window.addEventListener("load", updateSidebar, { + once: true, + }); + </script> </div> - <div id="general-page-header-trigger" hx-get="/api/v1/general-page/header{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="popstate from:window" hx-target="#general-page-header" hx-swap="innerHTML"></div> - <hr class="max-xs:hidden [@media(max-height:32rem)]:hidden border-t-4 border-dotted border-main-dark mb-2"> + <div + class="bg-paper bg-background-dark @container/main flex z-10 w-dvw xs:w-[90dvw] sm:w-[85dvw] md:w-[80dvw] lg:w-[75dvw] xl:w-[70dvw] 2xl:w-[60dvw] 3xl:w-[50dvw] grow-0 h-[calc(100dvh-5rem)] sm:h-dvh shadow-[0_0_1rem_black]" + > + <div + class="bg-paper bg-background-dark border-r-2 border-dashed border-main-hard" + ></div> - <div id="general-page-body" class="flex flex-col grow overflow-y-auto" - hx-get="/api/v1/general-page/body{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"> - </div> - <div id="general-page-body-trigger" hx-get="/api/v1/general-page/body{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="popstate from:window" hx-target="#general-page-body" hx-swap="innerHTML"></div> + <div + class="max-h-full max-w-full flex flex-col grow bg-paper bg-background-dark relative pt-4 ml-8 pr-4" + > + <header + id="general-page-header" + class="max-xs:hidden [@media(max-height:32rem)]:hidden flex flex-col basis-8" + hx-get="/api/v1/general-page/header{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></header> + <div + id="general-page-header-trigger" + hx-get="/api/v1/general-page/header{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="popstate from:window" + hx-target="#general-page-header" + hx-swap="innerHTML" + ></div> - <div id="general-page-footer" class="flex flex-row basis-4 my-1" - hx-get="/api/v1/general-page/footer{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"> - </div> - <div id="general-page-footer-trigger" hx-get="/api/v1/general-page/footer{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="popstate from:window" hx-target="#general-page-footer" hx-swap="innerHTML"></div> + <hr + class="max-xs:hidden [@media(max-height:32rem)]:hidden border-t-4 border-dotted border-main-hard mb-2" + /> - <hr class="border-t-4 border-dotted border-main-dark mb-2"> - </div> + <main + id="general-page-body" + class="relative flex flex-col grow overflow-y-auto" + hx-get="/api/v1/general-page/body{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></main> + <div + id="general-page-body-trigger" + hx-get="/api/v1/general-page/body{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="popstate from:window" + hx-target="#general-page-body" + hx-swap="innerHTML" + ></div> - <div class="pr-[0.5dvw] bg-background-dark shadow-2xl border-l-2 border-dotted border-main-dark"></div> - </div> + <footer + id="general-page-footer" + class="flex flex-row basis-4 my-1" + hx-get="/api/v1/general-page/footer{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></footer> + <div + id="general-page-footer-trigger" + hx-get="/api/v1/general-page/footer{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="popstate from:window" + hx-target="#general-page-footer" + hx-swap="innerHTML" + ></div> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/htmx/2.0.6/htmx.min.js"></script> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.js"></script> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/imagesloaded/5.0.0/imagesloaded.pkgd.min.js"></script> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/masonry-layout/4.2.2/masonry.pkgd.min.js"></script> + <hr + class="border-t-4 border-dotted border-main-hard mb-2" + /> + </div> - <div id="general-page-bottom-embeds" class="flex" - hx-get="/api/v1/general-page/bottom-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="load" hx-target="this" hx-swap="innerHTML"> - </div> - <div id="general-page-bottom-embeds-trigger" hx-get="/api/v1/general-page/bottom-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" hx-trigger="popstate from:window" hx-target="#general-page-bottom-embeds" hx-swap="innerHTML"></div> + <div + class="pr-[0.5dvw] bg-paper bg-background-dark shadow-2xl border-l-2 border-dotted border-main-hard" + ></div> + </div> + </div> - <script> - const themeWheel = document.getElementById('theme-wheel'); - const themeButton = document.getElementById('theme-button'); - const themeIcons = document.querySelectorAll('.theme-icon'); - const themeWheelRadius = convertRemToPixels(6); + <div class="grow bg-split-right"></div> - themeIcons.forEach(icon => { - if (icon.getAttribute('data-theme') == savedTheme) { - icon.classList.add('active'); - } - }); + <script src="{{ .StaticStorageBaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/imagesloaded/5.0.0/imagesloaded.pkgd.min.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/packery/3.0.0/dist/packery.pkgd.min.js"></script> - function calculateVmax(percent) { - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; + <script> + // Enable fitWidth-like attribute for Packery. Source: https://codepen.io/desandro/pen/kVmLYz + var __resetLayout = Packery.prototype._resetLayout; + Packery.prototype._resetLayout = function () { + __resetLayout.call(this); + var parentSize = getSize(this.element.parentNode); + var colW = this.columnWidth + this.gutter; + this.fitWidth = + Math.floor((parentSize.innerWidth + this.gutter) / colW) * + colW; + this.packer.width = this.fitWidth; + this.packer.height = Number.POSITIVE_INFINITY; + this.packer.reset(); + }; - const largerDimension = Math.max(viewportWidth, viewportHeight); + Packery.prototype._getContainerSize = function () { + var emptyWidth = 0; + for (var i = 0, len = this.packer.spaces.length; i < len; i++) { + var space = this.packer.spaces[i]; + if ( + space.y === 0 && + space.height === Number.POSITIVE_INFINITY + ) { + emptyWidth += space.width; + } + } - const vmaxValue = (largerDimension / 100) * percent; + return { + width: this.fitWidth - this.gutter - emptyWidth, + height: this.maxY - this.gutter, + }; + }; - return vmaxValue; - } + Packery.prototype.needsResizeLayout = function () { + return true; + }; + </script> - function positionThemeIcons() { - const sliceAngle = 360 / themeIcons.length; + <div + id="general-page-bottom-embeds" + class="flex" + hx-get="/api/v1/general-page/bottom-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="load" + hx-target="this" + hx-swap="innerHTML" + ></div> + <div + id="general-page-bottom-embeds-trigger" + hx-get="/api/v1/general-page/bottom-embeds{{ if .QueryString }}?{{ .QueryString }}{{ end }}" + hx-trigger="popstate from:window" + hx-target="#general-page-bottom-embeds" + hx-swap="innerHTML" + ></div> - themeIcons.forEach((icon, index) => { - const startAngle = index * sliceAngle; - const endAngle = (index + 1) * sliceAngle; + <script> + const themeWheel = document.getElementById("theme-wheel"); + const themeButton = document.getElementById("toolbar-theme-button"); + const themeIcons = document.querySelectorAll(".theme-icon"); + const themeWheelRadius = convertRemToPixels(6); - const clipPath = createSliceClipPath(startAngle, endAngle, themeWheelRadius); - icon.style.clipPath = clipPath; + themeIcons.forEach((icon) => { + if (icon.getAttribute("data-theme") == savedTheme) { + icon.classList.add("active"); + } }); - } - function createSliceClipPath(startAngle, endAngle, radius) { - const centerX = radius; - const centerY = radius; + function calculateVmax(percent) { + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; - let points = `${centerX}px ${centerY}px`; + const largerDimension = Math.max(viewportWidth, viewportHeight); - const segments = Math.max(2, Math.ceil(Math.abs(endAngle - startAngle) / 5)); - const angleStep = (endAngle - startAngle) / segments; + const vmaxValue = (largerDimension / 100) * percent; - for (let i = 0; i <= segments; i++) { - const angle = ((startAngle + angleStep * i - 90) * Math.PI) / 180; - const x = centerX + Math.cos(angle) * radius; - const y = centerY + Math.sin(angle) * radius; - points += `, ${x}px ${y}px`; + return vmaxValue; } - points += `, ${centerX}px ${centerY}px`; - - return `polygon(${points})`; - } + function positionThemeIcons() { + const sliceAngle = 360 / themeIcons.length; - function positionThemeWheel() { - const buttonRect = themeButton.getBoundingClientRect(); - const buttonCenterX = buttonRect.left + buttonRect.width / 2; - const buttonCenterY = buttonRect.top + buttonRect.height / 2; + themeIcons.forEach((icon, index) => { + const startAngle = index * sliceAngle; + const endAngle = (index + 1) * sliceAngle; - const margin = 0; + const clipPath = createSliceClipPath( + startAngle, + endAngle, + themeWheelRadius, + ); + icon.style.clipPath = clipPath; + }); + } - let wheelX = buttonCenterX - themeWheelRadius; - let wheelY = buttonCenterY - themeWheelRadius; + function createSliceClipPath(startAngle, endAngle, radius) { + const centerX = radius; + const centerY = radius; - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; + let points = `${centerX}px ${centerY}px`; - if (wheelX < margin) { - wheelX = margin; - } else if (wheelX + (themeWheelRadius * 2) > viewportWidth - margin) { - wheelX = viewportWidth - (themeWheelRadius * 2) - margin; - } + const segments = Math.max( + 2, + Math.ceil(Math.abs(endAngle - startAngle) / 5), + ); + const angleStep = (endAngle - startAngle) / segments; + + for (let i = 0; i <= segments; i++) { + const angle = + ((startAngle + angleStep * i - 90) * Math.PI) / 180; + const x = centerX + Math.cos(angle) * radius; + const y = centerY + Math.sin(angle) * radius; + points += `, ${x}px ${y}px`; + } - if (wheelY < margin) { - wheelY = margin; - } else if (wheelY + (themeWheelRadius * 2) > viewportHeight - margin) { - wheelY = viewportHeight - (themeWheelRadius * 2) - margin; + points += `, ${centerX}px ${centerY}px`; + + return `polygon(${points})`; } - themeWheel.style.left = wheelX + 'px'; - themeWheel.style.top = wheelY + 'px'; - } + function positionThemeWheel() { + const buttonRect = themeButton.getBoundingClientRect(); + const buttonCenterX = buttonRect.left + buttonRect.width / 2; + const buttonCenterY = buttonRect.top + buttonRect.height / 2; + + const margin = 0; - function hideThemeWheel() { - themeWheel.classList.remove('active'); - } + let wheelX = buttonCenterX - themeWheelRadius; + let wheelY = buttonCenterY - themeWheelRadius; - function showThemeWheel() { - positionThemeWheel(); - positionThemeIcons(); - themeWheel.classList.add('active'); - } + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; - function toggleThemeWheel() { - if (themeWheel.classList.contains('active')) { - hideThemeWheel(); - } else { - showThemeWheel(); + if (wheelX < margin) { + wheelX = margin; + } else if ( + wheelX + themeWheelRadius * 2 > + viewportWidth - margin + ) { + wheelX = viewportWidth - themeWheelRadius * 2 - margin; + } + + if (wheelY < margin) { + wheelY = margin; + } else if ( + wheelY + themeWheelRadius * 2 > + viewportHeight - margin + ) { + wheelY = viewportHeight - themeWheelRadius * 2 - margin; + } + + themeWheel.style.left = wheelX + "px"; + themeWheel.style.top = wheelY + "px"; } - } - themeIcons.forEach(icon => { - icon.addEventListener('click', (e) => { - e.stopPropagation(); - const theme = icon.getAttribute('data-theme'); - switchTheme(theme); - themeIcons.forEach(icon => { - icon.classList.remove('active'); - }) - icon.classList.add('active'); - }); - }); + function hideThemeWheel() { + themeWheel.classList.remove("active"); + } - themeWheel.addEventListener('mouseleave', hideThemeWheel); + function showThemeWheel() { + positionThemeWheel(); + positionThemeIcons(); + themeWheel.classList.add("active"); + } - document.addEventListener('click', (e) => { - if (!themeWheel.contains(e.target) && e.target !== themeButton) { - hideThemeWheel(); + function toggleThemeWheel() { + if (themeWheel.classList.contains("active")) { + hideThemeWheel(); + } else { + showThemeWheel(); + } } - }); - </script> - <script> - let decorResizeTimeout; - window.addEventListener('resize', function() { - clearTimeout(decorResizeTimeout); - decorResizeTimeout = setTimeout(switchThemeDecor, 300); - }); - </script> + themeIcons.forEach((icon) => { + icon.addEventListener("click", (e) => { + e.stopPropagation(); + const theme = icon.getAttribute("data-theme"); + switchTheme(theme); + themeIcons.forEach((icon) => { + icon.classList.remove("active"); + }); + icon.classList.add("active"); + }); + }); + + themeWheel.addEventListener("mouseleave", hideThemeWheel); + + document.addEventListener("click", (e) => { + if ( + !themeWheel.contains(e.target) && + e.target !== themeButton && + !e.target.classList.contains("toolbar-theme-button-content") + ) { + hideThemeWheel(); + } + }); + </script> -</body> + <script> + let decorResizeTimeout; + window.addEventListener("resize", function () { + clearTimeout(decorResizeTimeout); + decorResizeTimeout = setTimeout(switchThemeDecor, 300); + }); + </script> + </body> </html> diff --git a/views/messages/new-post.html b/views/messages/new-post.html new file mode 100644 index 0000000..98f0b52 --- /dev/null +++ b/views/messages/new-post.html @@ -0,0 +1,19 @@ +{{ define "body" }} +<p>{{ .L.Mail.NewPost.Intro }}</p> +<table> + <tr> + <td rowspan="3"><a href="https://{{ .ClientHost }}/{{ .Lang }}/blog/{{ .Post.FileName }}"><img src="{{ printf .ThumbnailBaseUrl .Post.Metadata.Thumbnail }}"></a></td> + <td class="darkened" style="font-size: 24px"><a style="color: #273de1 !important;" href="https://{{ .ClientHost }}/{{ .Lang }}/blog/{{ .Post.FileName }}">{{ .Post.Metadata.Title }}</a></td> + </tr> + <tr> + <td class="darkened" style="font-size: 16px">{{ .Post.Metadata.ShortDescription }}</td> + </tr> + <tr> + <td class="darkened" style="font-size: 16px">{{ .L.Mail.NewPost.CapturedOn }} {{ .Post.Metadata.ActionDate }}</td> + </tr> +</table> +{{ end }} + +{{ define "footer" }} +<p>{{ .UnsubscribeFooter }}</p> +{{ end }} diff --git a/views/messages/verify-email.html b/views/messages/verify-email.html new file mode 100644 index 0000000..dd6ef33 --- /dev/null +++ b/views/messages/verify-email.html @@ -0,0 +1,10 @@ +{{ define "body" }} +<p>{{ .L.Mail.VerifyEmail.Welcome }}</p> +<p>{{ .L.Mail.VerifyEmail.Intro }}</p> +<ol> + <li>{{ .L.Mail.VerifyEmail.GotoLink }} <a href="https://{{ .ClientHost }}/{{ .Lang }}/user?email_code={{ .VerificationCode }}" class="outlier">>>></a></li> + <li>{{ .L.Mail.VerifyEmail.InputCode }} <span class="outlier">{{ .VerificationCode }}</span><span></li> +</ol> +<p>{{ .L.Mail.VerifyEmail.IfRandom }}</p> +<a href="https://{{ .ClientHost }}" class="outlier">{{ .ClientHost }}</a> +{{ end }}
\ No newline at end of file diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html index 3845275..8e1cf72 100644 --- a/views/pages/blog-catalogue.html +++ b/views/pages/blog-catalogue.html @@ -1,61 +1,150 @@ {{ define "body" }} -<div class="flex flex-row ar-lt-0.8:flex-col text-base h-[100%]"> - <form hx-get="/api/v1/blog-search" hx-vals='{"lang": "{{ .Lang }}"}' hx-target=".blog-cards" hx-swap="innerHTML" - class="tags-list ar-gt-0.8:min-w-[10dvh] ar-gt-0.8:max-w-[20dvh] ar-gt-0.8:w-fit ar-lt-0.8:w-[100%] ar-lt-0.8:max-h-[30%] flex shrink-0 flex-col ar-gt-0.8:mr-6 bg-background-dark ar-lt-0.8:bg-background-light inset-shadow"> - <div class="flex flex-col overflow-y-auto"> - <fieldset> - <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.TagsHeader }}</legend> - <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 xs:grid-cols-4 sm:grid-cols-5 grid-flow-row-dense"> - <div class="m-0.5"> - <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();"> {{ .L.BlogSearch.ChooseAllTags }}</label> +<!-- Streamline Icons: https://streamlinehq.com --> +<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true"> + <symbol id="icon-like" viewBox="-2 -2 28 28" fill="none"> + <path fill="currentColor" d="M22.155 6.29294c-0.8113 -1.34837 -1.8229 -2.56561 -3 -3.61 -1.6282 -1.47261 -3.6782 -2.396134 -5.86 -2.6399952C11.1388 -0.137008 8.97321 0.252665 7.015 1.17294c-1.6708 0.75913 -3.14018 1.8997 -4.29 3.33C1.59785 5.92036 0.798705 7.56988 0.385003 9.33294c-0.010341 0.04271 -0.012051 0.08704 -0.005029 0.13041 0.007022 0.04337 0.022633 0.08491 0.045919 0.12216 0.023286 0.03726 0.053777 0.06949 0.089685 0.09481 0.035908 0.02532 0.07651 0.04321 0.119425 0.05262 0.083317 0.01503 0.169224 -0.00254 0.239953 -0.04907 0.070728 -0.04653 0.120867 -0.11846 0.140044 -0.20093 0.42729 -1.65655 1.22384 -3.19495 2.33 -4.5 1.0777 -1.30186 2.44846 -2.32993 4 -3 1.83266 -0.80027 3.84 -1.117035 5.83 -0.92 1.9654 0.25006 3.804 1.10643 5.26 2.45 1.0733 0.9746 1.9996 2.09963 2.75 3.34 0.77 1.20382 1.2366 2.57635 1.36 3.99996 0.1304 1.5877 -0.0565 3.1854 -0.55 4.7 -0.5032 1.5017 -1.4221 2.8298 -2.65 3.83 -0.7981 0.673 -1.7331 1.1644 -2.74 1.44 -1.5427 0.4432 -3.1354 0.6885 -4.74 0.73 -0.0778 0.0052 -0.1505 0.0404 -0.2028 0.0983 -0.0523 0.0579 -0.08 0.1338 -0.0772 0.2117 0 0.039 0.0078 0.0775 0.023 0.1134 0.0152 0.0358 0.0374 0.0683 0.0654 0.0953 0.028 0.0271 0.0612 0.0482 0.0976 0.0622 0.0363 0.014 0.0751 0.0205 0.114 0.0191 1.6629 -0.0085 3.3184 -0.2201 4.93 -0.63 1.0928 -0.2824 2.1146 -0.7899 3 -1.49 1.3927 -1.0653 2.454 -2.5046 3.06 -4.15 0.5754 -1.6287 0.8235 -3.3551 0.73 -5.08 -0.1076 -1.59917 -0.6055 -3.14765 -1.45 -4.50996Z" stroke-width="1"></path> + <path fill="currentColor" d="M15.935 15.903c0.6791 -0.2938 1.2468 -0.7964 1.6208 -1.4347 0.3741 -0.6384 0.5349 -1.3794 0.4592 -2.1153 -0.0284 -0.1682 -0.0994 -0.3264 -0.2061 -0.4594 -0.1068 -0.133 -0.2458 -0.2365 -0.4039 -0.3007 -0.4529 -0.1228 -0.927 -0.1466 -1.39 -0.0699 -1.22 0.07 -3.05 0.38 -4.54 0.54 -0.4 0.05 -0.78 0.08 -1.11 0.1 -0.0689 0.0127 -0.1312 0.0492 -0.1761 0.1031 -0.0448 0.0539 -0.0693 0.1217 -0.0693 0.1919 0 0.0701 0.0245 0.1379 0.0693 0.1918 0.0449 0.0539 0.1072 0.0904 0.1761 0.1032l5.66 -0.18h0.83c0.0328 0.5028 -0.0896 1.0037 -0.3508 1.4347 -0.2611 0.431 -0.6483 0.7715 -1.1092 0.9753 -0.4499 0.3065 -0.9606 0.5125 -1.4972 0.6039 -0.5367 0.0915 -1.0868 0.0663 -1.6128 -0.074 -0.4102 -0.1727 -0.7888 -0.4126 -1.12 -0.71 -0.3351 -0.2774 -0.6136 -0.6169 -0.82 -1 -0.0184 -0.0407 -0.0446 -0.0774 -0.0771 -0.108 -0.0326 -0.0305 -0.0709 -0.0544 -0.1127 -0.0702 -0.0418 -0.0158 -0.0862 -0.0232 -0.1309 -0.0218 -0.04465 0.0014 -0.08858 0.0116 -0.12929 0.03 -0.04037 0.0174 -0.07686 0.0426 -0.10734 0.0743 -0.03049 0.0316 -0.05435 0.069 -0.07018 0.11 -0.01584 0.041 -0.02333 0.0847 -0.02203 0.1286 0.00129 0.044 0.01133 0.0872 0.02955 0.1272 0.21114 0.4969 0.52069 0.9459 0.90999 1.32 0.3786 0.3811 0.8137 0.7015 1.29 0.95 0.6592 0.2289 1.361 0.3082 2.0546 0.2321 0.6936 -0.0762 1.3616 -0.3057 1.9554 -0.6721Z" stroke-width="1"></path> + <path fill="currentColor" d="M8.33499 10.9829c0.22252 0.0613 0.45748 0.0613 0.68 0 0.17653 -0.0617 0.33498 -0.1663 0.46112 -0.3044 0.12615 -0.1381 0.21605 -0.3053 0.26164 -0.4867 0.04559 -0.1814 0.04545 -0.37124 -0.00041 -0.55256 -0.04586 -0.18131 -0.136 -0.34841 -0.26235 -0.4863 -1 -1.24 -3 0.82 -1.74 1.71996 0.1869 0.0887 0.39382 0.1266 0.6 0.11Z" stroke-width="1"></path> + <path fill="currentColor" d="M19.1653 10.343c0.2247 0.0401 0.456 0.0194 0.67 -0.06 0.1784 -0.0611 0.3386 -0.1658 0.4661 -0.30464 0.1275 -0.13885 0.2183 -0.3074 0.264 -0.4903 0.0457 -0.1829 0.045 -0.37432 -0.0022 -0.55685 -0.0472 -0.18253 -0.1393 -0.35035 -0.2679 -0.48818 -1 -1.23 -3 0.83 -1.74 1.72997 0.1838 0.1117 0.3949 0.1705 0.61 0.17Z" stroke-width="1"></path> + <path fill="currentColor" d="M9.63505 20.0929c-0.10233 -0.204 -0.25778 -0.3768 -0.45 -0.5 0.19629 -0.3235 0.27075 -0.7064 0.21 -1.08 -0.04172 -0.3535 -0.15757 -0.6943 -0.34 -1 -0.13496 -0.1997 -0.31256 -0.3671 -0.52 -0.49 0.04212 -0.0952 0.06912 -0.1964 0.08 -0.3 0.05325 -0.3277 0.02651 -0.6634 -0.07796 -0.9786 -0.10447 -0.3151 -0.28357 -0.6003 -0.52204 -0.8314 -0.51428 -0.3306 -1.1194 -0.4915 -1.73 -0.46 -0.74972 -0.022 -1.49478 0.125 -2.18 0.43 0.05982 -0.8997 -0.19079 -1.7927 -0.71 -2.53 -0.9 -1 -2.54 -0.13 -2.71 1.36 -0.017326 0.6116 0.060262 1.2222 0.23 1.81 0.28 1 0.23 0.95 0.48 1.33 -0.22002 0.2395 -0.405175 0.5088 -0.55 0.8 -0.1 0.34 -0.28 0.74 -0.12 1 0.16 0.26 0.3 0.23 0.47 0.06 0.17 -0.17 0.09 -0.44 0.14 -0.6 0.07428 -0.2866 0.22256 -0.5487 0.43 -0.76 0.16 -0.21 0.28 -0.22 0.47 -0.4 1.85 -1.77 4.62 -1.97 5.12 -1.28 0.85 1.16 -1.16 1.29 -2.9 2.37 -0.03825 0.011 -0.07372 0.03 -0.10405 0.0557 -0.03034 0.0258 -0.05483 0.0577 -0.07187 0.0936 -0.01703 0.036 -0.0262 0.0752 -0.02691 0.1149 -0.0007 0.0398 0.00708 0.0793 0.02283 0.1158 0.01228 0.0374 0.03203 0.0719 0.05805 0.1014 0.02602 0.0295 0.05777 0.0534 0.09332 0.0702 0.03554 0.0168 0.07414 0.0263 0.11345 0.0277 0.0393 0.0015 0.07849 -0.0051 0.11518 -0.0193 0.41 -0.06 0.77 -0.06 1.12 -0.11 1 -0.17 1.25 -0.66 2.16 -0.51 0.15573 0.127 0.26274 0.3039 0.30284 0.5008 0.0401 0.1968 0.01083 0.4015 -0.08284 0.5792 -0.45993 0.315 -0.97588 0.539 -1.52 0.66 -0.36935 0.1765 -0.72678 0.377 -1.07 0.6 -0.03798 0.0103 -0.07348 0.0282 -0.10433 0.0526 -0.03085 0.0244 -0.05641 0.0549 -0.07512 0.0895 -0.01871 0.0346 -0.03018 0.0727 -0.03371 0.1119 -0.00353 0.0391 0.00094 0.0786 0.01316 0.116 0.01027 0.038 0.02815 0.0735 0.05258 0.1044 0.02442 0.0308 0.05486 0.0564 0.08948 0.0751 0.03461 0.0187 0.07267 0.0302 0.11185 0.0337 0.03919 0.0035 0.07869 -0.0009 0.11609 -0.0132 0.36103 -0.003 0.7216 -0.0264 1.08 -0.07 0.15662 -0.0212 0.31072 -0.0581 0.46 -0.11 0.36406 -0.2158 0.79585 -0.2872 1.21 -0.2 0.03286 0.0062 0.06407 0.0192 0.09162 0.0381 0.02755 0.019 0.05084 0.0435 0.06838 0.0719 0.03012 0.0772 0.03012 0.1629 0 0.24 -0.05277 0.2543 -0.17376 0.4893 -0.35 0.68 -0.20205 0.2292 -0.43028 0.434 -0.68 0.61 -0.66869 0.4607 -1.44234 0.7461 -2.25 0.83 -0.80071 0.1179 -1.61838 -0.0039 -2.35 -0.35 -0.53395 -0.2824 -0.95681 -0.737 -1.2 -1.29 -0.27045 -0.5855 -0.42982 -1.2162 -0.47 -1.86 -0.00126 -0.0442 -0.0114 -0.0877 -0.02981 -0.1279 -0.01841 -0.0403 -0.04473 -0.0764 -0.07738 -0.1062 -0.03265 -0.0299 -0.07097 -0.0529 -0.11269 -0.0676 -0.04171 -0.0148 -0.08596 -0.021 -0.13012 -0.0183 -0.044675 0.0026 -0.088408 0.0139 -0.12869 0.0334s-0.076321 0.0468 -0.106051 0.0802c-0.029729 0.0334 -0.052563 0.0724 -0.067194 0.1147 -0.014631 0.0423 -0.02077 0.0871 -0.018065 0.1317 0.012623 0.8862 0.235395 1.7567 0.65 2.54 0.30359 0.5453 0.74942 0.9981 1.29 1.31 0.8694 0.479 1.8607 0.6912 2.85 0.61 0.9936 -0.0912 1.94725 -0.4355 2.77 -1 0.4107 -0.274 0.77525 -0.6115 1.08 -1 0.2272 -0.2941 0.3845 -0.6361 0.46 -1 0.04902 -0.304 -0.00001 -0.6157 -0.14 -0.89Zm-7.81 -3.72 -0.09 -1c0 -0.75 -0.13 -1.73 0.37 -2.19 0.1 0 0.45 -0.59 0.88 0.22 0.2375 0.4432 0.38371 0.9294 0.43 1.43 -0.00039 0.0506 0.01118 0.1005 0.03377 0.1457 0.0226 0.0451 0.05557 0.0843 0.09623 0.1143 -0.63566 0.3358 -1.2158 0.7676 -1.72 1.28Z" stroke-width="1"></path> + </symbol> + <symbol id="icon-view" viewBox="-2 -2 28 28" fill="none"> + <path fill="currentColor" fill-rule="evenodd" d="M23.6534 10.5263c-1.3628 -1.56103 -3.0387 -2.81795 -4.9189 -3.68916 -1.8052 -0.8978 -3.7485 -1.48585 -5.7486 -1.7396 -0.998 -0.11306 -2.0067 -0.09289 -2.99934 0.05999 -2.63062 0.3739 -5.14267 1.33771 -7.34828 2.81934 -0.84337 0.55535 -1.63325 1.18793 -2.359454 1.88956 -0.08425 0.0868 -0.161162 0.18047 -0.2299462 0.27997C0.0169406 10.1973 0 10.2562 0 10.3163s0.0169406 0.1191 0.0488798 0.17c0.0179573 0.0358 0.0429176 0.0677 0.0734052 0.0937 0.030488 0.0261 0.065885 0.0457 0.104099 0.0578 0.038214 0.0122 0.078469 0.0165 0.118385 0.0128 0.039917 -0.0037 0.078686 -0.0154 0.114015 -0.0343C1.5971 9.42178 2.95486 8.45777 4.45785 7.77692c0.959 -0.48729 1.9636 -0.87909 2.9993 -1.16973 0.88979 -0.25558 1.79943 -0.43617 2.71935 -0.53987 0.8961 -0.11518 1.8033 -0.11518 2.6994 0 1.8879 0.28843 3.7154 0.88632 5.4087 1.76959 1.6883 0.80557 3.2029 1.93308 4.459 3.31919 0.0877 0.152 0.1339 0.3244 0.1339 0.4999s-0.0462 0.3479 -0.1339 0.4999c-0.2004 0.4189 -0.4747 0.7983 -0.8098 1.1197 -0.9132 0.9438 -1.9179 1.7945 -2.9993 2.5394 -1.0941 0.7277 -2.2767 1.3123 -3.5192 1.7396 -0.786 0.2536 -1.6039 0.395 -2.4294 0.4199 -1.2488 0.0246 -2.4978 -0.0321 -3.73917 -0.1699 -1.55221 -0.2376 -3.01813 -0.8673 -4.259 -1.8296 -1.29902 -0.9356 -2.40844 -2.1095 -3.26924 -3.4592 -0.02265 -0.0404 -0.05333 -0.0757 -0.09014 -0.1039 -0.03681 -0.0281 -0.07898 -0.0484 -0.12392 -0.0596 -0.04494 -0.0113 -0.0917 -0.0132 -0.13741 -0.0057s-0.08941 0.0243 -0.12842 0.0492c-0.07659 0.0529 -0.13044 0.1327 -0.15082 0.2235 -0.02039 0.0908 -0.0058 0.1859 0.04085 0.2664 0.8813 1.4449 2.02784 2.7102 3.37921 3.7292 1.34805 1.0808 2.9532 1.7938 4.65891 2.0695 1.26545 0.179 2.54115 0.2758 3.81915 0.2899 0.8996 0.0048 1.7952 -0.1198 2.6593 -0.3699 1.3434 -0.4251 2.6244 -1.0269 3.8092 -1.7896 1.1699 -0.7705 2.2556 -1.6619 3.2392 -2.6594 0.5478 -0.5108 0.9601 -1.1498 1.1997 -1.8595 0.0999 -0.2934 0.1302 -0.606 0.0886 -0.9131 -0.0417 -0.3071 -0.1541 -0.6003 -0.3285 -0.8565Z" clip-rule="evenodd" stroke-width="1"></path> + <path fill="currentColor" fill-rule="evenodd" d="M10.6964 9.1166c0.7827 -0.2444 1.6136 -0.29247 2.4194 -0.13997 0.3938 0.09551 0.7634 0.27195 1.0852 0.51809 0.3219 0.24613 0.589 0.55658 0.7844 0.91158 0.0189 0.0362 0.0448 0.0683 0.0762 0.0944 0.0314 0.0262 0.0677 0.0458 0.1067 0.0579 0.039 0.0121 0.08 0.0163 0.1207 0.0125 0.0407 -0.0038 0.0802 -0.0157 0.1163 -0.0348 0.0713 -0.04 0.124 -0.1066 0.1465 -0.1853 0.0224 -0.0786 0.0129 -0.163 -0.0266 -0.2346 -0.213 -0.44629 -0.5169 -0.84312 -0.8922 -1.16507 -0.3753 -0.32195 -0.8138 -0.56192 -1.2873 -0.70452 -0.9344 -0.26583 -1.92 -0.29674 -2.8693 -0.08998 -0.53345 0.13369 -1.02665 0.39446 -1.4375 0.76005 -0.41085 0.36559 -0.72716 0.82514 -0.92194 1.33942 -0.41881 1.0115 -0.41881 2.1478 0 3.1593 0.4668 0.9845 1.29879 1.7483 2.31944 2.1295 0.9793 0.3819 2.0556 0.438 3.0693 0.16 0.8982 -0.2612 1.6632 -0.8546 2.1395 -1.6596 0.4789 -0.7747 0.6995 -1.6814 0.6299 -2.5894 -0.0061 -0.0449 -0.0211 -0.0881 -0.0441 -0.127 -0.0231 -0.039 -0.0536 -0.073 -0.0899 -0.1 -0.0363 -0.027 -0.0777 -0.0465 -0.1216 -0.0573 -0.044 -0.0108 -0.0897 -0.0127 -0.1344 -0.0057 -0.0457 0.0049 -0.0899 0.019 -0.1301 0.0415 -0.0401 0.0224 -0.0752 0.0528 -0.1033 0.0893 -0.028 0.0364 -0.0484 0.0782 -0.0598 0.1227 -0.0114 0.0446 -0.0137 0.091 -0.0067 0.1364 -0.0016 0.7166 -0.2252 1.4151 -0.6398 1.9996 -0.3854 0.5945 -0.9884 1.0144 -1.6796 1.1697 -0.8061 0.1738 -1.6466 0.0863 -2.3995 -0.2499 -0.7468 -0.2609 -1.36555 -0.7974 -1.72958 -1.4997 -0.29453 -0.7244 -0.29453 -1.5351 0 -2.2595 0.1115 -0.3727 0.31161 -0.7129 0.58321 -0.99148 0.27161 -0.27856 0.60667 -0.48722 0.97647 -0.60812Z" clip-rule="evenodd" stroke-width="1"></path> + </symbol> + <symbol id="icon-arrange-letter-asc" viewBox="-2 -2 28 28" fill="none"> + <path fill="var(--color-accent-light)" d="M11.9999 20.9053c5.1771 0 9.3739 -4.1969 9.3739 -9.374 0 -5.17702 -4.1968 -9.37389 -9.3739 -9.37389 -5.17706 0 -9.37392 4.19687 -9.37392 9.37389 0 5.1771 4.19686 9.374 9.37392 9.374Z" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M5.90674 4.49902V18.5599" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m3.09473 15.7477 2.81215 2.8121 2.81224 -2.8121" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M19.874 22.3113h-3.3746c-0.0837 0 -0.1658 -0.0224 -0.2378 -0.0649 -0.0722 -0.0424 -0.1316 -0.1035 -0.1721 -0.1766 -0.0405 -0.0732 -0.0608 -0.1559 -0.0586 -0.2395 0.0022 -0.0836 0.0268 -0.1652 0.0711 -0.2361l3.7908 -6.065c0.0441 -0.0706 0.0687 -0.1518 0.0711 -0.2352 0.0023 -0.0833 -0.0175 -0.1657 -0.0577 -0.2387 -0.0401 -0.0731 -0.099 -0.1342 -0.1705 -0.1769 -0.0717 -0.0427 -0.1532 -0.0656 -0.2366 -0.0663h-3.3746" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M16.2183 9.18788V3.56349c0 -0.4972 0.1975 -0.97405 0.5491 -1.32569 0.3517 -0.35154 0.8285 -0.54908 1.3257 -0.54908 0.4971 0 0.974 0.19754 1.3256 0.54908 0.3516 0.35164 0.5492 0.82849 0.5492 1.32569v5.62439" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M16.2183 5.4364h3.7496" stroke-width="1"></path> + </symbol> + <symbol id="icon-arrange-letter-desc" viewBox="-2 -2 28 28" fill="none"> + <path fill="var(--color-accent-light)" d="M11.9999 20.9053c5.1771 0 9.3739 -4.1969 9.3739 -9.374 0 -5.17702 -4.1968 -9.37389 -9.3739 -9.37389 -5.17706 0 -9.37392 4.19687 -9.37392 9.37389 0 5.1771 4.19686 9.374 9.37392 9.374Z" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M5.90674 4.49902V18.5599" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m3.09473 6.49902 2.81215 -2.8121 2.81224 2.8121" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M19.874 22.3113h-3.3746c-0.0837 0 -0.1658 -0.0224 -0.2378 -0.0649 -0.0722 -0.0424 -0.1316 -0.1035 -0.1721 -0.1766 -0.0405 -0.0732 -0.0608 -0.1559 -0.0586 -0.2395 0.0022 -0.0836 0.0268 -0.1652 0.0711 -0.2361l3.7908 -6.065c0.0441 -0.0706 0.0687 -0.1518 0.0711 -0.2352 0.0023 -0.0833 -0.0175 -0.1657 -0.0577 -0.2387 -0.0401 -0.0731 -0.099 -0.1342 -0.1705 -0.1769 -0.0717 -0.0427 -0.1532 -0.0656 -0.2366 -0.0663h-3.3746" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M16.2183 9.18788V3.56349c0 -0.4972 0.1975 -0.97405 0.5491 -1.32569 0.3517 -0.35154 0.8285 -0.54908 1.3257 -0.54908 0.4971 0 0.974 0.19754 1.3256 0.54908 0.3516 0.35164 0.5492 0.82849 0.5492 1.32569v5.62439" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M16.2183 5.4364h3.7496" stroke-width="1"></path> + </symbol> + <symbol id="icon-arrange-number-asc" viewBox="-2 -2 28 28" fill="none"> + <path fill="var(--color-accent-light)" d="M12.0002 20.9833c4.9615 0 8.9836 -4.0221 8.9836 -8.9836 0 -4.9615 -4.0221 -8.98365 -8.9836 -8.98365 -4.96154 0 -8.9836 4.02215 -8.9836 8.98365 0 4.9615 4.02206 8.9836 8.9836 8.9836Z" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M6.16064 5.26019V18.7356" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m3.46533 16.0405 2.69509 2.6951 2.6951 -2.6951" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M15.5933 8.85364h4.4918" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17.8392 8.85545V2.11767c-0.0001 -0.08451 -0.0241 -0.16729 -0.0692 -0.23879 -0.045 -0.0715 -0.1093 -0.12888 -0.1855 -0.16546 -0.0762 -0.03657 -0.1612 -0.05096 -0.2452 -0.04143 -0.0839 0.00953 -0.1636 0.04263 -0.2296 0.09533l-1.5164 1.21101" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17.39 19.6338c1.2403 0 2.2458 -1.0054 2.2458 -2.2458s-1.0055 -2.2459 -2.2458 -2.2459c-1.2404 0 -2.246 1.0055 -2.246 2.2459 0 1.2404 1.0056 2.2458 2.246 2.2458Z" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M19.636 17.3898v2.2459c0 0.7147 -0.284 1.4003 -0.7895 1.9057 -0.5054 0.5054 -1.1909 0.7894 -1.9056 0.7894h-0.4492" stroke-width="1"></path> + </symbol> + <symbol id="icon-arrange-number-desc" viewBox="-2 -2 28 28" fill="none"> + <path fill="var(--color-accent-light)" d="M12.0002 20.9833c4.9615 0 8.9836 -4.0221 8.9836 -8.9836 0 -4.9615 -4.0221 -8.98365 -8.9836 -8.98365 -4.96154 0 -8.9836 4.02215 -8.9836 8.98365 0 4.9615 4.02206 8.9836 8.9836 8.9836Z" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M6.16064 5.26019V18.7356" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m3.09473 6.49902 2.81215 -2.8121 2.81224 2.8121" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M15.5933 8.85364h4.4918" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17.8392 8.85545V2.11767c-0.0001 -0.08451 -0.0241 -0.16729 -0.0692 -0.23879 -0.045 -0.0715 -0.1093 -0.12888 -0.1855 -0.16546 -0.0762 -0.03657 -0.1612 -0.05096 -0.2452 -0.04143 -0.0839 0.00953 -0.1636 0.04263 -0.2296 0.09533l-1.5164 1.21101" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17.39 19.6338c1.2403 0 2.2458 -1.0054 2.2458 -2.2458s-1.0055 -2.2459 -2.2458 -2.2459c-1.2404 0 -2.246 1.0055 -2.246 2.2459 0 1.2404 1.0056 2.2458 2.246 2.2458Z" stroke-width="1"></path> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M19.636 17.3898v2.2459c0 0.7147 -0.284 1.4003 -0.7895 1.9057 -0.5054 0.5054 -1.1909 0.7894 -1.9056 0.7894h-0.4492" stroke-width="1"></path> + </symbol> + <symbol id="icon-camera-settings-flip" viewBox="-2 -2 28 28" fill="none"> + <path fill="currentColor" fill-rule="evenodd" d="M22.1058 13.4701c-0.7103 -0.2001 -0.2501 1.0004 -2.1609 3.7917 -0.7602 1.1535 -1.7872 2.1068 -2.9941 2.779 -1.2069 0.6722 -2.558 1.0436 -3.9389 1.0826 -4.60204 0.3902 -9.00396 -2.0008 -9.83432 -6.6328 1.14049 0.6302 1.59069 0.7203 2.64115 1.2205 0.07919 0.0398 0.1703 0.049 0.25586 0.0259 0.08557 -0.0232 0.1596 -0.0771 0.20791 -0.1514 0.04831 -0.0743 0.06751 -0.1639 0.05393 -0.2515 -0.01358 -0.0876 -0.05899 -0.1671 -0.12753 -0.2233 -3.83167 -2.8912 -2.89126 -2.2009 -3.05133 -2.301 -1.1505 -0.7503 -2.15094 1.9008 -3.041328 3.0814C0.0416301 15.9693 0 16.0732 0 16.1813c0 0.108 0.0416301 0.212 0.116242 0.2901 0.450196 0.4002 0.790344 -0.2701 2.200958 -1.8208 0.88039 7.0031 9.3841 10.0044 15.4968 6.3028 2.2409 -1.4206 3.8817 -4.472 4.5119 -7.0031 0.0176 -0.0459 0.0258 -0.0949 0.0242 -0.1441 -0.0016 -0.0491 -0.013 -0.0975 -0.0335 -0.1422 -0.0205 -0.0447 -0.0497 -0.0848 -0.0859 -0.1181 -0.0361 -0.0333 -0.0786 -0.0591 -0.1249 -0.0758Z" clip-rule="evenodd" stroke-width="1"></path> + <path fill="currentColor" fill-rule="evenodd" d="M23.8566 7.50752c-0.4502 -0.39017 -0.8804 0.37016 -2.0709 1.74076 -0.9504 -7.00306 -9.4141 -9.854303 -15.47677 -6.20271 -2.25098 1.42062 -3.88169 4.47195 -4.51196 7.00303 -0.02462 0.0915 -0.01217 0.1891 0.03465 0.2715 0.04682 0.0824 0.12423 0.143 0.21546 0.1687 0.72031 0.2001 0.26011 -1.00042 2.17094 -3.79164 0.75857 -1.15276 1.78388 -2.10565 2.98903 -2.77788 1.20515 -0.67224 2.55461 -1.04401 3.93395 -1.0838 4.602 -0.39017 9.004 2.00087 9.8443 6.6529l-2.6211 -1.21053c-0.0796 -0.05306 -0.177 -0.07234 -0.2708 -0.05358 -0.0938 0.01877 -0.1764 0.07403 -0.2294 0.15363 -0.0531 0.0796 -0.0724 0.17702 -0.0536 0.27082 0.0188 0.09381 0.074 0.17633 0.1536 0.22939l2.301 1.78079c0.3002 0.2101 0.9404 1.0004 1.5107 0.5202 1.0004 -0.7903 1.4606 -2.15092 2.1309 -3.09133 0.0683 -0.08444 0.1012 -0.19203 0.0918 -0.3002 -0.0093 -0.10817 -0.0601 -0.20853 -0.1418 -0.28005Z" clip-rule="evenodd" stroke-width="1"></path> + </symbol> + <symbol id="icon-search-magnifier" viewBox="-2 -2 28 28" fill="none"> + <path fill="currentColor" d="M16.1952 8.05092c-0.1789 -0.9786 -0.609 -1.89392 -1.2482 -2.65625 -0.1897 -0.22968 -0.3894 -0.4993 -0.6191 -0.72898 -0.1672 -0.17504 -0.3631 -0.32026 -0.5792 -0.42939 -0.0751 -0.03803 -0.1616 -0.04682 -0.2428 -0.02468s-0.1512 0.07364 -0.1966 0.14451h-0.1298c-0.9986 0.57918 -1.9972 1.19831 -2.9159 1.85738 -0.46933 0.32954 -0.93867 0.65907 -1.39802 0.99859L7.52747 8.34051c-0.68903 0.60914 -1.418 1.22827 -2.08706 1.89729 -0.30956 0.3096 -0.59915 0.6291 -0.87876 0.9986 -0.1547 -0.5346 -0.20221 -1.0946 -0.1398 -1.64765 0.28862 -1.67274 1.08327 -3.21674 2.27679 -4.42376 0.32935 -0.3211 0.71532 -0.57841 1.13839 -0.75893 0.59833 -0.21847 1.23041 -0.33002 1.86737 -0.32953 1.0089 -0.00223 2.0145 0.11509 2.9958 0.3495 0.069 0.00477 0.1376 -0.01451 0.194 -0.05456 0.0564 -0.04004 0.0972 -0.09839 0.1155 -0.16513 0.0184 -0.07804 0.0058 -0.16017 -0.0352 -0.22908 -0.041 -0.06892 -0.1071 -0.11925 -0.1845 -0.1404 -1.0143 -0.27385 -2.0559 -0.43461 -3.10557 -0.47932 -0.72843 -0.02631 -1.45565 0.07854 -2.14697 0.30956 -2.127 0.74895 -3.92447 3.79465 -4.20407 5.79183 -0.12584 1.06047 0.04348 2.13517 0.48931 3.10567 0.44242 0.955 1.11493 1.7854 1.95724 2.4165 0.47792 0.3465 1.01643 0.6005 1.58776 0.749 0.53703 0.1498 1.09034 0.2337 1.64767 0.2496 1.55563 0.031 3.08183 -0.4265 4.36383 -1.3081 1.2904 -0.8722 2.2229 -2.1805 2.6263 -3.6848 0.2708 -0.9541 0.3355 -1.95488 0.1897 -2.93588ZM4.8812 12.0453c-0.02753 -0.0548 -0.0509 -0.1115 -0.0699 -0.1698 0.37992 -0.3657 0.78356 -0.706 1.2083 -1.0185 0.72897 -0.5792 1.49788 -1.09849 2.23684 -1.62774 0.73896 -0.52926 1.25823 -0.99859 1.86736 -1.4879 0.6091 -0.48931 1.2083 -0.9986 1.8274 -1.52785 0.6192 -0.52925 1.0585 -0.83882 1.5978 -1.21828 0.0847 0.06414 0.1648 0.13422 0.2396 0.2097 0.1998 0.21969 0.3795 0.47933 0.5493 0.69902 0.4793 0.5818 0.8207 1.2646 0.9986 1.99718 -1.0586 0.64908 -2.137 1.23825 -3.1955 1.87735 -0.7845 0.46302 -1.5444 0.96632 -2.27683 1.50792 -1.15599 0.8863 -2.21685 1.8903 -3.16553 2.9957l-0.21969 -0.1298c-0.69787 -0.5438 -1.24738 -1.2547 -1.59775 -2.0671v-0.0399Zm10.2655 -1.3481c-0.3753 1.2769 -1.2023 2.3736 -2.3267 3.0856 -1.1145 0.7131 -2.4231 1.062 -3.74471 0.9986 -0.47267 -0.0061 -0.94256 -0.0732 -1.39803 -0.1997 -0.12982 0 -0.24965 -0.0899 -0.36948 -0.1298 0.44937 -0.3895 0.89873 -0.749 1.36807 -1.0985 0.46934 -0.3495 0.99859 -0.659 1.46795 -0.9986l1.4779 -0.9985c1.2915 -0.8855 2.5597 -1.79751 3.8046 -2.73618 0.0318 0.71661 -0.0629 1.43318 -0.2796 2.11698v-0.0399Z" stroke-width="1"></path> + <path fill="currentColor" d="M23.2453 20.5533c-0.04 -0.2181 -0.1001 -0.4321 -0.1797 -0.6391 -0.1361 -0.3547 -0.3173 -0.6903 -0.5393 -0.9986 -0.3034 -0.4388 -0.637 -0.8559 -0.9986 -1.2482 -0.5284 -0.5741 -1.0956 -1.1113 -1.6976 -1.6078 -0.8288 -0.679 -1.6976 -1.2981 -2.5763 -1.9971 0.2631 -0.5055 0.4803 -1.0335 0.649 -1.5778 0.248 -0.8172 0.4349 -1.6516 0.5592 -2.4965 0.1569 -0.81258 0.2305 -1.63902 0.2197 -2.46652 -0.0078 -0.57747 -0.1055 -1.1502 -0.2896 -1.6976 -0.1575 -0.4947 -0.3476 -0.97841 -0.5692 -1.44796 -0.1562 -0.33202 -0.347 -0.64665 -0.5691 -0.93868 -0.3781 -0.48797 -0.8217 -0.92146 -1.3182 -1.28818 -0.7357 -0.52062 -1.53 -0.95298 -2.3666 -1.288183 -0.5827 -0.254108 -1.1835 -0.464396 -1.7975 -0.629112 -0.6518 -0.1693231 -1.324 -0.246631 -1.9972 -0.22967613C8.94606 0.0473495 8.12871 0.212165 7.34773 0.491599c-0.76731 0.278516 -1.50629 0.629611 -2.20689 1.048521 -0.60025 0.33964 -1.16553 0.73768 -1.68762 1.18832C1.21637 4.69567 0.757023 8.98961 0.727065 9.45895 0.620124 10.7287 0.804559 12.0061 1.2663 13.1937c0.12146 0.3013 0.26153 0.5948 0.41941 0.8787 0.22968 0.3995 0.48931 0.7889 0.75893 1.1784 0.5038 0.7726 1.12352 1.463 1.83741 2.0471 0.49826 0.396 1.06292 0.7003 1.66765 0.8987 0.69046 0.2254 1.39862 0.3924 2.11701 0.4993 0.49142 0.0823 0.99015 0.1124 1.4879 0.0899 0.77439 -0.05 1.53939 -0.1977 2.27679 -0.4394 0.7156 -0.2507 1.4101 -0.5579 2.0771 -0.9187 0.1351 0.1597 0.2617 0.3264 0.3794 0.4993 0.4294 0.6591 0.769 1.3681 1.1684 1.9972 0.2809 0.4341 0.5877 0.851 0.9187 1.2482 0.3126 0.3904 0.646 0.7638 0.9986 1.1184 0.9711 0.9019 2.1935 1.4871 3.505 1.6777 0.262 0.0471 0.5307 0.0413 0.7904 -0.0169 0.2597 -0.0582 0.5052 -0.1678 0.7219 -0.3222 0.2168 -0.1544 0.4005 -0.3506 0.5404 -0.5771 0.1398 -0.2264 0.233 -0.4785 0.2741 -0.7415 0.0404 -0.1745 0.0704 -0.3512 0.0898 -0.5292 0.0097 -0.1763 0.0097 -0.353 0 -0.5293 0.0036 -0.234 -0.0131 -0.4679 -0.0499 -0.699Zm-1.1584 1.4679c-0.0509 0.2532 -0.1979 0.4768 -0.4102 0.6239 -0.2122 0.1471 -0.4732 0.2062 -0.7282 0.165 -1.0467 -0.1308 -2.0325 -0.5647 -2.836 -1.2482 -0.3522 -0.313 -0.6856 -0.6464 -0.9986 -0.9986 -0.3312 -0.341 -0.6381 -0.7047 -0.9187 -1.0885 -0.4393 -0.6191 -0.8388 -1.3081 -1.2981 -1.9372 -0.1219 -0.1597 -0.2519 -0.313 -0.3895 -0.4594l0.1298 -0.0899c0.2213 -0.1586 0.4315 -0.3321 0.6292 -0.5192 0.0617 -0.0665 0.096 -0.1539 0.096 -0.2447 0 -0.0908 -0.0343 -0.1782 -0.096 -0.2447 -0.0637 -0.0634 -0.1499 -0.099 -0.2397 -0.099 -0.0898 0 -0.176 0.0356 -0.2397 0.099 -0.1708 0.1582 -0.3545 0.3018 -0.5492 0.4294 -0.1917 0.1358 -0.3918 0.2592 -0.5991 0.3695 -0.6447 0.3275 -1.3121 0.6078 -1.9972 0.8388 -0.6937 0.2123 -1.4121 0.3332 -2.13702 0.3595 -0.55091 0.0138 -1.10132 -0.0432 -1.63769 -0.1697 -0.5569 -0.1063 -1.10451 -0.2566 -1.63769 -0.4494 -0.62749 -0.2261 -1.19946 -0.5835 -1.67763 -1.0485 -0.49431 -0.4831 -0.93003 -1.0228 -1.29817 -1.6077 -0.22967 -0.3595 -0.46934 -0.719 -0.67904 -1.1185 -0.13067 -0.2462 -0.24735 -0.4996 -0.34951 -0.7589 -0.38354 -1.05 -0.52354 -2.1734 -0.40942 -3.28536 0.45935 -5.29254 3.14556 -6.55076 3.91448 -6.99014 0.63297 -0.38101 1.30193 -0.69877 1.99718 -0.94867 0.6757 -0.25505 1.38606 -0.40655 2.10703 -0.44936 0.56148 -0.01552 1.12238 0.04495 1.66768 0.17975 0.5643 0.13703 1.1183 0.31389 1.6576 0.52925 0.6401 0.2469 1.2581 0.54755 1.8474 0.89873 0.5688 0.32061 1.076 0.7399 1.4979 1.23825 0.1894 0.23504 0.3534 0.48943 0.4893 0.75893 0.2209 0.42872 0.4111 0.87254 0.5692 1.32813 0.1632 0.46345 0.2607 0.94748 0.2896 1.43797 0.0241 0.77857 -0.026 1.55767 -0.1498 2.32672 -0.0854 0.6722 -0.2088 1.3389 -0.3695 1.9972 -0.1609 0.6599 -0.3954 1.2996 -0.699 1.9073 -0.1608 0.3485 -0.3513 0.6826 -0.5692 0.9986 -0.2143 0.3034 -0.4549 0.5874 -0.719 0.8488 -0.0288 0.0275 -0.052 0.0605 -0.0681 0.097 -0.0161 0.0364 -0.0249 0.0757 -0.0258 0.1156 -0.0019 0.0805 0.0283 0.1585 0.084 0.2168 0.0556 0.0582 0.1321 0.092 0.2126 0.0939 0.0805 0.0019 0.1585 -0.0283 0.2168 -0.0839 0.2935 -0.2844 0.5639 -0.5916 0.8088 -0.9187 0.1099 -0.1498 0.1997 -0.3096 0.2996 -0.4694 0.8088 0.6791 1.6177 1.3182 2.3866 1.9972 0.5434 0.4957 1.0506 1.0297 1.5179 1.5978 0.3133 0.3747 0.6034 0.7682 0.8688 1.1783 0.1584 0.2534 0.2858 0.525 0.3794 0.8088 0.0505 0.1638 0.0905 0.3305 0.1198 0.4993 0.0099 0.1597 0.0099 0.3197 0 0.4794l-0.0599 0.8088Z" stroke-width="1"></path> + </symbol> +</svg> +<div class="flex flex-row ar-lt-0.8:flex-col text-base h-full"> + <div class="relative flex shrink-0 ar-gt-0.8:min-w-1/10 ar-gt-0.8:max-w-1/4 ar-gt-0.8:w-fit ar-lt-0.8:w-full ar-lt-0.8:max-h-[30%]"> + <div class="absolute w-full h-full inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> + <form hx-get="/api/v1/blog-search" hx-vals='{"lang": "{{ .Lang }}"}' hx-target=".blog-cards" hx-swap="innerHTML" + class="tags-list relative flex flex-col w-full bg-paper bg-background-light"> + <div class="flex flex-col overflow-y-auto"> + <fieldset> + <legend class="font-bold w-full text-center">{{ .L.BlogSearch.OrderByHeader }}</legend> + <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 grid-rows-2 grid-flow-col"> + <div> + <label class="flex justify-start gap-1 items-center"> + <input type="radio" id="sortTitleAsc" name="sort" value="titleAsc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "titleAsc" }}checked{{ end }}> + {{ .L.BlogSearch.TitleOrdered }} + <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-letter-asc"/></svg> + </label> + </div> + <div> + <label class="flex justify-start gap-1 items-center"> + <input type="radio" id="sortTitleDesc" name="sort" value="titleDesc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "titleDesc" }}checked{{ end }}> + {{ .L.BlogSearch.TitleOrdered }} + <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-letter-desc"/></svg> + </label> + </div> + <div> + <label class="flex justify-start gap-1 items-center"> + <input type="radio" id="sortActionDateAsc" name="sort" value="actionDateAsc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "actionDateAsc" }}checked{{ end }}> + {{ .L.BlogSearch.ActionDateOrdered }} + <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-number-asc"/></svg> + </label> + </div> + <div> + <label class="flex justify-start gap-1 items-center"> + <input type="radio" id="sortActionDateDesc" name="sort" value="actionDateDesc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "actionDateDesc" }}checked{{ end }}> + {{ .L.BlogSearch.ActionDateOrdered }} + <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-number-desc"/></svg> + </label> + </div> + <div> + <label class="flex justify-start gap-1 items-center"> + <input type="radio" id="sortPublicationDateAsc" name="sort" value="publicationDateAsc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "publicationDateAsc" }}checked{{ end }}> + {{ .L.BlogSearch.PublicationDateOrdered }} + <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-number-asc"/></svg> + </label> + </div> + <div> + <label class="flex justify-start gap-1 items-center"> + <input type="radio" id="sortPublicationDateDesc" name="sort" value="publicationDateDesc" class="bg-accent-light border-transparent text-accent-deep focus:border-transparent focus:bg-accent-light focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" {{ if eq .QuerySort "publicationDateDesc" }}checked{{ end }}> + {{ .L.BlogSearch.PublicationDateOrdered }} + <svg viewBox="0 0 24 24" class="h-8"><use href="#icon-arrange-number-desc"/></svg> + </label> + </div> </div> - {{- range .Tags }} - <div class="m-0.5"> - <label><input type="checkbox" id="tag{{ .Name }}Checkbox" name="tags[]" value="{{ .Name }}" onclick="contextAll();" {{ if contains $.QueryTags .Name }}checked{{ end }}> {{ .Name }} <span class="text-secondary italic">{{ .Count }}</span></label> + </fieldset> + <fieldset class="mt-1"> + <legend class="font-bold w-full text-center">{{ .L.BlogSearch.TagsHeader }}</legend> + <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 xs:grid-cols-4 sm:grid-cols-5 grid-flow-row-dense"> + <div class="m-1"> + <label class="font-bold"><input type="checkbox" id="tagsAllCheckbox" onclick="selectAll();" class="bg-accent-light rounded border-transparent text-accent-deep focus:border-transparent focus:bg-background-dark focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep"> {{ .L.BlogSearch.ChooseAllTags }}</label> + </div> + {{- range .Tags }} + <div class="m-1"> + <label><input type="checkbox" id="tag{{ .Name }}Checkbox" name="tags[]" value="{{ .Name }}" class="bg-accent-light rounded border-transparent text-accent-deep focus:border-transparent focus:bg-background-dark focus:ring-1 focus:ring-offset-2 focus:ring-accent-deep" onclick="contextAll();" {{ if contains $.QueryTags .Name }}checked{{ end }}> {{ .Name }} <span class="text-secondary italic">{{ .Count }}</span></label> + </div> + {{- end }} </div> - {{- end }} - </div> - </fieldset> - <fieldset class="mt-1"> - <legend class="font-bold w-[100%] text-center">{{ .L.BlogSearch.OrderByHeader }}</legend> - <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 grid-rows-2 grid-flow-col"> - <div class="m-0.5"> - <label><input type="radio" id="sortTitleAsc" name="sort" value="titleAsc" {{ if eq .QuerySort "titleAsc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-a-z"></i></label> - </div> - <div class="m-0.5"> - <label><input type="radio" id="sortTitleDesc" name="sort" value="titleDesc" {{ if eq .QuerySort "titleDesc" }}checked{{ end }}> {{ .L.BlogSearch.TitleOrdered }} <i class="fas fa-arrow-down-z-a"></i></label> - </div> - <div class="m-0.5"> - <label><input type="radio" id="sortActionDateAsc" name="sort" value="actionDateAsc" {{ if eq .QuerySort "actionDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label> - </div> - <div class="m-0.5"> - <label><input type="radio" id="sortActionDateDesc" name="sort" value="actionDateDesc" {{ if eq .QuerySort "actionDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.ActionDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label> - </div> - <div class="m-0.5"> - <label><input type="radio" id="sortPublicationDateAsc" name="sort" value="publicationDateAsc" {{ if eq .QuerySort "publicationDateAsc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-1-9"></i></label> - </div> - <div class="m-0.5"> - <label><input type="radio" id="sortPublicationDateDesc" name="sort" value="publicationDateDesc" {{ if eq .QuerySort "publicationDateDesc" }}checked{{ end }}> {{ .L.BlogSearch.PublicationDateOrdered }} <i class="fas fa-arrow-down-9-1"></i></label> - </div> - </div> - </fieldset> - </div> - <hr class="border-t-2 border-dotted border-main-dark my-0.5"> - <div class="flex flex-row bg-background-dark my-2 p-0.5 rounded-1 justify-items-center"> - <i onclick="defaultSearchParams(); cleanHrefParams();" class="fas fa-rotate-left flex-1 text-center text-2xl hover:bg-background-light cursor-pointer transition-colors duration-300"></i> - <div class="w-1 bg-background-light grow-0"></div> - <button class="flex-1" type="submit"> - <i onclick="setHrefParams();" class="fas fa-magnifying-glass text-center text-2xl hover:bg-background-light cursor-pointer transition-colors duration-300"></i> - </button> - </div> - </form> + </fieldset> + </div> + <hr class="border-t-2 border-dotted border-main-hard my-0.5"> + <div class="flex flex-row bg-paper bg-background-dark p-0.5 rounded-1 h-12"> + <button type="button" onclick="defaultSearchParams(); cleanHrefParams();" class="flex flex-1 justify-center text-main-hard bg-paper bg-accent-light cursor-pointer hover:bg-accent-deep active:text-background-dark transition-colors duration-500"> + <svg viewBox="0 0 24 24"><use href="#icon-camera-settings-flip"/></svg> + </button> + <div class="w-1 bg-paper bg-background-light grow-0"></div> + <button type="submit" onclick="setHrefParams();" class="flex flex-1 justify-center text-main-hard bg-paper bg-accent-light cursor-pointer hover:bg-accent-deep active:text-background-dark transition-colors duration-500"> + <svg viewBox="0 0 24 24"><use href="#icon-search-magnifier"/></svg> + </button> + </div> + </form> + </div> - <div class="w-[0.5dvh] border-r-2 border-dotted border-main-dark bg-background-dark"></div> + <div class="w-[0.5dvh] border-r-2 border-dotted border-main-hard bg-paper bg-background-dark"></div> - <div hx-get="/api/v1/blog-search" hx-vals='js:{lang: "{{ .Lang }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load" hx-include=".tags-list" - class="blog-cards grow flex flex-col overflow-y-auto bg-background-light inset-shadow p-6"> - Loading... + <div class="relative flex grow h-full"> + <div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> + <div hx-get="/api/v1/blog-search" hx-vals='js:{lang: "{{ .Lang }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load" hx-include=".tags-list" + class="blog-cards relative grow flex flex-col overflow-y-auto bg-paper bg-background-light pr-2"> + Loading... + </div> </div> + </div> <script> diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html index 77164fa..8978eb8 100644 --- a/views/pages/blog-page.html +++ b/views/pages/blog-page.html @@ -1,22 +1,29 @@ {{ define "body" }} -<div class="bg-background-light flex flex-col inset-shadow px-8 py-4 overflow-y-auto"> - <p class="max-xs:block [@media(max-height:32rem)]:block hidden font-spectral text-2xl font-bold italic text-main-dark tracking-[.0125rem] mb-1">{{ .Title }}</p> - <p class="max-xs:block [@media(max-height:32rem)]:block hidden grow text-lg font-spectral text-secondary">{{ .L.Metadata.Published }}: {{ .PublishedDate }}</p> - <p class="max-xs:block [@media(max-height:32rem)]:block hidden grow text-lg font-spectral text-secondary">{{ .L.Metadata.Action }}: {{ .ActionDate }}</p> - <hr class="max-xs:block [@media(max-height:32rem)]:block hidden border-t-2 border-dotted border-main-dark mt-1 mb-2 w-full ml-auto mr-auto"> +<div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> +<article class="relative bg-paper bg-background-light flex flex-col px-8 py-4 overflow-y-auto"> + <h1 class="max-xs:block [@media(max-height:32rem)]:block hidden font-gentium text-2xl font-bold italic text-main-hard tracking-[.0125rem] mb-1">{{ .Title }}</h1> + <p class="block grow text-lg font-gentium text-secondary"> + <b>{{ .L.Metadata.Published }}</b>: + <time datetime="{{ .PublishedDate }}" hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load"> + {{ .PublishedDate }} + </time> + </p> + <p class="block grow text-lg font-gentium text-secondary"><b>{{ .L.Metadata.Action }}</b>: {{ .ActionDate }}</p> + <p class="block grow text-md font-gentium font-thin text-main-hard">{{ .ShortDescription }}</p> + <hr class="block border-t-2 border-dotted border-main-hard mt-1 mb-2 w-full ml-auto mr-auto"> {{ .ParsedMarkdown }} {{- if .MapLocationX }} - <hr class="border-t-3 border-dotted border-main-dark mt-1 mb-2 w-[80%] ml-auto mr-auto"> + <hr class="border-t-3 border-dotted border-main-hard mt-1 mb-2 w-[80%] ml-auto mr-auto"> <div id="map-container" class="relative p-1 flex-none ml-auto mr-auto w-[60%] ar-lt-0.8:w-[80%] h-[40dvh] ar-lt-0.8:h-[30dvh]"></div> - <hr class="border-t-3 border-dotted border-main-dark mt-1 mb-2 w-[80%] ml-auto mr-auto"> + <hr class="border-t-3 border-dotted border-main-hard mt-1 mb-2 w-[80%] ml-auto mr-auto"> <script> function initLocationMap(id, x, y, relativeErrorMeters = 0) { map = L.map(id).setView([x, y], 8); - L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 18, - attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' + L.tileLayer('https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', { + maxZoom: 20, + attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); if (relativeErrorMeters != 0) { @@ -53,9 +60,9 @@ }); </script> {{- end }} -</div> +</article> {{ end }} -{{ define "footer" }} +{{ define "header" }} <div hx-get="/api/v1/like" hx-target="this" hx-swap="outerHTML" hx-trigger="load"></div> {{ end }} diff --git a/views/pages/global-map.html b/views/pages/global-map.html index 686b46b..ce9d5b1 100644 --- a/views/pages/global-map.html +++ b/views/pages/global-map.html @@ -1,291 +1,428 @@ -<!DOCTYPE html> +<!doctype html> <html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>{{ .L.GlobalMap.Header }} // SAYA TODAY</title> - <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"> - <link rel="preconnect" href="https://fonts.googleapis.com"> - <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> - <link href="https://fonts.googleapis.com/css2?family=Spectral:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,200;1,300;1,400;1,500;1,600;1,700;1,800&family=Patua+One&display=swap" rel="stylesheet"> - {{ block "header" . }}{{ end }} - <link href="/output.css" rel="stylesheet"> - <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.css"> - <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet.markercluster/1.4.1/MarkerCluster.css"> - <link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css"> -</head> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="description" content="{{ .L.GlobalMap.Description }}" /> + <meta property="og:title" content="{{ .L.GlobalMap.Header }}" /> + <meta + property="og:description" + content="{{ .L.GlobalMap.Description }}" + /> + <meta + property="og:url" + content="{{ .CanonicalEndpoint }}/{{ .Lang }}/map" + /> + <meta property="og:type" content="site" /> + <title>{{ .L.GlobalMap.Header }} // SAYA.UZ</title> + <link rel="canonical" href="{{ .CanonicalEndpoint }}/{{ .Lang }}/map" /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/fonts/fonts.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/fontawesome.min.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/brands.min.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/font-awesome/7.2.0/css/solid.min.css" + /> + {{ block "header" . }}{{ end }} + <link rel="stylesheet" href="/output.css" /> + <link + rel="icon" + href="/favicon-light.svg" + type="image/svg+xml" + media="(prefers-color-scheme: light)" + /> + <link + rel="icon" + href="/favicon-dark.svg" + type="image/svg+xml" + media="(prefers-color-scheme: dark)" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.css" + /> + <link + rel="stylesheet" + href="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/MarkerCluster.Default.css" + /> + </head> -<body data-theme="ram" class="font-spectral text-main-dark overflow-hidden bg-interlocked-hexagons h-[100dvh] flex flex-row ar-lt-0.8:flex-col"> - - <script> - function switchTheme(theme) { - if (theme == "" || typeof theme == "undefined") { - const currentTheme = document.body.getAttribute('data-theme'); - switch (currentTheme) { - case "olive": theme = "lettuce"; break; - case "lettuce": theme = "night"; break; - case "night": theme = "ram"; break; - case "ram": theme = "olive"; break; + <body + data-theme="ram" + class="font-m-plus text-main-hard overflow-hidden bg-interlocked-hexagons h-dvh" + > + <script> + function switchTheme(theme) { + if (theme == "" || typeof theme == "undefined") { + const currentTheme = + document.body.getAttribute("data-theme"); + switch (currentTheme) { + case "olive": + theme = "lettuce"; + break; + case "lettuce": + theme = "night"; + break; + case "night": + theme = "ram"; + break; + case "ram": + theme = "lilac"; + break; + case "lilac": + theme = "olive"; + break; + } } + localStorage.setItem("theme", theme); + document.body.setAttribute("data-theme", theme); } - localStorage.setItem('theme', theme); - document.body.setAttribute('data-theme', theme); - } - const savedTheme = localStorage.getItem('theme') || 'ram'; - switchTheme(savedTheme); - </script> + const savedTheme = localStorage.getItem("theme") || "lilac"; + switchTheme(savedTheme); + </script> - <script> - const clientTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + <script> + const clientTimeZone = + Intl.DateTimeFormat().resolvedOptions().timeZone; - function convertRemToPixels(rem) { - return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); - } - </script> + function convertRemToPixels(rem) { + return ( + rem * + parseFloat( + getComputedStyle(document.documentElement).fontSize, + ) + ); + } + </script> - <div id="sidebar" class="bg-sidebar flex sticky flex-col ar-gt-0.8:items-center shadow-2xl z-20 w-[5rem] ar-lt-0.8:w-[100dvw] h-[100dvh] ar-lt-0.8:h-[8dvh]"> - <div class="logo-custom ar-lt-0.8:hidden text-sidebar font-patua font-extrabold text-center relative z-20"> - saya.today - </div> - <div class="text-[3rem] ar-lt-0.8:text-[5dvh] text-stroke-(--sidebar-stroke-color) text-stroke-[0.1dvw] flex flex-col ar-lt-0.8:flex-row gap-0 relative z-20"> - <a href="/{{ .Lang }}" class="w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh]"> - <i class="fas fa-home flex w-full h-full content-center text-center text-sidebar hover:bg-background-dark transition-colors duration-300 rounded-lg"></i> - </a> - <a href="/{{ .Lang }}/blog" class="w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh]"> - <i class="fas fa-search flex w-full h-full content-center text-center text-sidebar hover:bg-background-dark transition-colors duration-300 rounded-lg"></i> - </a> - <a href="/{{ .Lang }}/map" class="w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh]"> - <i class="fas fa-map flex w-full h-full content-center text-center text-sidebar hover:bg-background-dark transition-colors duration-300 rounded-lg"></i> - </a> - <i onclick="toggleThemeWheel();" id="theme-button" class="fas fa-swatchbook flex w-[5rem] ar-lt-0.8:w-[8dvh] h-[5rem] ar-lt-0.8:h-[8dvh] content-center text-center text-sidebar hover:bg-background-dark cursor-pointer transition-colors duration-300 rounded-lg"></i> + <div + id="sidebar" + class="bg-sidebar block fixed shadow-[0_0_0.4rem_black] z-1001 w-fit h-auto" + > + <div class="text-[3rem] flex flex-col gap-0 relative z-1001"> + <a + href="./" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + class="fas fa-circle-left flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + href="/{{ .Lang }}" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + class="fas fa-home flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + href="/{{ .Lang }}/blog" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + class="fas fa-search flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <a + href="/{{ .Lang }}/map" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + class="fas fa-map flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + <div + id="toolbar-theme-button" + onclick="toggleThemeWheel()" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + id="toolbar-theme-button-content" + class="fas fa-swatchbook flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </div> + <a + href="/{{ .Lang }}/user" + id="sidebar-user-button" + class="themed-button themed-button-sidebar w-fit h-fit" + draggable="false" + > + <i + onclick="return changeUrl('/{{ .Lang }}/user');" + class="fas fa-user flex faw-18 h-18 content-center text-center text-sidebar" + ></i> + </a> + </div> + <div class="theme-wheel" id="theme-wheel"> + <div class="theme-icon" data-theme="olive"></div> + <div class="theme-icon" data-theme="lettuce"></div> + <div class="theme-icon" data-theme="night"></div> + <div class="theme-icon" data-theme="ram"></div> + <div class="theme-icon" data-theme="lilac"></div> + <div class="theme-pin"></div> + </div> </div> - <div class="theme-wheel" id="theme-wheel"> - <div class="theme-icon" data-theme="olive"></div> - <div class="theme-icon" data-theme="lettuce"></div> - <div class="theme-icon" data-theme="night"></div> - <div class="theme-icon" data-theme="ram"></div> - <div class="theme-pin"></div> - </div> - </div> - - <div id="map-container" class="bg-background-dark flex z-10 w-[calc(100dvw-5rem)] ar-lt-0.8:w-[100dvw] h-[100dvh] ar-lt-0.8:h-[92dvh]"></div> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/htmx/2.0.6/htmx.min.js"></script> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet/1.9.4/leaflet.js"></script> - <script src="https://f003.backblazeb2.com/file/sayana-static/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script> + <div + id="map-container" + class="bg-paper bg-background-dark flex z-10 w-full h-full" + ></div> - <script> - const themeWheel = document.getElementById('theme-wheel'); - const themeButton = document.getElementById('theme-button'); - const themeIcons = document.querySelectorAll('.theme-icon'); - const themeWheelRadius = convertRemToPixels(6); + <script src="{{ .StaticStorageBaseUrl }}/libs/htmx/2.0.6/htmx.min.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet/1.9.4/leaflet.js"></script> + <script src="{{ .StaticStorageBaseUrl }}/libs/leaflet.markercluster/1.4.1/leaflet.markercluster.js"></script> - themeIcons.forEach(icon => { - if (icon.getAttribute('data-theme') == savedTheme) { - icon.classList.add('active'); - } - }); - - function calculateVmax(percent) { - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; - - const largerDimension = Math.max(viewportWidth, viewportHeight); + <script> + const themeWheel = document.getElementById("theme-wheel"); + const themeButton = document.getElementById("toolbar-theme-button"); + const themeIcons = document.querySelectorAll(".theme-icon"); + const themeWheelRadius = convertRemToPixels(6); + const THUMB_BASE = "{{ .ThumbnailBaseUrl }}"; - const vmaxValue = (largerDimension / 100) * percent; - - return vmaxValue; - } + themeIcons.forEach((icon) => { + if (icon.getAttribute("data-theme") == savedTheme) { + icon.classList.add("active"); + } + }); - function positionThemeIcons() { - const sliceAngle = 360 / themeIcons.length; + function calculateVmax(percent) { + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; - themeIcons.forEach((icon, index) => { - const startAngle = index * sliceAngle; - const endAngle = (index + 1) * sliceAngle; + const largerDimension = Math.max(viewportWidth, viewportHeight); - const clipPath = createSliceClipPath(startAngle, endAngle, themeWheelRadius); - icon.style.clipPath = clipPath; - }); - } + const vmaxValue = (largerDimension / 100) * percent; - function createSliceClipPath(startAngle, endAngle, radius) { - const centerX = radius; - const centerY = radius; + return vmaxValue; + } - let points = `${centerX}px ${centerY}px`; + function positionThemeIcons() { + const sliceAngle = 360 / themeIcons.length; - const segments = Math.max(2, Math.ceil(Math.abs(endAngle - startAngle) / 5)); - const angleStep = (endAngle - startAngle) / segments; + themeIcons.forEach((icon, index) => { + const startAngle = index * sliceAngle; + const endAngle = (index + 1) * sliceAngle; - for (let i = 0; i <= segments; i++) { - const angle = ((startAngle + angleStep * i - 90) * Math.PI) / 180; - const x = centerX + Math.cos(angle) * radius; - const y = centerY + Math.sin(angle) * radius; - points += `, ${x}px ${y}px`; + const clipPath = createSliceClipPath( + startAngle, + endAngle, + themeWheelRadius, + ); + icon.style.clipPath = clipPath; + }); } - points += `, ${centerX}px ${centerY}px`; - - return `polygon(${points})`; - } + function createSliceClipPath(startAngle, endAngle, radius) { + const centerX = radius; + const centerY = radius; - function positionThemeWheel() { - const buttonRect = themeButton.getBoundingClientRect(); - const buttonCenterX = buttonRect.left + buttonRect.width / 2; - const buttonCenterY = buttonRect.top + buttonRect.height / 2; + let points = `${centerX}px ${centerY}px`; - const margin = 0; + const segments = Math.max( + 2, + Math.ceil(Math.abs(endAngle - startAngle) / 5), + ); + const angleStep = (endAngle - startAngle) / segments; - let wheelX = buttonCenterX - themeWheelRadius; - let wheelY = buttonCenterY - themeWheelRadius; + for (let i = 0; i <= segments; i++) { + const angle = + ((startAngle + angleStep * i - 90) * Math.PI) / 180; + const x = centerX + Math.cos(angle) * radius; + const y = centerY + Math.sin(angle) * radius; + points += `, ${x}px ${y}px`; + } - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; + points += `, ${centerX}px ${centerY}px`; - if (wheelX < margin) { - wheelX = margin; - } else if (wheelX + (themeWheelRadius * 2) > viewportWidth - margin) { - wheelX = viewportWidth - (themeWheelRadius * 2) - margin; + return `polygon(${points})`; } - if (wheelY < margin) { - wheelY = margin; - } else if (wheelY + (themeWheelRadius * 2) > viewportHeight - margin) { - wheelY = viewportHeight - (themeWheelRadius * 2) - margin; - } + function positionThemeWheel() { + const buttonRect = themeButton.getBoundingClientRect(); + const buttonCenterX = buttonRect.left + buttonRect.width / 2; + const buttonCenterY = buttonRect.top + buttonRect.height / 2; - themeWheel.style.left = wheelX + 'px'; - themeWheel.style.top = wheelY + 'px'; - } + const margin = 0; - function hideThemeWheel() { - themeWheel.classList.remove('active'); - } + let wheelX = buttonCenterX - themeWheelRadius; + let wheelY = buttonCenterY - themeWheelRadius; - function showThemeWheel() { - positionThemeWheel(); - positionThemeIcons(); - themeWheel.classList.add('active'); - } + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; - function toggleThemeWheel() { - if (themeWheel.classList.contains('active')) { - hideThemeWheel(); - } else { - showThemeWheel(); - } - } + if (wheelX < margin) { + wheelX = margin; + } else if ( + wheelX + themeWheelRadius * 2 > + viewportWidth - margin + ) { + wheelX = viewportWidth - themeWheelRadius * 2 - margin; + } - themeIcons.forEach(icon => { - icon.addEventListener('click', (e) => { - e.stopPropagation(); - const theme = icon.getAttribute('data-theme'); - switchTheme(theme); - themeIcons.forEach(icon => { - icon.classList.remove('active'); - }) - icon.classList.add('active'); - }); - }); + if (wheelY < margin) { + wheelY = margin; + } else if ( + wheelY + themeWheelRadius * 2 > + viewportHeight - margin + ) { + wheelY = viewportHeight - themeWheelRadius * 2 - margin; + } - themeWheel.addEventListener('mouseleave', hideThemeWheel); + themeWheel.style.left = wheelX + "px"; + themeWheel.style.top = wheelY + "px"; + } - document.addEventListener('click', (e) => { - if (!themeWheel.contains(e.target) && e.target !== themeButton) { - hideThemeWheel(); + function hideThemeWheel() { + themeWheel.classList.remove("active"); } - }); - </script> - <script> - var map; - var markers = L.markerClusterGroup(); + function showThemeWheel() { + positionThemeWheel(); + positionThemeIcons(); + themeWheel.classList.add("active"); + } - function createCustomIcon(color, borderColor = "var(--main-light-color)") { - const svgIcon = ` - <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> - <path d="M12 1C7.5 1 4 4.5 4 9c0 6.5 8 14 8 14s8-7.5 8-14c0-4.5-3.5-8-8-8zm0 11c-1.5 0-3-1.5-3-3s1.5-3 3-3 3 1.5 3 3-1.5 3-3 3z" - fill="${color}" - stroke="${borderColor}" - stroke-width="1" - stroke-linejoin="round"/> - </svg> - `; + function toggleThemeWheel() { + if (themeWheel.classList.contains("active")) { + hideThemeWheel(); + } else { + showThemeWheel(); + } + } - return L.divIcon({ - html: svgIcon, - className: 'custom-svg-marker', - iconSize: [32, 32], - iconAnchor: [16, 32], - popupAnchor: [0, -32] + themeIcons.forEach((icon) => { + icon.addEventListener("click", (e) => { + e.stopPropagation(); + const theme = icon.getAttribute("data-theme"); + switchTheme(theme); + themeIcons.forEach((icon) => { + icon.classList.remove("active"); + }); + icon.classList.add("active"); + }); }); - } - function initLocationMap() { - map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], 4); + themeWheel.addEventListener("mouseleave", hideThemeWheel); - L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { - maxZoom: 18, - attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' - }).addTo(map); + document.addEventListener("click", (e) => { + if ( + !themeWheel.contains(e.target) && + e.target !== themeButton && + !e.target.classList.contains("toolbar-theme-button-content") + ) { + hideThemeWheel(); + } + }); + </script> - map.addLayer(markers); - } + <script> + var map; + var markers = L.markerClusterGroup(); - function stringToColor(str) { - let hash = 0; - for (let i = 0; i < str.length; i++) { - hash = str.charCodeAt(i) + ((hash << 5) - hash); - } + function createCustomIcon(color, borderColor = "var(--color-main-soft)") { + const svgIcon = ` + <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> + <path d="M12 1C7.5 1 4 4.5 4 9c0 6.5 8 14 8 14s8-7.5 8-14c0-4.5-3.5-8-8-8zm0 11c-1.5 0-3-1.5-3-3s1.5-3 3-3 3 1.5 3 3-1.5 3-3 3z" + fill="${color}" + stroke="${borderColor}" + stroke-width="1" + stroke-linejoin="round"/> + </svg> + `; - let color = '#'; - for (let i = 0; i < 3; i++) { - const value = (hash >> (i * 8)) & 0xFF; - color += ('00' + value.toString(16)).substr(-2); + return L.divIcon({ + html: svgIcon, + className: 'custom-svg-marker', + iconSize: [32, 32], + iconAnchor: [16, 32], + popupAnchor: [0, -32] + }); } - return color; - } + function initLocationMap() { + map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], 4); - function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "") { - const titleColor = stringToColor(title); + L.tileLayer('https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', { + maxZoom: 20, + attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', + zoomControl: false + }).addTo(map); - if (relativeErrorMeters != 0) { - var circle = L.circle([x, y], { - color: titleColor, - fillColor: titleColor, - fillOpacity: 0.15, - radius: relativeErrorMeters + L.control.zoom({ + position: 'topright' }).addTo(map); + + map.addLayer(markers); } - var marker = L.marker([x, y], { - icon: createCustomIcon(titleColor), - title: title, - }).bindPopup(` - <div class="flex flex-row justify-center justify-items-center"> - <img onclick="location.href='${link}';" class="cursor-pointer object-cover rounded-[10%] select-none w-12 h-12 mr-1" src="https://f003.backblazeb2.com/file/sayana-photos/webp-320p/${thumbnail}.webp"> - <span><b><a href="${link}">${title}</a></b><br><a href="https://www.openstreetmap.org/#map=13/${x}/${y}">${x}, ${y}</a></span> - </div>`); + function markerToHsl(str, newCoef) { + let hash = 0; + for (let i = 0; i < str.length / 2; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + hash = str.charCodeAt(str.length - i - 1) + ((hash << 5) - hash); + hash %= 360; + } - markers.addLayer(marker); - } + return [hash, (20 + newCoef * 80).toFixed(0), (20 + newCoef * 75).toFixed(0)]; + } - document.addEventListener('DOMContentLoaded', () => { - initLocationMap(); - {{- range .MapMarkers }} - mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }}); - {{- end }} - }); + function mapAddMarker(x, y, relativeErrorMeters = 0, title = "", link = "", thumbnail = "", newCoef = 1) { + const [h, s, l] = markerToHsl(title, newCoef); + const color = `hsl(${h} ${s}% ${l}%)`; - window.addEventListener('resize', function() { - setTimeout(() => { - map.invalidateSize(); - }, 100); - }); - </script> + if (relativeErrorMeters != 0) { + var circle = L.circle([x, y], { + color: color, + fillColor: color, + fillOpacity: 0.15, + radius: relativeErrorMeters + }).addTo(map); + } -</body> + var marker = L.marker([x, y], { + icon: createCustomIcon(color), + title: title, + }).bindPopup(` + <div class="flex flex-row justify-center justify-items-center"> + <img onclick="location.href='${link}';" class="cursor-pointer object-cover rounded-[10%] select-none w-12 h-12 mr-1" src="${THUMB_BASE.replace('%s', thumbnail)}"> + <span><b><a href="${link}">${title}</a></b><br><a href="https://www.openstreetmap.org/#map=13/${x}/${y}">${x}, ${y}</a></span> + </div>`); + + markers.addLayer(marker); + } + + document.addEventListener('DOMContentLoaded', () => { + initLocationMap(); + {{- range .MapMarkers }} + mapAddMarker({{ .Lat }}, {{ .Long }}, {{ .AccuracyMeters }}, {{ .Title }}, {{ .PageLink }}, {{ .Thumbnail }}, {{ fdiv .Index (len $.MapMarkers) }}); + {{- end }} + }); + + window.addEventListener('resize', function() { + setTimeout(() => { + map.invalidateSize(); + }, 100); + }); + </script> + </body> </html> diff --git a/views/pages/home-page.html b/views/pages/home-page.html index 95093a0..c966fc2 100644 --- a/views/pages/home-page.html +++ b/views/pages/home-page.html @@ -1,21 +1,22 @@ {{ define "body" }} -<div class="bg-background-light flex flex-row ar-lt-0.6:flex-col w-full h-full inset-shadow overflow-y-auto text-base/[2] tracking-wide"> +<div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> +<div class="relative bg-paper bg-background-light font-gentium flex flex-row ar-lt-0.6:flex-col w-full h-full overflow-y-auto text-base/loose tracking-wide z-10"> <div class="ar-gt-0.6:flex-3/5 justify-center content-start p-8"> - <div class="flex flex-col justify-start items-start bg-background-dark mb-8"> - <p class="text-3xl/[2] sm:text-4xl/[2] lg:text-5xl/[2] ml-4 tracking-widest uppercase w-full font-spectral italic text-left - border-l-1.5 border-solid border-(--background-dark-color) - bg-gradient-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .L.HomePage.Hymn1 }}</p> - <hr class="w-full border-t-2 border-dotted border-main-dark mb-1"> - <p class="text-xl/[2] sm:text-2xl/[2] lg:text-3xl/[2] -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn2 }}</p> - <p class="text-xl/[2] sm:text-2xl/[2] lg:text-3xl/[2] -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn3 }}</p> - <p class="text-xl/[2] sm:text-2xl/[2] lg:text-3xl/[2] -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn4 }}</p> + <div class="flex flex-col justify-start items-start bg-paper bg-background-dark mb-8 shadow-[0.4rem_0.4rem_0.4rem_black]"> + <p class="text-3xl/loose sm:text-4xl/loose lg:text-5xl/loose tracking-widest uppercase w-full font-m-plus font-thin italic text-left + border-l-4 border-solid border-background-dark + bg-linear-to-r from-background-dark to-background-light">{{ .L.HomePage.Hymn1 }}</p> + <hr class="w-full border-t-2 border-dotted border-main-hard mb-1"> + <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn2 }}</p> + <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn3 }}</p> + <p class="text-xl/loose sm:text-2xl/loose lg:text-3xl/loose -indent-8 ml-12 p-1">{{ .L.HomePage.Hymn4 }}</p> </div> <div class="flex flex-col justify-center items-center"> - <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-spectral italic text-left - border-l-1.5 border-solid border-(--background-dark-color) - bg-gradient-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .L.HomePage.DidYouKnowThat }}</p> - <hr class="w-full border-t-2 border-dotted border-main-dark mb-1"> + <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-m-plus font-thin italic text-left + border-l-1.5 border-solid border-background-dark + bg-linear-to-r from-background-dark to-background-light">{{ .L.HomePage.DidYouKnowThat }}</p> + <hr class="w-full border-t-2 border-dotted border-main-hard mb-1"> <ul class="w-full text-left list-[circle]"> <li class="ml-4 mb-1">{{ index .FunFacts 0 }}</li> <li class="ml-4 mb-1">{{ index .FunFacts 1 }}</li> @@ -26,21 +27,22 @@ <div class="ar-gt-0.6:flex-2/5 justify-center content-start p-8"> <div class="flex flex-col justify-center items-start mb-4"> - <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-spectral italic text-left - border-l-1.5 border-solid border-(--background-dark-color) - bg-gradient-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .L.HomePage.Contacts }}</p> + <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-m-plus font-thin italic text-left + border-l-1.5 border-solid border-background-dark + bg-linear-to-r from-background-dark to-background-light">{{ .L.HomePage.Contacts }}</p> + <hr class="w-full border-t-2 border-dotted border-main-hard mb-1"> <a href="https://t.me/EarlInisMona"> - <i class="fa-brands fa-telegram w-4 h-4 mr-1"></i> + <i class="fa-brands fa-telegram faw-4 h-4 mr-1"></i> <span class="font-bold">Telegram:</span> <span>@EarlInisMona</span> </a> <a href="https://discord.com/users/356057270951346176"> - <i class="fa-brands fa-discord w-4 h-4 mr-1"></i> + <i class="fa-brands fa-discord faw-4 h-4 mr-1"></i> <span class="font-bold">Discord:</span> <span>montferrat</span> </a> <a href="mailto:saya.andy@posteo.com"> - <i class="fas fa-envelope w-4 h-4 mr-1"></i> + <i class="fas fa-envelope faw-4 h-4 mr-1"></i> <span class="font-bold">E-mail:</span> <span>saya.andy@posteo.com</span> </a> @@ -53,8 +55,8 @@ {{- range $_ := iterate .OutlineHeartCount }} <div class="home-page-heart z-50">♡</div> {{- end }} - <img src="https://f003.backblazeb2.com/file/sayana-static/home-page-gifs/{{ .GifName }}" - class="home-page-gif z-49 -rotate-12 max-w-[70%] max-h-[70%] border-4 border-(--background-dark-color) border-inset"> + <img src="{{ .GifUrl }}" + class="home-page-gif z-49 -rotate-12 max-w-[70%] max-h-[70%] border-4 border-background-dark border-inset"> </div> <script> @@ -100,19 +102,19 @@ </script> <div class="flex flex-col justify-center items-center"> - <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-spectral italic text-left - border-l-1.5 border-solid border-(--background-dark-color) - bg-gradient-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .L.HomePage.SidebarDescription }}</p> - <hr class="w-full border-t-2 border-dotted border-main-dark mb-1"> + <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-m-plus font-thin italic text-left + border-l-1.5 border-solid border-color-background-dark + bg-linear-to-r from-background-dark to-background-light">{{ .L.HomePage.SidebarDescription }}</p> + <hr class="w-full border-t-2 border-dotted border-main-hard mb-1"> <div class="w-full grid grid-cols-[1fr_4fr] content-center items-center - [&>*:nth-child(4n+2)]:bg-(--background-medium-color) [&>*:nth-child(4n+3)]:bg-(--background-medium-color)" style="container-type: inline-size;"> - <i class="fas fa-home w-full h-full text-[10cqi] content-center text-center text-main-dark"></i> + [&>*:nth-child(4n+2)]:bg-background-medium/50 [&>*:nth-child(4n+3)]:bg-background-medium/50" style="container-type: inline-size;"> + <i class="fas fa-home faw-full h-full text-[10cqi] content-center text-center text-main-hard"></i> <p class="p-1">{{ .L.HomePage.HomePageDescription }}</p> - <i class="fas fa-search w-full h-full text-[10cqi] content-center text-center text-main-dark"></i> + <i class="fas fa-search faw-full h-full text-[10cqi] content-center text-center text-main-hard"></i> <p class="p-1">{{ .L.HomePage.BlogSearchDescription }}</p> - <i class="fas fa-map w-full h-full text-[10cqi] content-center text-center text-main-dark"></i> + <i class="fas fa-map faw-full h-full text-[10cqi] content-center text-center text-main-hard"></i> <p class="p-1">{{ .L.HomePage.MarkerMapDescription }}</p> - <i class="fas fa-swatchbook w-full h-full text-[10cqi] content-center text-center text-main-dark"></i> + <i class="fas fa-swatchbook faw-full h-full text-[10cqi] content-center text-center text-main-hard"></i> <p class="p-1">{{ .L.HomePage.ThemeSwitchDescription }}</p> </div> </div> diff --git a/views/pages/language-pick.html b/views/pages/language-pick.html new file mode 100644 index 0000000..f4119e4 --- /dev/null +++ b/views/pages/language-pick.html @@ -0,0 +1,16 @@ +{{ define "body" }} +<div class="flex flex-col justify-center text-3xl h-full"> + {{- range .AvailableLanguages }} + <hr class="border-t-2 border-dotted border-main-hard"> + <div class="nth-[2n+1_of_div]:bg-background-medium/50 nth-[2n_of_div]:bg-background-light/50 p-2.5 flex flex-row justify-center justify-items-center"> + <a href="/{{ .Name }}" class="w-12 h-12 basis-12 shrink-0 mr-4"> + <img onclick="return changeUrl('{{ .Name }}');" class="w-full h-full aspect-square cursor-pointer object-cover rounded-md select-none" src="{{ .Flag }}"> + </a> + <a href="/{{ .Name }}" onclick="return changeUrl('{{ .Name }}');" class="cursor-pointer"> + {{ .Alt }} + </a> + </div> + {{- end }} + <hr class="border-t-2 border-dotted border-main-hard"> +</div> +{{ end }} diff --git a/views/pages/robots.txt b/views/pages/robots.txt new file mode 100644 index 0000000..aaef3f4 --- /dev/null +++ b/views/pages/robots.txt @@ -0,0 +1,7 @@ +User-agent: * +Allow: / +Disallow: /api/v1/email/ +Disallow: /ru/user/ +Disallow: /en/user/ + +Sitemap: {{ .CanonicalEndpoint }}/sitemap.xml
\ No newline at end of file diff --git a/views/pages/sitemap.xml b/views/pages/sitemap.xml new file mode 100644 index 0000000..1474a56 --- /dev/null +++ b/views/pages/sitemap.xml @@ -0,0 +1,10 @@ +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> + {{- range .URLs }} + <url> + <loc>{{ $.CanonicalEndpoint }}{{ .Loc }}</loc> + <lastmod>{{ .LastModified.UTC.Format "2006-01-02T15:04:05Z" }}</lastmod> + <changefreq>{{ .ChangeFreq }}</changefreq> + <priority>{{ printf "%.1f" .Priority }}</priority> + </url> + {{- end }} +</urlset> diff --git a/views/pages/unsubscribe-page.html b/views/pages/unsubscribe-page.html new file mode 100644 index 0000000..a917746 --- /dev/null +++ b/views/pages/unsubscribe-page.html @@ -0,0 +1,77 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta name="robots" content="noindex,nofollow" /> + <link + rel="icon" + href="/favicon-light.svg" + type="image/svg+xml" + media="(prefers-color-scheme: light)" + /> + <link + rel="icon" + href="/favicon-dark.svg" + type="image/svg+xml" + media="(prefers-color-scheme: dark)" + /> + <title>{{ .L.UnsubscribePage.Header }} // SAYA.UZ</title> + <style> + .bg-paper { + background-image: url(data:image/webp;base64,UklGRi4BAABXRUJQVlA4ICIBAABQCQCdASqAAIAAPm02lUmkIqIkIImIgA2JaW3rMFmGDAOKLAoJtP591ByFR74Tun2DP+Lygpfsz5FH7+cKgxi2Islfd03i5AJdXUfU0SDc8SXYAAD++hlzeYAJWQEF8ZdkJdlcA+5btZg0FMGaso8SHoBph0qAOQysElJ5C5SJFNoKIV1J550UyL7Y9Gh0/c/uzLnBmr8lvz7k1WeA81H4S5ViU/wBqa7aM1m8D9rDtrk8cSTqx+xWF6MS8UfZGSsks6CT/igtgv6syFN9uNA3LjnjiCJKYOvFcMkX+jB2578GWVo3nyLaM1QNPYCxn3TiUbnf7U0MdD2NsBpg1+Jd1wu6yW/U+goN+IQS6Nt4RMWltVcZHlxrneKJi0SobO0AAA==); + background-size: 128px; + background-repeat: repeat; + background-blend-mode: multiply; + } + </style> + </head> + + <body + style=" + width: 100dvw; + height: 100dvh; + display: flex; + flex-direction: column; + position: fixed; + " + > + <div + class="bg-paper" + style="flex: 3 0 0; background-color: rgba({{ .StatusColor }}, 0.1)" + ></div> + + <div class="bg-paper" style="flex: 1 0 0; align-content: center"> + <p + style=" + position: relative; + width: 100%; + margin-inline: auto; + text-align: center; + font-size: 2rem; + " + > + {{ .StatusEmoji }} + </p> + </div> + + <div class="bg-paper" style="flex: 1 0 0; align-content: center"> + <p + style=" + position: relative; + width: 100%; + margin-inline: auto; + text-align: center; + font-size: 1.5rem; + " + > + {{ .StatusText }} + </p> + </div> + + <div + class="bg-paper" + style="flex: 3 0 0; background-color: rgba({{ .StatusColor }}, 0.2)" + ></div> + </body> +</html> diff --git a/views/pages/user-page.html b/views/pages/user-page.html new file mode 100644 index 0000000..0dd006a --- /dev/null +++ b/views/pages/user-page.html @@ -0,0 +1,206 @@ +{{ define "body" }} +<div class="absolute w-full h-full inset-0 inset-shadow-[0_0_0.4rem_black] pointer-events-none z-20"></div> +<div class="relative bg-paper bg-background-light flex flex-col px-8 py-4"> + <form id="email-form" class="w-full mb-2 flex flex-col relative crossable" + hx-get="/api/v1/email/is-in-verification" hx-target="#email-message" hx-swap="outerHTML" hx-trigger="load" hx-indicator="this"> + <div class="flex flex-row w-full mb-4 z-21"> + <div class="flex flex-col w-full sm:flex-3/5 grow-0"> + <label class="block text-main-medium font-bold mb-1 z-22"> + {{ .L.UserProfile.EmailHeader }} + </label> + <input class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-[0_0_0.4rem_black] focus:outline-none focus:bg-background-light focus:text-main-hard z-22" + id="email-input" name="email" type="text" value="{{ .Email }}" placeholder="{{ .L.UserProfile.TypeEmail }}" maxlength="64" + data-validated-email="{{ .Email }}" onchange="checkIfValidated(this);"> + </div> + </div> + <button class="w-max mx-auto bg-main-hard hover:bg-main-medium active:bg-main-soft active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-background-light font-bold py-2 px-4 rounded z-22" + hx-post="/api/v1/email/send-verification-code" hx-target="#email-message" hx-swap="outerHTML" hx-indicator="#email-form"> + {{ .L.UserProfile.SendCodeButton }} + </button> + </form> + <div id="email-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-[0_0_0.4rem_black] py-2 px-4 text-center"></div> + + <hr class="my-2 border-t-4 border-dotted border-main-hard"> + + <form id="email-verification-form" class="w-full mb-2 flex flex-col relative hidden crossable" + {{ if .EmailCode }}hx-post="/api/v1/email/verify" hx-target="#verification-message" hx-swap="outerHTML" hx-trigger="load" hx-indicator="this"{{ end }}> + <div class="flex flex-row w-full mb-4 z-21"> + <div class="flex flex-col w-full sm:flex-2/5 grow-0"> + <label class="block text-main-medium font-bold ml-1 z-22"> + {{ .L.UserProfile.VerificationCodeHeader }} + </label> + <input class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-[0_0_0.4rem_black] focus:outline-none focus:bg-background-light focus:text-main-hard z-22" + name="email_code" type="text" value="{{ .EmailCode }}" placeholder="0123456789ABCDEF" maxlength="16" autocapitalize="characters"> + </div> + </div> + <button class="w-max mx-auto bg-main-hard hover:bg-main-medium active:bg-main-soft active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-background-light font-bold py-2 px-4 rounded z-22" + hx-post="/api/v1/email/verify" hx-target="#verification-message" hx-swap="outerHTML" hx-indicator="#email-verification-form"> + {{ .L.UserProfile.VerifyButton }} + </button> + </form> + <div id="verification-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-[0_0_0.4rem_black] py-2 px-4 text-center"></div> +</div> + +<hr class="my-4 border-t-4 border-dotted border-main-hard"> + +<div class="bg-paper bg-background-light flex flex-col inset-shadow-[0_0_0.4rem_black] px-8 py-4"> + <form id="subs-form" class="w-full mb-2 flex flex-col relative crossable"> + <div class="flex flex-col w-full mb-4 z-21"> + <label class="block text-main-medium font-bold mb-1 z-22"> + {{ .L.UserProfile.SubscriptionHeader }} + </label> + <div class="flex flex-col sm:flex-row w-full"> + <div class="m-1 w-1/3"> + <label><input class="mr-1" type="radio" id="tagsNone" name="tags" value="none" {{ if eq .TagsPicked "none" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ .L.UserProfile.TagsNone }}</label> + </div> + <div class="m-1 w-1/3"> + <label><input class="mr-1" type="radio" id="tagsAll" name="tags" value="all" {{ if eq .TagsPicked "all" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ .L.UserProfile.TagsAll }}</label> + </div> + <div class="m-1 w-1/3"> + <label><input class="mr-1" type="radio" id="tagsSpecific" name="tags" value="specific" {{ if eq .TagsPicked "specific" }}checked{{ end }} onclick="toggleTagsPick(this);">{{ .L.UserProfile.TagsSpecific }}</label> + </div> + </div> + <div id="pick-tags-form" class="flex flex-col w-full relative crossable"> + <input type="text" class="bg-background-medium appearance-none border-background-medium rounded ml-6 mr-4 py-2 px-4 text-main-medium leading-tight inset-shadow-[0_0_0.4rem_black] focus:outline-none focus:bg-background-light focus:text-main-hard z-22" + list="existing-tags" onchange="addTag(this.value);" onkeydown="if (event.keyCode === 13) {addTag(this.value); this.value=''; return false;}"> + <datalist id="existing-tags"> + {{- range .ExistingTags }} + <option value="{{ .Name }}"> + {{- end }} + </datalist> + <div id="tags-picked-list" class="bg-paper bg-background-dark flex flex-wrap inset-shadow-[0_0_0.4rem_black] p-2 m-4 w-full"> + {{- range .TagsPickedList }} + <div id="tagPicked{{ . }}" class="bg-paper bg-main-hard text-background-light m-2 flex flex-row"> + <p class="py-1 px-2">{{ . }}</p> + <p class="py-1 px-2 bg-paper bg-main-soft cursor-pointer" onclick="htmx.remove(htmx.find('#tagPicked{{ . }}'));">✘</p> + <input type="hidden" name="tags_picked" value="{{ . }}"> + </div> + {{- end }} + </div> + </div> + <button class="w-max mx-auto bg-color-main-hard hover:bg-main-medium active:bg-color-main-soft active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-background-light font-bold py-2 px-4 rounded z-22" + hx-put="/api/v1/subs" hx-target="#subs-message" hx-swap="outerHTML" hx-indicator="#subs-form"> + {{ .L.UserProfile.SaveButton }} + </button> + </div> + </form> + <div id="subs-message" class="bg-paper bg-background-dark text-main-soft z-26 w-full inset-shadow-[0_0_0.4rem_black] py-2 px-4 text-center"></div> +</div> + +<script> +htmx.on("htmx:beforeSwap", (e) => { + if (e.detail.xhr.status === 422) { + e.detail.shouldSwap = true; + e.detail.isError = false; + } +}); + +var formAllowTimeout; +var verificationHideTimeout; + +var url = new URL(window.location.href); +url.searchParams.delete('email_code'); +window.history.pushState({}, '', url.toString()); + +htmx.on("htmx:afterSwap", (e) => { + emailMessage = htmx.find("#email-message"); + verificationMessage = htmx.find("#verification-message"); + emailForm = htmx.find("#email-form"); + emailInput = htmx.find("#email-input"); + emailVerificationForm = htmx.find("#email-verification-form"); + subsForm = htmx.find("#subs-form"); + tryAgainTimeDiff = parseInt(emailMessage.dataset.strikedEndTime || "0") - new Date().getTime(); + verificationWaitingTimeDiff = parseInt(emailMessage.dataset.codeExpiryTime || "0") - new Date().getTime(); + + if (tryAgainTimeDiff > 0) { + emailForm.classList.add("disabled"); + formAllowTimeout = setTimeout(() => { + emailForm.classList.remove("disabled"); + emailMessage.classList.add("hidden"); + }, tryAgainTimeDiff); + } else { + emailForm.classList.remove("disabled"); + } + + if (verificationWaitingTimeDiff > 0) { + verificationHideTimeout = setTimeout(() => emailVerificationForm.classList.add("hidden"), verificationWaitingTimeDiff); + emailVerificationForm.classList.remove("hidden"); + } else { + emailVerificationForm.classList.add("hidden"); + } + + if ((verificationMessage.dataset.hideVerificationPanel || "false") == "true") { + emailVerificationForm.classList.add("hidden"); + } + + if (emailInput.dataset.validatedEmail != "") { + subsForm.classList.remove("disabled"); + } else { + subsForm.classList.add("disabled"); + } +}); + +function checkIfValidated(elem) { + if ((elem.dataset.validatedEmail != "") && (elem.value == elem.dataset.validatedEmail)) { + elem.classList.add("!bg-green-300/20"); + } else { + elem.classList.remove("!bg-green-300/20"); + } +} + +checkIfValidated(htmx.find("#email-input")); + +function toggleTagsPick(elem) { + const pickTagsForm = htmx.find("#pick-tags-form"); + if (elem.value === "specific" && elem.checked) { + pickTagsForm.classList.remove("disabled"); + } else { + pickTagsForm.classList.add("disabled"); + } +} + +toggleTagsPick(htmx.find("#tagsSpecific")); + +function addTag(name) { + name = name.trim(); + const subsMessage = htmx.find("#subs-message"); + const existingTags = htmx.findAll(htmx.find("#existing-tags"), "option"); + found = [...existingTags].find(opt => opt.value===name); + if (!found) { + subsMessage.classList.remove("hidden"); + subsMessage.innerHTML = "{{ .L.UserProfile.TagDoesNotExist }}".replace("{}", name); + return; + } + + const tagsPickedList = htmx.find("#tags-picked-list"); + const subscribedTags = htmx.findAll(tagsPickedList, "div input"); + found = [...subscribedTags].find(opt => opt.value===name); + if (found) { + subsMessage.classList.remove("hidden"); + subsMessage.innerHTML = "{{ .L.UserProfile.TagAlreadyAdded }}".replace("{}", name); + return; + } + + tagsPickedList.innerHTML += ` + <div id="tagPicked${name}" class="bg-paper bg-main-hard text-background-light m-2 flex flex-row"> + <p class="py-1 px-2">${name}</p> + <p class="py-1 px-2 bg-paper bg-main-soft cursor-pointer" onclick="htmx.remove(htmx.find('#tagPicked${name}'));">✘</p> + <input type="hidden" name="tags_picked" value="${name}"> + </div> + `; +} + +htmx.on("htmx:configRequest", (e) => { + const params = e.detail.parameters; + + if (Array.isArray(params.tags_picked)) { + tags_join = params.tags_picked[0] || ""; + for (i = 1; i < params.tags_picked.length; i++) { + tags_join += ","; + tags_join += params.tags_picked[i]; + } + params.tags_picked = tags_join; + } +}) +</script> +{{ end }}
\ No newline at end of file diff --git a/views/partials/blog-page-like-button.html b/views/partials/blog-page-like-button.html index f23ef9d..d8238f4 100644 --- a/views/partials/blog-page-like-button.html +++ b/views/partials/blog-page-like-button.html @@ -1,6 +1,5 @@ -<button - hx-put="/api/v1/like" hx-vals='{"like": {{ not .Liked }}}' hx-target="this" hx-swap="outerHTML" hx-trigger="click" - class="text-sm/[0.9] shrink-0 font-spectral text-left px-1 py-0.5 {{ if .Liked }}bg-main-light hover:bg-main-medium text-background-dark border-inset{{ else }}bg-main-dark hover:bg-main-medium text-background-light border-outset{{ end }} cursor-pointer border-2 border-background-dark"> - <i class="fas fa-thumbs-up w-3 h-3"></i> - <span class="italic ml-0.5 text-background-light">({{ .LikedCount }})</span> +<button hx-put="/api/v1/like" hx-vals='{"like": {{ not .Liked }}}' hx-target="this" hx-swap="outerHTML" hx-trigger="click" + class="themed-button themed-button-like flex flex-col shrink-0 w-fit h-fit mr-2 my-auto{{ if .Liked }} active{{ end }}"> + <i class="fas fa-thumbs-up faw-8 h-fit py-0.5 content-center text-center"></i> + <div class="h-fit px-0.5 content-center bg-background-medium text-main-hard">{{ .LikedCount }}</div> </button>
\ No newline at end of file diff --git a/views/partials/catalogue-blog-cards.html b/views/partials/catalogue-blog-cards.html index 5c3bb95..0a93797 100644 --- a/views/partials/catalogue-blog-cards.html +++ b/views/partials/catalogue-blog-cards.html @@ -1,27 +1,29 @@ {{- range .BlogPages }} -<hr class="first-of-type:hidden my-1 border-t-2 border-dotted border-main-dark"> -<div class="m-1 flex flex-row justify-center justify-items-center"> - <a href="{{ .ArticleLink }}" class="w-24 h-24 basis-24 shrink-0 my-2.5 mr-2"> - <img onclick="return changeUrl('{{ .ArticleLink }}');" class="w-full h-full aspect-square cursor-pointer object-cover rounded-lg select-none" src="https://f003.backblazeb2.com/file/sayana-photos/webp-320p/{{ .Thumbnail }}.webp"> - </a> - <div class="grow my-0.5 mx-1 flex flex-col justify-center"> - <div class="flex flex-row gap-2 max-w-[100%]"> - <a href="{{ .ArticleLink }}" onclick="return changeUrl('{{ .ArticleLink }}');" class="grow text-base cursor-pointer"> +<hr class="first-of-type:hidden border-t-2 border-dotted border-main-hard"> +<div class="w-full flex flex-row items-stretch"> + <div class="relative min-h-24 flex flex-col w-28 overflow-hidden shrink-0 mask-r-from-70% mask-r-to-100%"> + <img onclick="return changeUrl('{{ .ArticleLink }}');" class="h-full min-h-24 w-full cursor-pointer select-none object-cover block absolute" src="{{ printf $.ThumbnailBaseUrl .Thumbnail }}"> + <div class="w-full h-6 flex flex-row mt-auto relative"> + <div class="flex-1/2 flex flex-row justify-center select-none bg-linear-180 {{ if .Liked }}from-accent-deep/40 to-accent-deep/80 text-background-dark{{ else }}from-accent-light/40 to-accent-light/80 text-main-hard{{ end }}"> + <svg viewBox="0 0 24 24"><use href="#icon-like"/></svg> + <span>{{ .LikeCount }}</span> + </div> + <div class="flex-1/2 flex flex-row justify-center select-none bg-linear-180 {{ if .Viewed }}from-accent-deep/40 to-accent-deep/80 text-background-dark{{ else }}from-accent-light/40 to-accent-light/80 text-main-hard{{ end }}"> + <svg viewBox="0 0 24 24"><use href="#icon-view"/></svg> + <span>{{ .ViewCount }}</span> + </div> + </div> + </div> + <div class="grow w-full h-fit flex flex-col justify-center my-auto py-1 px-1"> + <div class="flex flex-row gap-2 max-w-full"> + <a href="{{ .ArticleLink }}" onclick="return changeUrl('{{ .ArticleLink }}');" class="grow-2 text-base cursor-pointer font-m-plus"> <span class="font-extrabold">{{ .Title }}</span> - <span class="font-extrabold select-none text-secondary">//</span> - <span class="italic text-secondary">{{ .ActionDate }}</span> + <span class="italic">[{{ .ActionDate }}]</span> </a> - <p class="basis-56 text-base italic text-secondary text-right">{{ .PublishedTime }}</p> + <p class="basis-56 grow text-base italic text-secondary text-right">{{ .PublishedTime }}</p> </div> - <p class="text-base text-main-dark">{{ .ShortDescription }}</p> - <p class="text-base text-secondary italic"> - <i class="fas fa-thumbs-up w-4 h-4"></i> - <span>{{ .LikeCount }}</span> - <span class="font-extrabold select-none">//</span> - <i class="fas fa-eye w-4 h-4"></i> - <span>{{ .ViewCount }}</span> - </p> - <p class="text-base text-secondary">{{ $.L.TagsLabel }} {{ template "catalogue-blog-card-tags.html" . }}</p> + <p class="font-gentium">{{ .ShortDescription }}</p> + <p class="font-m-plus text-sm">{{ $.L.TagsLabel }} {{ template "catalogue-blog-card-tags.html" . }}</p> </div> </div> {{- end }}
\ No newline at end of file diff --git a/views/partials/general-page-body.html b/views/partials/general-page-body.html index c3aa3eb..d697c14 100644 --- a/views/partials/general-page-body.html +++ b/views/partials/general-page-body.html @@ -1,2 +1 @@ -<title>{{ .Title }} // SAYA TODAY</title> {{ block "body" . }}{{ end }}
\ No newline at end of file diff --git a/views/partials/general-page-footer.html b/views/partials/general-page-footer.html index 8979423..3f5f9ee 100644 --- a/views/partials/general-page-footer.html +++ b/views/partials/general-page-footer.html @@ -1,7 +1,23 @@ {{ block "footer" . }}{{ end }} -<p class="grow content-center text-base/[1] font-spectral italic text-right text-secondary mr-1"> - All the photos on <a href="https://saya.today/">saya.today</a> are licensed under <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a> by <a href="https://t.me/EarlInisMona">Saya Andy</a> © {{ .PublishedYear }} +<p + class="grow content-center text-base/[1] font-gentium font-thin italic text-right text-secondary mr-1" +> + All the photos on <a href="https://saya.uz/">saya.uz</a> are licensed under + <a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a> + by <a href="https://t.me/EarlInisMona">Saya Andy</a> © {{ .PublishedYear }} </p> -<img class="max-w-4 max-h-4 self-center" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" alt="CC"> -<img class="max-w-4 max-h-4 self-center" src="https://mirrors.creativecommons.org/presskit/icons/by.svg" alt="BY"> -<img class="max-w-4 max-h-4 self-center" src="https://mirrors.creativecommons.org/presskit/icons/sa.svg" alt="SA">
\ No newline at end of file +<img + class="max-w-4 max-h-4 self-center" + src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" + alt="CC" +/> +<img + class="max-w-4 max-h-4 self-center" + src="https://mirrors.creativecommons.org/presskit/icons/by.svg" + alt="BY" +/> +<img + class="max-w-4 max-h-4 self-center" + src="https://mirrors.creativecommons.org/presskit/icons/sa.svg" + alt="SA" +/> diff --git a/views/partials/general-page-header.html b/views/partials/general-page-header.html index e5488b4..995ebf3 100644 --- a/views/partials/general-page-header.html +++ b/views/partials/general-page-header.html @@ -1,10 +1,9 @@ -<div {{ if .PublishedDate }} hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="#published-date" hx-swap="innerHTML" hx-trigger="load" {{ end }} - class="flex flex-row mb-2"> - <a href="./" class="w-(--text-4xl) h-(--text-4xl) mx-1 my-auto"> - <i onclick="return changeUrl('/../', true);" class="fas fa-circle-left hover:bg-main-dark hover:bg-opacity-10 cursor-pointer transition-colors duration-300 rounded-md text-4xl"></i> - </a> - <p class="text-4xl font-spectral italic font-extrabold select-none my-auto pl-3 pr-3">//</p> - <p class="text-4xl font-spectral italic text-left mt-auto">{{ .Title }}</p> - <p id="published-date" class="grow text-2xl font-spectral text-secondary text-right mt-auto">{{ .PublishedDate }}</p> +<title>{{ .Title }} // SAYA.UZ</title> +<div class="flex flex-row mb-2"> + {{ block "header" . }}{{ end }} + <p + class="text-4xl font-m-plus font-bold italic text-shadow-[0_2px_2px_var(--color-main-soft)] text-left my-auto" + > + {{ .Title }} + </p> </div> -{{ block "header" . }}{{ end }} diff --git a/views/partials/personal-page-status.html b/views/partials/personal-page-status.html new file mode 100644 index 0000000..078fb24 --- /dev/null +++ b/views/partials/personal-page-status.html @@ -0,0 +1,12 @@ +<div id="{{ .StatusId }}" class='z-26 w-full inset-shadow-[0_0_0.4rem_black] + {{if eq .Status "OK"}}bg-green-700 text-green-100 + {{else if eq .Status "Failed"}}bg-red-700 text-red-100 + {{else}}bg-paper bg-background-dark text-main-soft{{end}} + {{if eq .Message ""}}hidden{{end}} + py-2 px-4 text-center' + {{- range $_, $val := .DataAttributes }} + {{ $val }} + {{- end }} + > + {{ .Message }} +</div>
\ No newline at end of file |