Compare commits
16 Commits
0509565c2b
...
main
Author | SHA1 | Date | |
---|---|---|---|
134159f79c | |||
489156fe87 | |||
83a24557bd | |||
facb304a52 | |||
1e8f48b6fe | |||
1473b676f4 | |||
74f875460a | |||
cd639a830b | |||
cde3faa3c6 | |||
e8f9ecc241 | |||
1a059a005a | |||
ff087b0577 | |||
13820f58eb | |||
f7eb6cc95d | |||
fec60900f5 | |||
96ead1a38f |
11
Cargo.toml
11
Cargo.toml
@ -6,11 +6,11 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
askama = { version = "0.12", features = ["with-axum", "mime", "mime_guess"] }
|
||||
askama_axum = "0.4.0"
|
||||
axum = "0.7.3"
|
||||
askama = { version = "0.14", features = ["with-axum", "mime", "mime_guess"] }
|
||||
askama_axum = "0.5.0"
|
||||
axum = "0.8.0"
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
pulldown-cmark = { version = "0.12" }
|
||||
pulldown-cmark = { version = "0.13" }
|
||||
gray_matter = "0.2.6"
|
||||
rss = "2.0.7"
|
||||
serde = "1.0.195"
|
||||
@ -25,7 +25,8 @@ anyhow = "1.0.86"
|
||||
rayon = "1.10.0"
|
||||
syntect = "5.2.0"
|
||||
indoc = "2.0.5"
|
||||
askama_escape = "0.10.3"
|
||||
askama_escape = "0.13.0"
|
||||
mime_guess = "2.0.5"
|
||||
|
||||
[build]
|
||||
rustflags = ["-Z", "threads=8"]
|
||||
|
29
src/feed.rs
29
src/feed.rs
@ -1,9 +1,10 @@
|
||||
use axum::http::{header, StatusCode};
|
||||
use axum::response::IntoResponse;
|
||||
use chrono::Utc;
|
||||
use rss::{ChannelBuilder, GuidBuilder, Item, ItemBuilder};
|
||||
use rss::{ChannelBuilder, EnclosureBuilder, GuidBuilder, Item, ItemBuilder};
|
||||
|
||||
use crate::blog_posts::blog_post_model::{BlogPostMetadata, BLOG_POST_PATH};
|
||||
use crate::filters::{parse_markdown, truncate_md};
|
||||
use crate::post_utils::post_listing::get_post_list;
|
||||
|
||||
pub async fn render_rss_feed() -> Result<impl IntoResponse, StatusCode> {
|
||||
@ -25,8 +26,30 @@ pub async fn render_rss_feed() -> Result<impl IntoResponse, StatusCode> {
|
||||
ItemBuilder::default()
|
||||
.title(Some(post.metadata.title))
|
||||
.link(Some(format!("https://michalvanko.dev/blog/{}", post.slug)))
|
||||
// TODO Description should be just a preview
|
||||
.description(None)
|
||||
.description({
|
||||
let truncated =
|
||||
truncate_md(&post.body, 2).unwrap_or("Can't parse post body".to_string());
|
||||
let parsed_md = parse_markdown(&truncated)
|
||||
.unwrap_or("Can't process truncated post body".to_string());
|
||||
Some(parsed_md)
|
||||
})
|
||||
.content({
|
||||
let parsed_md = parse_markdown(&post.body)
|
||||
.unwrap_or("Can't process full post body".to_string());
|
||||
Some(parsed_md)
|
||||
})
|
||||
.enclosure({
|
||||
post.metadata.thumbnail.map(|src| {
|
||||
let mime_type = mime_guess::from_path(&src)
|
||||
.first()
|
||||
.map(|mime| mime.to_string())
|
||||
.unwrap_or("image".to_string());
|
||||
EnclosureBuilder::default()
|
||||
.url(src)
|
||||
.mime_type(mime_type)
|
||||
.build()
|
||||
})
|
||||
})
|
||||
.guid(Some(
|
||||
GuidBuilder::default()
|
||||
.value(format!("https://michalvanko.dev/blog/{}", post.slug))
|
||||
|
@ -56,6 +56,5 @@ async fn main() {
|
||||
// - fotos
|
||||
// THINK deploy to alula? rather then katelyn? can be change whenever
|
||||
//
|
||||
// TODO view page transitions
|
||||
// TODO cookbook
|
||||
// TODO remove m-logo-svg from justfile and mention it in some article!!! WRITE SOME NEW ARTICLES
|
||||
// TODO remove m-logo-svg from justfile and mention it in some article!!!
|
||||
|
@ -3,7 +3,9 @@ use axum::extract::OriginalUri;
|
||||
use axum::{extract::Path, http::StatusCode};
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
use crate::blog_posts::blog_post_model::BLOG_POST_PATH;
|
||||
use crate::blog_posts::blog_post_model::{Segment, BLOG_POST_PATH};
|
||||
use crate::post_utils::post_listing::get_post_list;
|
||||
use crate::post_utils::post_parser::ParseResult;
|
||||
use crate::{
|
||||
blog_posts::blog_post_model::BlogPostMetadata, components::site_header::Link, filters,
|
||||
post_utils::post_parser::parse_post,
|
||||
@ -18,10 +20,11 @@ pub struct BlogPostTemplate {
|
||||
pub body: String,
|
||||
pub date: DateTime<Utc>,
|
||||
pub tags: Vec<String>,
|
||||
pub segment: String,
|
||||
pub segment: Segment,
|
||||
pub header_props: HeaderProps,
|
||||
pub slug: String,
|
||||
pub thumbnail: Option<String>,
|
||||
pub recommended_posts: Vec<ParseResult<BlogPostMetadata>>,
|
||||
}
|
||||
|
||||
pub async fn render_blog_post(
|
||||
@ -29,22 +32,27 @@ pub async fn render_blog_post(
|
||||
OriginalUri(original_uri): OriginalUri,
|
||||
) -> Result<BlogPostTemplate, StatusCode> {
|
||||
let path = format!("{}/{}.md", BLOG_POST_PATH, post_id);
|
||||
let parse_post = parse_post::<BlogPostMetadata>(&path);
|
||||
let parsed = parse_post.await?;
|
||||
let post = parse_post::<BlogPostMetadata>(&path).await?;
|
||||
let segment = if original_uri.to_string().starts_with("/blog") {
|
||||
"blog"
|
||||
Segment::Blog
|
||||
} else if original_uri.to_string().starts_with("/broadcasts") {
|
||||
"broadcasts"
|
||||
Segment::Broadcasts
|
||||
} else {
|
||||
"blog"
|
||||
Segment::Blog
|
||||
};
|
||||
|
||||
let mut recommended_posts = get_recommended_posts(&segment, &post.metadata.tags).await?;
|
||||
recommended_posts.retain(|post| post.slug != post_id);
|
||||
recommended_posts.sort_by_key(|post| post.slug.to_string());
|
||||
recommended_posts.reverse();
|
||||
let recommended_posts = recommended_posts.into_iter().take(2).collect::<Vec<_>>();
|
||||
|
||||
let header_props = match segment {
|
||||
"blog" => HeaderProps::with_back_link(Link {
|
||||
Segment::Blog => HeaderProps::with_back_link(Link {
|
||||
href: "/blog".to_string(),
|
||||
label: "All posts".to_string(),
|
||||
}),
|
||||
"broadcasts" => HeaderProps::with_back_link(Link {
|
||||
Segment::Broadcasts => HeaderProps::with_back_link(Link {
|
||||
href: "/broadcasts".to_string(),
|
||||
label: "All broadcasts".to_string(),
|
||||
}),
|
||||
@ -52,13 +60,40 @@ pub async fn render_blog_post(
|
||||
};
|
||||
|
||||
Ok(BlogPostTemplate {
|
||||
title: parsed.metadata.title,
|
||||
date: parsed.metadata.date,
|
||||
tags: parsed.metadata.tags,
|
||||
body: parsed.body,
|
||||
slug: parsed.slug,
|
||||
segment: segment.to_string(),
|
||||
thumbnail: parsed.metadata.thumbnail,
|
||||
title: post.metadata.title,
|
||||
date: post.metadata.date,
|
||||
tags: post.metadata.tags,
|
||||
body: post.body,
|
||||
slug: post.slug,
|
||||
segment,
|
||||
thumbnail: post.metadata.thumbnail,
|
||||
header_props,
|
||||
recommended_posts,
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_recommended_posts(
|
||||
segment: &Segment,
|
||||
tags: &[String],
|
||||
) -> Result<Vec<ParseResult<BlogPostMetadata>>, StatusCode> {
|
||||
let posts = get_post_list::<BlogPostMetadata>(BLOG_POST_PATH).await?;
|
||||
|
||||
let recommended_posts = posts
|
||||
.into_iter()
|
||||
.filter(|post| {
|
||||
let is_same_segment = post
|
||||
.metadata
|
||||
.segments
|
||||
.iter()
|
||||
.any(|post_segment| post_segment == segment);
|
||||
let has_same_tags = post
|
||||
.metadata
|
||||
.tags
|
||||
.iter()
|
||||
.any(|post_tag| tags.contains(post_tag));
|
||||
is_same_segment && has_same_tags
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(recommended_posts)
|
||||
}
|
||||
|
@ -44,6 +44,12 @@ pub async fn render_contact() -> Result<ContactPageTemplate, StatusCode> {
|
||||
title: "YouTube channel".to_string(),
|
||||
svg: "youtube".to_string(),
|
||||
},
|
||||
ContactLink {
|
||||
href: "https://mastodon.online/@michalvankodev".to_string(),
|
||||
label: "Mastodon".to_string(),
|
||||
title: "Mastodon profile".to_string(),
|
||||
svg: "mastodon".to_string(),
|
||||
},
|
||||
ContactLink {
|
||||
href: "https://instagram.com/michalvankodev".to_string(),
|
||||
label: "Instagram".to_string(),
|
||||
|
1
static/svg/input/mastodon.svg
Normal file
1
static/svg/input/mastodon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5 0 0 1 -.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6v-114.2c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"/></svg>
|
After Width: | Height: | Size: 811 B |
@ -107,7 +107,7 @@
|
||||
}
|
||||
|
||||
/*
|
||||
! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com
|
||||
! tailwindcss v3.4.15 | MIT License | https://tailwindcss.com
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -756,6 +756,10 @@ video {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
@ -804,6 +808,10 @@ video {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.max-h-\[236px\] {
|
||||
max-height: 236px;
|
||||
}
|
||||
@ -832,6 +840,10 @@ video {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.max-w-\[24rem\] {
|
||||
max-width: 24rem;
|
||||
}
|
||||
@ -962,44 +974,53 @@ video {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.border-l {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.border-blue-300 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(130 195 247 / var(--tw-border-opacity));
|
||||
border-color: rgb(130 195 247 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
|
||||
.border-blue-500 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(23 137 224 / var(--tw-border-opacity));
|
||||
border-color: rgb(23 137 224 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
|
||||
.border-blue-950 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(11 39 70 / var(--tw-border-opacity));
|
||||
border-color: rgb(11 39 70 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
|
||||
.border-slate-300 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(203 213 225 / var(--tw-border-opacity));
|
||||
border-color: rgb(203 213 225 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
|
||||
.border-l-slate-300 {
|
||||
--tw-border-opacity: 1;
|
||||
border-left-color: rgb(203 213 225 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-blue-100 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(225 239 253 / var(--tw-bg-opacity));
|
||||
background-color: rgb(225 239 253 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-blue-50 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(241 247 254 / var(--tw-bg-opacity));
|
||||
background-color: rgb(241 247 254 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-pink-200 {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 207 247 / var(--tw-bg-opacity));
|
||||
background-color: rgb(255 207 247 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.fill-blue-900 {
|
||||
@ -1140,42 +1161,42 @@ video {
|
||||
|
||||
.text-blue-500 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(23 137 224 / var(--tw-text-opacity));
|
||||
color: rgb(23 137 224 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-blue-700 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(10 86 154 / var(--tw-text-opacity));
|
||||
color: rgb(10 86 154 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-blue-900 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity));
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-blue-950 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(11 39 70 / var(--tw-text-opacity));
|
||||
color: rgb(11 39 70 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-pink-900 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(119 24 89 / var(--tw-text-opacity));
|
||||
color: rgb(119 24 89 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-pink-950 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(80 2 56 / var(--tw-text-opacity));
|
||||
color: rgb(80 2 56 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-slate-600 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(71 85 105 / var(--tw-text-opacity));
|
||||
color: rgb(71 85 105 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-slate-800 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(30 41 59 / var(--tw-text-opacity));
|
||||
color: rgb(30 41 59 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.no-underline {
|
||||
@ -1190,14 +1211,14 @@ video {
|
||||
|
||||
a {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(146 22 110 / var(--tw-text-opacity));
|
||||
color: rgb(146 22 110 / var(--tw-text-opacity, 1));
|
||||
text-decoration-line: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(23 137 224 / var(--tw-text-opacity));
|
||||
color: rgb(23 137 224 / var(--tw-text-opacity, 1));
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter;
|
||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter;
|
||||
@ -1236,7 +1257,7 @@ strong {
|
||||
}
|
||||
h1 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity));
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
h1 {
|
||||
@ -1276,7 +1297,7 @@ strong {
|
||||
}
|
||||
h2 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity));
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
h2 {
|
||||
@ -1336,7 +1357,7 @@ strong {
|
||||
}
|
||||
h3 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity));
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
h3 {
|
||||
@ -1396,7 +1417,7 @@ strong {
|
||||
}
|
||||
h4 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity));
|
||||
color: rgb(16 62 106 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
h4 {
|
||||
@ -1450,7 +1471,7 @@ strong {
|
||||
}
|
||||
p {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(2 6 23 / var(--tw-text-opacity));
|
||||
color: rgb(2 6 23 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
p {
|
||||
@ -1530,7 +1551,7 @@ strong {
|
||||
}
|
||||
figcaption {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(12 73 128 / var(--tw-text-opacity));
|
||||
color: rgb(12 73 128 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
figcaption {
|
||||
@ -1574,7 +1595,7 @@ strong {
|
||||
}
|
||||
table {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(226 232 240 / var(--tw-border-opacity));
|
||||
border-color: rgb(226 232 240 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
table {
|
||||
font-size: 0.875rem;
|
||||
@ -1600,11 +1621,11 @@ strong {
|
||||
}
|
||||
thead {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(225 239 253 / var(--tw-bg-opacity));
|
||||
background-color: rgb(225 239 253 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
tbody {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
|
||||
background-color: rgb(248 250 252 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
td,
|
||||
th {
|
||||
@ -1636,7 +1657,7 @@ strong {
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(241 245 249 / var(--tw-bg-opacity));
|
||||
background-color: rgb(241 245 249 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
blockquote {
|
||||
margin-left: 1.5rem;
|
||||
@ -1650,11 +1671,11 @@ strong {
|
||||
}
|
||||
blockquote {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(215 34 169 / var(--tw-border-opacity));
|
||||
border-color: rgb(215 34 169 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
blockquote {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 244 253 / var(--tw-bg-opacity));
|
||||
background-color: rgb(255 244 253 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
blockquote {
|
||||
padding-top: 0.25rem;
|
||||
@ -1680,7 +1701,7 @@ strong {
|
||||
}
|
||||
p {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(71 85 105 / var(--tw-text-opacity));
|
||||
color: rgb(71 85 105 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
p {
|
||||
@ -1697,11 +1718,11 @@ strong {
|
||||
}
|
||||
:not(pre) code {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(130 195 247 / var(--tw-border-opacity));
|
||||
border-color: rgb(130 195 247 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
:not(pre) code {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(225 239 253 / var(--tw-bg-opacity));
|
||||
background-color: rgb(225 239 253 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
:not(pre) code {
|
||||
padding-left: 0.25rem;
|
||||
@ -1717,7 +1738,7 @@ strong {
|
||||
}
|
||||
:not(pre) code {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(119 24 89 / var(--tw-text-opacity));
|
||||
color: rgb(119 24 89 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
:not(pre) code {
|
||||
@ -1785,7 +1806,7 @@ strong {
|
||||
ul,
|
||||
ol {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(2 6 23 / var(--tw-text-opacity));
|
||||
color: rgb(2 6 23 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
ul,
|
||||
@ -1993,6 +2014,10 @@ article a:visited {
|
||||
|
||||
/* } */
|
||||
|
||||
.first\:border-l-0:first-child {
|
||||
border-left-width: 0px;
|
||||
}
|
||||
|
||||
.visited\:text-blue-950:visited {
|
||||
color: rgb(11 39 70 );
|
||||
}
|
||||
@ -2003,7 +2028,7 @@ article a:visited {
|
||||
|
||||
.hover\:bg-pink-200:hover {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 207 247 / var(--tw-bg-opacity));
|
||||
background-color: rgb(255 207 247 / var(--tw-bg-opacity, 1));
|
||||
}
|
||||
|
||||
.hover\:fill-blue-400:hover {
|
||||
@ -2186,6 +2211,14 @@ article a:visited {
|
||||
margin-top: 5rem;
|
||||
}
|
||||
|
||||
.lg\:mt-8 {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.lg\:mb-10 {
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.lg\:block {
|
||||
display: block;
|
||||
}
|
||||
@ -2228,6 +2261,11 @@ article a:visited {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.lg\:text-4xl {
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
@ -2259,10 +2297,30 @@ article a:visited {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.xl\:flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.xl\:grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.xl\:hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.xl\:auto-cols-auto {
|
||||
grid-auto-columns: auto;
|
||||
}
|
||||
|
||||
.xl\:auto-cols-fr {
|
||||
grid-auto-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.xl\:grid-flow-col {
|
||||
grid-auto-flow: column;
|
||||
}
|
||||
|
||||
.xl\:grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
@ -2271,15 +2329,36 @@ article a:visited {
|
||||
grid-template-columns: 1fr 2fr;
|
||||
}
|
||||
|
||||
.xl\:justify-start {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.xl\:gap-8 {
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.xl\:gap-5 {
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.xl\:gap-10 {
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.xl\:gap-x-32 {
|
||||
-moz-column-gap: 8rem;
|
||||
column-gap: 8rem;
|
||||
}
|
||||
|
||||
.xl\:border-l {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.xl\:border-slate-300 {
|
||||
--tw-border-opacity: 1;
|
||||
border-color: rgb(203 213 225 / var(--tw-border-opacity, 1));
|
||||
}
|
||||
|
||||
.xl\:text-4xl {
|
||||
font-size: 2.25rem;
|
||||
line-height: 2.5rem;
|
||||
@ -2293,6 +2372,10 @@ article a:visited {
|
||||
.xl\:font-medium {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.xl\:first\:border-l-0:first-child {
|
||||
border-left-width: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
@ -41,7 +41,7 @@
|
||||
bgAnimationTimeline.add({
|
||||
targets: '#m-logo #bg-gradient #bg-stop',
|
||||
offset: "0%",
|
||||
stopColor: "rgba(216, 246, 255, 1)",
|
||||
stopColor: "rgba(216, 246, 255, 0.8)",
|
||||
easing: 'easeInQuint',
|
||||
duration: 123,
|
||||
})
|
||||
@ -50,6 +50,18 @@
|
||||
offset: "100%",
|
||||
easing: 'easeOutExpo',
|
||||
duration: 333,
|
||||
complete: (animation) => {
|
||||
const target = animation.animatables[0].target
|
||||
target.setAttribute("stop-color", "rgba(216, 246, 255, 0.8)")
|
||||
anime({
|
||||
targets: '#m-logo #bg-gradient #bg-stop',
|
||||
stopColor: "rgba(216, 246, 255, 0.3)",
|
||||
easing: 'easeOutQuad',
|
||||
duration: 3333,
|
||||
direction: 'alternate',
|
||||
loop: true,
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 160)
|
||||
|
@ -16,6 +16,10 @@
|
||||
href="/feed.xml"
|
||||
/>
|
||||
|
||||
<!-- Mastodon -->
|
||||
<link rel="me" href="https://mastodon.online/@michalvankodev" />
|
||||
<meta name="fediverse:creator" content="@michalvankodev@mastodon.online">
|
||||
|
||||
{% block og_meta %}
|
||||
<meta property="og:title" content="{% block title %} {{ title }} {% endblock %} @michalvankodev" />
|
||||
<meta property="og:type" content="website" />
|
||||
|
@ -33,7 +33,32 @@
|
||||
</article>
|
||||
|
||||
<!-- TODO: Next recommendations for reading -->
|
||||
<!-- TODO: Bact to all posts -->
|
||||
<!-- TODO: Back to all posts -->
|
||||
|
||||
{# footer #}
|
||||
<footer class="max-w-maxindex mx-auto">
|
||||
|
||||
{% if recommended_posts.len() > 0 %}
|
||||
<section id="recommended-articles">
|
||||
<hr class="border-slate-300 m-5 md:my-8">
|
||||
|
||||
<h2 class="m-5 text-2xl md:text-2xl lg:text-4xl lg:mt-8 text-blue-900 lg:mb-10 font-bold">Further reading</h2>
|
||||
<ul class="mx-5 xl:flex xl:justify-start xl:gap-10">
|
||||
{% for post in recommended_posts %}
|
||||
<li class="flex-1">
|
||||
{% include "components/blog_post_preview.html" %}
|
||||
<hr class="border-slate-300 my-5 md:my-8 xl:hidden">
|
||||
</li>
|
||||
{% if !loop.last %}
|
||||
<div class="h-auto w-0 border-l border-slate-300 hidden xl:block"></div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<section class="text-center my-3 md:text-lg">
|
||||
<a href="/blog">see all blog posts</a>
|
||||
</section>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
<!-- xl:border-l xl:border-slate-300 xl:first:border-l-0 xl: -->
|
||||
|
@ -14,6 +14,6 @@
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</symbol><symbol id="linkedin" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"/></symbol><symbol id="mail" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z"/></symbol><symbol id="person-chalkboard" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M192 96a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm-8 384V352h16V480c0 17.7 14.3 32 32 32s32-14.3 32-32V192h56 64 16c17.7 0 32-14.3 32-32s-14.3-32-32-32H384V64H576V256H384V224H320v48c0 26.5 21.5 48 48 48H592c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48H368c-26.5 0-48 21.5-48 48v80H243.1 177.1c-33.7 0-64.9 17.7-82.3 46.6l-58.3 97c-9.1 15.1-4.2 34.8 10.9 43.9s34.8 4.2 43.9-10.9L120 256.9V480c0 17.7 14.3 32 32 32s32-14.3 32-32z"/></symbol><symbol id="phone" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M164.9 24.6c-7.7-18.6-28-28.5-47.4-23.2l-88 24C12.1 30.2 0 46 0 64C0 311.4 200.6 512 448 512c18 0 33.8-12.1 38.6-29.5l24-88c5.3-19.4-4.6-39.7-23.2-47.4l-96-40c-16.3-6.8-35.2-2.1-46.3 11.6L304.7 368C234.3 334.7 177.3 277.7 144 207.3L193.3 167c13.7-11.2 18.4-30 11.6-46.3l-40-96z"/></symbol><symbol id="rss" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24c137 0 248 111 248 248c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-200-200-200c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24c83.9 0 152 68.1 152 152c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104c-13.3 0-24-10.7-24-24zm0 120a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></symbol><symbol id="tiktok-2" viewBox="0 0 24 24">
|
||||
</symbol><symbol id="linkedin" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z"/></symbol><symbol id="mail" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z"/></symbol><symbol id="mastodon" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5 0 0 1 -.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6v-114.2c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"/></symbol><symbol id="person-chalkboard" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M192 96a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm-8 384V352h16V480c0 17.7 14.3 32 32 32s32-14.3 32-32V192h56 64 16c17.7 0 32-14.3 32-32s-14.3-32-32-32H384V64H576V256H384V224H320v48c0 26.5 21.5 48 48 48H592c26.5 0 48-21.5 48-48V48c0-26.5-21.5-48-48-48H368c-26.5 0-48 21.5-48 48v80H243.1 177.1c-33.7 0-64.9 17.7-82.3 46.6l-58.3 97c-9.1 15.1-4.2 34.8 10.9 43.9s34.8 4.2 43.9-10.9L120 256.9V480c0 17.7 14.3 32 32 32s32-14.3 32-32z"/></symbol><symbol id="phone" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M164.9 24.6c-7.7-18.6-28-28.5-47.4-23.2l-88 24C12.1 30.2 0 46 0 64C0 311.4 200.6 512 448 512c18 0 33.8-12.1 38.6-29.5l24-88c5.3-19.4-4.6-39.7-23.2-47.4l-96-40c-16.3-6.8-35.2-2.1-46.3 11.6L304.7 368C234.3 334.7 177.3 277.7 144 207.3L193.3 167c13.7-11.2 18.4-30 11.6-46.3l-40-96z"/></symbol><symbol id="rss" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24c137 0 248 111 248 248c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-200-200-200c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24c83.9 0 152 68.1 152 152c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104c-13.3 0-24-10.7-24-24zm0 120a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></symbol><symbol id="tiktok-2" viewBox="0 0 24 24">
|
||||
<path d="M16.8217 5.1344C16.0886 4.29394 15.6479 3.19805 15.6479 2H14.7293M16.8217 5.1344C17.4898 5.90063 18.3944 6.45788 19.4245 6.67608C19.7446 6.74574 20.0786 6.78293 20.4266 6.78293V10.2191C18.645 10.2191 16.9932 9.64801 15.6477 8.68211V15.6707C15.6477 19.1627 12.8082 22 9.32386 22C7.50043 22 5.85334 21.2198 4.69806 19.98C3.64486 18.847 2.99994 17.3331 2.99994 15.6707C2.99994 12.2298 5.75592 9.42509 9.17073 9.35079M16.8217 5.1344C16.8039 5.12276 16.7861 5.11101 16.7684 5.09914M6.9855 17.3517C6.64217 16.8781 6.43802 16.2977 6.43802 15.6661C6.43802 14.0734 7.73249 12.7778 9.32394 12.7778C9.62087 12.7778 9.9085 12.8288 10.1776 12.9124V9.40192C9.89921 9.36473 9.61622 9.34149 9.32394 9.34149C9.27287 9.34149 8.86177 9.36884 8.81073 9.36884M14.7244 2H12.2097L12.2051 15.7775C12.1494 17.3192 10.8781 18.5591 9.32386 18.5591C8.35878 18.5591 7.50971 18.0808 6.98079 17.3564" stroke="#000000" stroke-linejoin="round"/>
|
||||
</symbol><symbol id="tiktok" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M448 209.9a210.1 210.1 0 0 1 -122.8-39.3V349.4A162.6 162.6 0 1 1 185 188.3V278.2a74.6 74.6 0 1 0 52.2 71.2V0l88 0a121.2 121.2 0 0 0 1.9 22.2h0A122.2 122.2 0 0 0 381 102.4a121.4 121.4 0 0 0 67 20.1z"/></symbol><symbol id="twitch" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M391.2 103.5H352.5v109.7h38.6zM285 103H246.4V212.8H285zM120.8 0 24.3 91.4V420.6H140.1V512l96.5-91.4h77.3L487.7 256V0zM449.1 237.8l-77.2 73.1H294.6l-67.6 64v-64H140.1V36.6H449.1z"/></symbol><symbol id="twitter" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"/></symbol><symbol id="youtube" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M549.7 124.1c-6.3-23.7-24.8-42.3-48.3-48.6C458.8 64 288 64 288 64S117.2 64 74.6 75.5c-23.5 6.3-42 24.9-48.3 48.6-11.4 42.9-11.4 132.3-11.4 132.3s0 89.4 11.4 132.3c6.3 23.7 24.8 41.5 48.3 47.8C117.2 448 288 448 288 448s170.8 0 213.4-11.5c23.5-6.3 42-24.2 48.3-47.8 11.4-42.9 11.4-132.3 11.4-132.3s0-89.4-11.4-132.3zm-317.5 213.5V175.2l142.7 81.2-142.7 81.2z"/></symbol></svg>
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
Reference in New Issue
Block a user