summaryrefslogtreecommitdiff
path: root/views/pages
diff refs
from: back
to: back
| flip
diff options
context:
space:
mode:
Diffstat (limited to 'views/pages')
-rw-r--r--views/pages/blog-catalogue.html111
-rw-r--r--views/pages/blog-page.html34
-rw-r--r--views/pages/global-map.html121
-rw-r--r--views/pages/home-page.html46
-rw-r--r--views/pages/language-pick.html16
-rw-r--r--views/pages/unsubscribe-page.html30
-rw-r--r--views/pages/user-page.html206
7 files changed, 432 insertions, 132 deletions
diff --git a/views/pages/blog-catalogue.html b/views/pages/blog-catalogue.html
index 3845275..d2c74ae 100644
--- a/views/pages/blog-catalogue.html
+++ b/views/pages/blog-catalogue.html
@@ -1,61 +1,68 @@
{{ 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>
+<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-[10dvh] ar-gt-0.8:max-w-[20dvh] ar-gt-0.8:w-fit ar-lt-0.8:w-full ar-lt-0.8:max-h-[30%] ar-gt-0.8:mr-6">
+ <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-background-light">
+ <div class="flex flex-col overflow-y-auto">
+ <fieldset>
+ <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();"> {{ .L.BlogSearch.ChooseAllTags }}</label>
+ </div>
+ {{- range .Tags }}
+ <div class="m-1">
+ <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>
+ </div>
+ {{- end }}
</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.OrderByHeader }}</legend>
+ <div class="flex flex-col ar-lt-0.8:grid grid-cols-3 grid-rows-2 grid-flow-col">
+ <div class="m-1">
+ <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-1">
+ <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-1">
+ <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-1">
+ <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-1">
+ <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-1">
+ <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>
- {{- 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-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>
+ </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-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-background-light p-6">
+ Loading...
+ </div>
</div>
+
</div>
<script>
diff --git a/views/pages/blog-page.html b/views/pages/blog-page.html
index c9c9e91..c81406f 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>
+<div class="relative bg-background-light flex flex-col px-8 py-4 overflow-y-auto">
+ <p class="max-xs:block [@media(max-height:32rem)]:block hidden font-andika text-2xl font-bold italic text-main-hard tracking-[.0125rem] mb-1">{{ .Title }}</p>
+ <p class="block grow text-lg font-andika text-secondary">
+ <b>{{ .L.Metadata.Published }}</b>:
+ <span hx-get="/api/v1/tz" hx-vals='js:{timestamp: "{{ .PublishedDate }}", tz: clientTimeZone}' hx-target="this" hx-swap="innerHTML" hx-trigger="load">
+ {{ .PublishedDate }}
+ </span>
+ </p>
+ <p class="block grow text-lg font-andika text-secondary"><b>{{ .L.Metadata.Action }}</b>: {{ .ActionDate }}</p>
+ <p class="block grow text-md font-andika italic 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: '&copy; <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: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
if (relativeErrorMeters != 0) {
@@ -46,11 +53,16 @@
{{ .MapLocationY }},
{{ .MapLocationAreaMeters }}
), 1000);
+
+ var lightbox = GLightbox({
+ selector: '.glightbox',
+ moreLength: 0
+ });
</script>
{{- end }}
</div>
{{ 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 83078c9..7d788d6 100644
--- a/views/pages/global-map.html
+++ b/views/pages/global-map.html
@@ -5,9 +5,6 @@
<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">
@@ -15,7 +12,7 @@
<link rel="stylesheet" href="https://f003.backblazeb2.com/file/sayana-static/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">
+<body data-theme="ram" class="font-andika text-main-hard overflow-hidden bg-interlocked-hexagons h-dvh">
<script>
function switchTheme(theme) {
@@ -25,46 +22,58 @@
case "olive": theme = "lettuce"; break;
case "lettuce": theme = "night"; break;
case "night": theme = "ram"; break;
- case "ram": theme = "olive"; break;
+ case "ram": theme = "lilac"; break;
+ case "lilac": theme = "olive"; break;
}
}
localStorage.setItem('theme', theme);
document.body.setAttribute('data-theme', theme);
}
- const savedTheme = localStorage.getItem('theme') || 'ram';
+ const savedTheme = localStorage.getItem('theme') || 'lilac';
switchTheme(savedTheme);
</script>
<script>
const clientTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+
+ 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>
+ <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 w-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 w-18 h-18 content-center text-center text-sidebar"></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 href="/{{ .Lang }}/blog" class="themed-button themed-button-sidebar w-fit h-fit" draggable="false">
+ <i class="fas fa-search flex w-18 h-18 content-center text-center text-sidebar"></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 href="/{{ .Lang }}/map" class="themed-button themed-button-sidebar w-fit h-fit" draggable="false">
+ <i class="fas fa-map flex w-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 w-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 w-18 h-18 content-center text-center text-sidebar"></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>
<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 id="map-container" class="bg-background-dark flex z-10 w-[95dvw] ar-lt-0.8:w-[100dvw] h-[100dvh] ar-lt-0.8:h-[92dvh]"></div>
+ <div id="map-container" class="bg-background-dark flex z-10 w-full h-full"></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>
@@ -72,8 +81,9 @@
<script>
const themeWheel = document.getElementById('theme-wheel');
- const themeButton = document.getElementById('theme-button');
+ const themeButton = document.getElementById('toolbar-theme-button');
const themeIcons = document.querySelectorAll('.theme-icon');
+ const themeWheelRadius = convertRemToPixels(6);
themeIcons.forEach(icon => {
if (icon.getAttribute('data-theme') == savedTheme) {
@@ -93,49 +103,61 @@
}
function positionThemeIcons() {
- const icons = themeWheel.querySelectorAll('.theme-icon');
- const iconCount = icons.length;
- const vmax = calculateVmax(1);
- const wheelRadius = 7.5*vmax; // Radius of the wheel
- const iconRadius = 4*vmax; // Distance from center to place icons
- const iconSize = 3*vmax; // Half of icon size for centering
-
- icons.forEach((icon, index) => {
- const angle = (index * 360 / iconCount) - 90;
- const angleRad = (angle * Math.PI) / 180;
+ const sliceAngle = 360 / themeIcons.length;
- const x = Math.cos(angleRad) * iconRadius + wheelRadius - iconSize;
- const y = Math.sin(angleRad) * iconRadius + wheelRadius - iconSize;
+ themeIcons.forEach((icon, index) => {
+ const startAngle = index * sliceAngle;
+ const endAngle = (index + 1) * sliceAngle;
- icon.style.left = x + 'px';
- icon.style.top = y + 'px';
+ const clipPath = createSliceClipPath(startAngle, endAngle, themeWheelRadius);
+ icon.style.clipPath = clipPath;
});
}
+ function createSliceClipPath(startAngle, endAngle, radius) {
+ const centerX = radius;
+ const centerY = radius;
+
+ let points = `${centerX}px ${centerY}px`;
+
+ 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`;
+ }
+
+ points += `, ${centerX}px ${centerY}px`;
+
+ return `polygon(${points})`;
+ }
+
function positionThemeWheel() {
const buttonRect = themeButton.getBoundingClientRect();
const buttonCenterX = buttonRect.left + buttonRect.width / 2;
const buttonCenterY = buttonRect.top + buttonRect.height / 2;
- const wheelRadius = calculateVmax(7.5);
- const margin = calculateVmax(1);
+ const margin = 0;
- let wheelX = buttonCenterX - wheelRadius;
- let wheelY = buttonCenterY - wheelRadius;
+ let wheelX = buttonCenterX - themeWheelRadius;
+ let wheelY = buttonCenterY - themeWheelRadius;
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
if (wheelX < margin) {
wheelX = margin;
- } else if (wheelX + (wheelRadius * 2) > viewportWidth - margin) {
- wheelX = viewportWidth - (wheelRadius * 2) - margin;
+ } else if (wheelX + (themeWheelRadius * 2) > viewportWidth - margin) {
+ wheelX = viewportWidth - (themeWheelRadius * 2) - margin;
}
if (wheelY < margin) {
wheelY = margin;
- } else if (wheelY + (wheelRadius * 2) > viewportHeight - margin) {
- wheelY = viewportHeight - (wheelRadius * 2) - margin;
+ } else if (wheelY + (themeWheelRadius * 2) > viewportHeight - margin) {
+ wheelY = viewportHeight - (themeWheelRadius * 2) - margin;
}
themeWheel.style.left = wheelX + 'px';
@@ -175,7 +197,7 @@
themeWheel.addEventListener('mouseleave', hideThemeWheel);
document.addEventListener('click', (e) => {
- if (!themeWheel.contains(e.target) && e.target !== themeButton) {
+ if (!themeWheel.contains(e.target) && e.target !== themeButton && !e.target.classList.contains("toolbar-theme-button-content")) {
hideThemeWheel();
}
});
@@ -185,7 +207,7 @@
var map;
var markers = L.markerClusterGroup();
- function createCustomIcon(color, borderColor = "var(--main-light-color)") {
+ function createCustomIcon(color, borderColor = "var(--main-soft-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"
@@ -208,9 +230,14 @@
function initLocationMap() {
map = L.map('map-container').setView([{{ .MapLocationLat }}, {{ .MapLocationLong }}], 4);
- L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
- maxZoom: 18,
- attribution: '&copy; <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: &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
+ zoomControl: false
+ }).addTo(map);
+
+ L.control.zoom({
+ position: 'topright'
}).addTo(map);
map.addLayer(markers);
diff --git a/views/pages/home-page.html b/views/pages/home-page.html
index 95093a0..6d397fa 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-background-light 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-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-andika italic text-left
+ border-l-4 border-solid border-(--background-dark-color)
+ bg-linear-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-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
+ <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-andika 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">
+ bg-linear-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-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,9 +27,10 @@
<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
+ <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-andika 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>
+ bg-linear-to-r from-(--background-dark-color) to-(--background-light-color)">{{ .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>
<span class="font-bold">Telegram:</span>
@@ -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
+ <p class="text-xl sm:text-2xl lg:text-3xl p-1 w-full font-andika 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">
+ bg-linear-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-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-color)/50 [&>*:nth-child(4n+3)]:bg-(--background-medium-color)/50" style="container-type: inline-size;">
+ <i class="fas fa-home w-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 w-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 w-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 w-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..775c18e
--- /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-color)/50 nth-[2n_of_div]:bg-(--background-light-color)/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/unsubscribe-page.html b/views/pages/unsubscribe-page.html
new file mode 100644
index 0000000..3876818
--- /dev/null
+++ b/views/pages/unsubscribe-page.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>{{ .L.UnsubscribePage.Header }} // SAYA TODAY</title>
+<style>
+ html {
+ --noise-image: url(data:image/png;base64,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);
+ }
+</style>
+</head>
+
+<body style="width: 100dvw; height: 100dvh; display: flex; flex-direction: column; position: fixed">
+
+ <div style='flex: 3 0 0; background-image: var(--noise-image); background-color: rgba({{ .StatusColor }}, 0.1)'></div>
+
+ <div style="flex: 1 0 0; background-image: var(--noise-image); align-content: center;">
+ <p style="position: relative; width: 100%; margin-inline: auto; text-align: center; font-size: 2rem;">{{ .StatusEmoji }}</p>
+ </div>
+
+ <div style="flex: 1 0 0; background-image: var(--noise-image); align-content: center;">
+ <p style="position: relative; width: 100%; margin-inline: auto; text-align: center; font-size: 1.5rem;">{{ .StatusText }}</p>
+ </div>
+
+ <div style='flex: 3 0 0; background-image: var(--noise-image); 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..fda7e66
--- /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-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-color) appearance-none border-(--background-medium-color) 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-color) 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-color) hover:bg-(--main-medium-color) active:bg-(--main-soft-color) active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-(--background-light-color) 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-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-color) appearance-none border-(--background-medium-color) 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-color) 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-color) hover:bg-(--main-medium-color) active:bg-(--main-soft-color) active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-(--background-light-color) 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-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-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-color) appearance-none border-(--background-medium-color) 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-color) 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-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-main-hard text-background-light m-2 flex flex-row">
+ <p class="py-1 px-2">{{ . }}</p>
+ <p class="py-1 px-2 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-(--main-hard-color) hover:bg-(--main-medium-color) active:bg-(--main-soft-color) active:inset-shadow-[0.2em_0.2em_0.2rem_black] transition-colors transition-300 text-(--background-light-color) 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-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-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-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