39 Commits

Author SHA1 Message Date
4e367d73a0 order projects
Some checks failed
test / cargo test (push) Failing after 57s
2024-09-27 13:34:07 +02:00
85c98fac56 showcase page
Some checks failed
test / cargo test (push) Failing after 1m12s
2024-09-27 11:52:25 +02:00
f62673d6a7 yutube and instagram
Some checks failed
test / cargo test (push) Failing after 59s
2024-09-26 14:59:18 +02:00
221d5cef23 twitch and tiktok hover effects
Some checks failed
test / cargo test (push) Failing after 1m5s
2024-09-26 14:12:55 +02:00
dc1a01c352 yes yes yes
Some checks failed
test / cargo test (push) Failing after 1m6s
2024-09-26 12:19:45 +02:00
20d1314925 lol hide instagram between lg-xl
Some checks failed
test / cargo test (push) Failing after 1m12s
2024-09-25 16:21:35 +02:00
1eb0f55264 holy shit the responsiveness
Some checks failed
test / cargo test (push) Failing after 1m24s
2024-09-25 12:55:05 +02:00
fc26d77bfc few style 2024-09-25 11:05:19 +02:00
2949429fa4 calendly link
Some checks failed
test / cargo test (push) Failing after 58s
2024-09-20 16:31:00 +02:00
52bd4e5590 Contact page favicon
Some checks failed
test / cargo test (push) Failing after 1m3s
2024-09-20 12:35:30 +02:00
377aee315e mooooar moooar pretty fixes
Some checks failed
test / cargo test (push) Failing after 1m4s
2024-09-20 11:51:29 +02:00
f3c4df4458 strong bold medium and heading tag handling 2024-09-20 10:44:59 +02:00
6650366e60 better theme colors
Some checks failed
test / cargo test (push) Failing after 1m3s
2024-09-19 12:06:35 +02:00
0937de96dd coloooors 2024-09-18 18:36:54 +02:00
fb63a2011b fuckit 2024-09-18 16:30:16 +02:00
b9d42012ca trythis
Some checks failed
release / release (push) Failing after 5m55s
test / cargo test (push) Failing after 1m5s
2024-09-18 16:24:00 +02:00
21e04ccf73 just try this
Some checks failed
test / cargo test (push) Failing after 1m3s
release / release (push) Failing after 25s
2024-09-18 16:20:33 +02:00
1d23732812 try this
Some checks failed
release / release (push) Failing after 4m45s
test / cargo test (push) Failing after 1m9s
2024-09-18 16:04:07 +02:00
ea888a84ba fix code
Some checks failed
release / release (push) Has been cancelled
test / cargo test (push) Has been cancelled
2024-09-18 15:59:20 +02:00
b547bd9974 once again
Some checks failed
release / release (push) Failing after 3m36s
test / cargo test (push) Has been cancelled
2024-09-18 15:54:41 +02:00
eec2ceecf4 lets go
Some checks failed
release / release (push) Failing after 4m43s
test / cargo test (push) Failing after 1m4s
2024-09-18 15:41:34 +02:00
ca4e9674d4 lets see what te just prod says
Some checks failed
release / release (push) Has been cancelled
test / cargo test (push) Has been cancelled
2024-09-18 15:37:02 +02:00
9667ee54c4 wait on 3
Some checks failed
test / cargo test (push) Failing after 2m19s
release / release (push) Failing after 4m43s
2024-09-18 15:20:24 +02:00
a904450b71 more nice things
Some checks failed
release / release (push) Successful in 4m13s
test / cargo test (push) Failing after 2m20s
2024-09-18 15:10:59 +02:00
88fc3f47bf log
Some checks failed
release / release (push) Successful in 1m52s
test / cargo test (push) Failing after 2m15s
2024-09-18 13:54:29 +02:00
5a99eb40e7 what is wrong
Some checks failed
release / release (push) Failing after 3m59s
test / cargo test (push) Failing after 2m16s
2024-09-18 13:47:40 +02:00
3be70703e8 no background action
Some checks failed
test / cargo test (push) Waiting to run
release / release (push) Has been cancelled
2024-09-18 13:38:08 +02:00
92b38a1087 pls
Some checks failed
test / cargo test (push) Waiting to run
release / release (push) Has been cancelled
2024-09-18 13:28:24 +02:00
35dcde6953 different wait
Some checks failed
release / release (push) Failing after 23s
test / cargo test (push) Failing after 2m21s
2024-09-18 13:25:17 +02:00
1b24d45cd5 see logs
Some checks failed
release / release (push) Failing after 3m56s
test / cargo test (push) Failing after 2m13s
2024-09-18 13:17:18 +02:00
2e9e88b052 try thiiis
Some checks failed
release / release (push) Failing after 2m38s
test / cargo test (push) Failing after 2m19s
2024-09-18 13:10:06 +02:00
9b078c4a21 try this
Some checks failed
release / release (push) Failing after 23s
test / cargo test (push) Failing after 2m15s
2024-09-18 13:06:22 +02:00
7d454f502f more fixes
Some checks failed
test / cargo test (push) Failing after 2m48s
release / release (push) Failing after 22s
2024-09-18 12:56:49 +02:00
5134a6411e fix action
Some checks failed
release / release (push) Failing after 1m42s
test / cargo test (push) Failing after 2m19s
2024-09-18 12:52:26 +02:00
46394f362c run deploy action
Some checks failed
release / release (push) Failing after 0s
test / cargo test (push) Has been cancelled
2024-09-18 12:50:28 +02:00
fbb70fa6f2 deploy script
Some checks failed
test / cargo test (push) Failing after 2m26s
2024-09-18 12:47:25 +02:00
18bcd0a45e disable release workflow
Some checks failed
test / cargo test (push) Failing after 2m19s
2024-09-17 22:09:06 +02:00
49fff88702 amend release workflow
Some checks failed
test / cargo test (push) Waiting to run
release / release (push) Has been cancelled
2024-09-17 21:59:33 +02:00
9a1fab6eae differnt just
Some checks failed
release / release (push) Has been cancelled
test / cargo test (push) Failing after 2m18s
2024-09-17 21:48:34 +02:00
74 changed files with 1387 additions and 584 deletions

View File

@@ -1,19 +1,52 @@
name: release
on:
push:
branches:
- main
# on:
# push:
# branches:
# main
on: workflow_dispatch
env:
PORT: 3081
jobs:
release:
name: release
runs-on: ubuntu-latest
container:
image: node:18
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: extractions/setup-just@v2
- run: cd axum_server && just export
- name: cache
uses: Swatinem/rust-cache@v2
working-directory: axum_server
- uses: taiki-e/install-action@just
# - uses: JarvusInnovations/background-action@v1
# with:
# run: just prod &
# working-directory: axum_server
# wait-on: http://localhost:3081
# wait-for: 10m
# tail: true
- name: Build release
run: cargo build --release
working-directory: axum_server
- name: Run server in background
run: just prod &
working-directory: axum_server
- uses: cygnetdigital/wait_for_response@v2.0.0
with:
url: 'http://127.0.0.1:3081/'
responseCode: '200,500'
timeout: 50000
interval: 2000
- name: run ssg
run: just ssg
working-directory: axum_server
- name: Server log
run: cat axum_server/server.log
- uses: actions/upload-artifact@v4
with:
name: dist

View File

@@ -17,7 +17,9 @@ tags:
Creating my own website with blog was something I had in my mind from start of my professional career after I left school.
I had a lot of new experience with development which I wanted to elaborate on and save into a small library so I can take a look back on my thoughts how they evolve over time.
This was like 6 years ago. I had a successful first attempt at doing so. I created a WordPress with the simplest theme I've found and wrote some articles. I've published it under a domain of one of the first startups I've been part of. I still have a backup of the _Wordpress_ database somewhere so I can export those articles here when I will feel like doing so. The blog haven't lived for long as the domain once expired and I was not satisfied with it enough to deploy it somewhere else.
This was like 6 years ago. I had a successful first attempt at doing so.
I created a WordPress with the simplest theme I've found and wrote some articles. I've published it under a domain of one of the first startups I've been part of. I still have a backup of the _Wordpress_ database somewhere so I can export those articles here when I will feel like doing so. The blog haven't lived for long as the domain once expired and I was not satisfied with it enough to deploy it somewhere else.
For all those years I was trying to create it in my spare time (of which wasn't that much apparently). There were several attempts. One with _Angular_ when it was "the cool kid on the block". Another one with _cycle.js_ which was not that far from being done. I regret it now as it would be really satisfying to finish that one. I had created neat <abbr title="Server side rendering">SSR</abbr> layer which was not really difficult to accomplish with _cycle.js_ as it is reactive and it only required skipping first client render of _virtual-dom_ after page load. I'm still in love with _cycle.js_ but after _sapper_ was released I've found out of its ability to create a nice **static site** I wanted to try it out. I think that the approach of **compiling the source code** as classic client applications have been doing for many years makes a lot of sense on the internet as well. This is the one thing I'd really like to be able to accomplish with reactive frameworks like _cycle.js_.
@@ -115,7 +117,7 @@ For example this is the model of this blog post:
Neat part of the _CMS_ are those widgets. In editor they will be presented by appropriate component as well as in the editor preview.
I am very satisfied with it and I recommend it.
## What's next
## What's next {#whats-next}
I've decided not to wait for perfect product and I want to release this blog as soon as possible. I will have same approach as with other products. Make a <abbr title="Minimum Viable Product">**MVP**</abbr> and then release features as they are done.
I've put some features into _Github Projects Board_. I will very likely make a redesign with experimental layout changes. I'd like to experiment with colors and make the blog look distinguishable and personal while maintaining accessibility.

View File

@@ -38,7 +38,9 @@ I've set a new **sub goal** for buying a streaming PC. I'll buy it when the stre
Last week I've been showing off new headphones that I've ordered and the *YouTube* algorithm has been suggesting great videos on the topic since. I will give another shoutout. This time it is to [Joshua's Valour *YouTube* channel](https://www.youtube.com/channel/UCx9bOYEjkevIDYONBAstK-A)
<iframe width="560" height="315" src="https://www.youtube.com/embed/hoLMdrD5pic" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<div class="video-embed">
<iframe class="embed" width="100%" height="100%" src="https://www.youtube.com/embed/hoLMdrD5pic" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
My next pick will be a [podcast *Lifespan* hosted by Dr. David Sinclair](https://open.spotify.com/show/3PkkSdQE8DfeiKvSk1Mg1J) where he talks about what we can do and how should we treat our bodies to live a longer and healthier life.

View File

@@ -28,7 +28,7 @@ This week I've attended a [Rusty game jam #2](https://itch.io/jam/rusty-jam-2).
![Egg fetcher game preview](/images/uploads/screenshot-from-2022-06-26-22-37-16.png "Egg fetcher game preview")
[You can check the rusult built with WASM here.](/showcase/egg-fetcher/)
[You can check the result built with WASM here.](/showcase/egg-fetcher/)
## What's up with the weeklys

View File

@@ -1,11 +1,12 @@
---
title: CK Vive
displayed: true
description: Websitefor *CK Vive* travel agency with a **custom CMS system** for
managing travel destinations.
cover_image: /images/uploads/ck_vive_logo.svg
link: https://ckvive.sk/
classification: website
tags:
- PHP
featured: false
---
Websitefor *CK Vive* travel agency with a **custom CMS system** for
managing travel destinations.

View File

@@ -1,8 +1,6 @@
---
title: Košice Peace Marathon
displayed: true
description: "*Košice Peace Marathon* is the oldest marathon in Europe and the
third-oldest in the world."
link: https://www.kosicemarathon.com/
cover_image: /images/uploads/screenshot-from-2024-08-06-18-22-52.png
classification: website
@@ -11,3 +9,5 @@ tags:
- MySQL
featured: false
---
*Košice Peace Marathon* is the oldest marathon in Europe and the
third-oldest in the world.

View File

@@ -1,8 +1,6 @@
---
title: Docker
displayed: false
description: An introduction to Docker containerization technology and how it
differs from virtualization.
cover_image: /images/uploads/docker-use-cases.png
classification: presentation
tags:
@@ -10,3 +8,5 @@ tags:
- Docker
featured: false
---
An introduction to Docker containerization technology and how it
differs from virtualization.

View File

@@ -0,0 +1,13 @@
---
title: Unstoppable growth of front-end frameworks
displayed: true
link: https://michalvankodev.github.io/unstoppable-growth-of-frontend-frameworks/
classification: presentation
tags:
- Presentation
- NodeJS
featured: false
---
A simple summary of the web front-end evolution. Describes how and
why new tools in the NodeJS ecosystem improve & why there is still something
to explore.

View File

@@ -0,0 +1,10 @@
---
title: Skosy
displayed: false
classification: webapp
tags:
- Webapp
featured: false
---
*Skosy* is a web application whose purpose is to **automate the
writing of integration tests** for websites.

View File

@@ -0,0 +1,13 @@
---
title: beinSports
displayed: true
link: https://www.beinsports.com/en-us
cover_image: /images/uploads/bein_logo.af017869.webp
classification: website
tags:
- Freemarker
featured: false
---
*beIN Sports* is a global network of sports channels jointly owned
and operated by *Qatari Sports Investments*, an affiliate of *Al Jazeera Media
Networks*

View File

@@ -0,0 +1,10 @@
---
title: Livesport.tv
displayed: false
classification: website
tags:
- Freemarker
featured: false
---
*Livesport.tv* is a network of premium online sports channels,
featuring all the top sports competitions from around the world.

View File

@@ -0,0 +1,14 @@
---
title: Spreading the Web
displayed: true
link: https://michalvankodev.github.io/spreading-the-web
cover_image: /images/uploads/screenshot-from-2024-08-06-18-48-02.png
classification: presentation
tags:
- Presentation
- NodeJS
featured: false
---
A presentation about the rising number of use cases for utilizing
web technologies outside of the web platform such as native mobile
applications and robotics. 2015

View File

@@ -0,0 +1,15 @@
---
title: FX Sales
displayed: true
link: https://www.caplin.com/business/fx-sales
cover_image: /images/uploads/fx_sales_screen2x.png
classification: webapp
tags:
- Webapp
- React
- Knockout
featured: false
---
*Caplin FX Sales* allows sales people to **trade on behalf of
their clients**. This needs to be an efficient workflow providing all the
relevant information to the sales user

View File

@@ -1,11 +1,11 @@
---
title: SHIP (Structured heard input process)
displayed: true
description: "*SHIP* is a web application for **editors** who actively **track
trades offers and bids** on the commodity market."
classification: webapp
tags:
- Webapp
- Angular
featured: false
---
*SHIP* is a web application for **editors** who actively **track
trades offers and bids** on the commodity market.

View File

@@ -0,0 +1,15 @@
---
title: responzIO
displayed: true
link: https://www.croptech.com/
cover_image: /images/uploads/responzio.png
classification: embedded
tags:
- Webapp
- Embedded
- NodeJS
featured: false
---
***responzIO*** is a smart, easy-to-use monitoring and automation
system. The ultimate tool for various applications such as hydroponics,
aquariums, and gardens.

View File

@@ -0,0 +1,14 @@
---
title: Manualogic
displayed: false
classification: webapp
tags:
- Webapp
- Angular
- RxJS
featured: false
---
*Manualogic* is a **single-page application** for product manual
creators. It contains **custom web editor** and management system of
**translatable pages, books** and **products.** Its main goal is to enable
customers to get manuals of their products in digital form.

View File

@@ -0,0 +1,13 @@
---
title: Signal Hub Manager
displayed: true
classification: webapp
tags:
- Webapp
- React
featured: false
---
*Signal Hub* is an end-to-end **Big Data analytics platform** for
large enterprises. It accelerates the process of extracting insights and
intelligence from large volumes of data, including data of different types and
in different formats.

View File

@@ -1,9 +1,6 @@
---
title: Panoramic
displayed: true
description: "*Panoramic* was a company focused on building a web application
for data scientists to be able to create and share models and graphs in
between each other."
classification: webapp
tags:
- Webapp
@@ -14,3 +11,6 @@ tags:
- Data analytics
featured: false
---
*Panoramic* was a company focused on building a web application
for data scientists to be able to create and share models and graphs in
between each other.

View File

@@ -1,11 +1,6 @@
---
title: The Expert
displayed: true
description: _The Expert_ is a digital platform that connects clients to
interior designers around the world. For experts, it allows **managing** their
**portfolio and profile** and **schedule** in which they are open for
**consultations**. Clients are able to view their profiles and book
consultations.
link: https://www.theexpert.com/
cover_image: /images/uploads/the-expert-logo.svg
classification: webapp
@@ -17,3 +12,8 @@ tags:
- GraphQL
featured: true
---
_The Expert_ is a digital platform that connects clients to
interior designers around the world. For experts, it allows **managing** their
**portfolio and profile** and **schedule** in which they are open for
**consultations**. Clients are able to view their profiles and book
consultations.

View File

@@ -1,8 +1,7 @@
---
title: WebAssembly presentation
displayed: true
description: A presentation about what WebAssembly is about and how it might
affect the future of the world.
link: https://michalvankodev.github.io/presentation-webassembly/
cover_image: /images/uploads/screenshot-from-2024-08-06-18-52-41.png
classification: presentation
tags:
@@ -10,3 +9,5 @@ tags:
- WebAssembly
featured: false
---
A presentation about what WebAssembly is about and how it might
affect the future of the world.

View File

@@ -0,0 +1,14 @@
---
title: The Grand Escape
displayed: true
link: https://michalvankodev.itch.io/the-grand-escape
cover_image: /images/uploads/logo.png
classification: videogame
tags:
- Rust
- Bevy
featured: true
---
A videogame where you need to steer your boat to avoid obstacles
and enemy bullets. The difficulty will be increased after a certain time and
new enemies will be spawned to make your escape harder.

View File

@@ -1,8 +1,6 @@
---
title: Renaissance of hypermedia systems
displayed: true
description: A presentation about hypermedia systems, HTMX, HyperView, and the
HATEOAS principles. 2024
link: https://michalvankodev.github.io/presentation-renaissance-of-hypermedia-systems/#/intro
cover_image: /images/uploads/screenshot-from-2024-08-06-19-01-03.png
classification: presentation
@@ -12,3 +10,5 @@ tags:
- HTMX
featured: true
---
A presentation about hypermedia systems, HTMX, HyperView, and the
HATEOAS principles. 2024

View File

@@ -1,12 +0,0 @@
---
title: beinSports
displayed: true
description: "*beIN Sports* is a global network of sports channels jointly owned
and operated by *Qatari Sports Investments*, an affiliate of *Al Jazeera Media
Networks*"
cover_image: /images/uploads/bein_logo.af017869.webp
classification: website
tags:
- Freemarker
featured: false
---

View File

@@ -1,9 +1,9 @@
---
title: dev project test
displayed: true
description: Testing project
displayed: false
classification: webapp
tags:
- Webapp
featured: false
---
Testing project

View File

@@ -1,14 +0,0 @@
---
title: FX Sales
displayed: true
description: "*Caplin FX Sales* allows sales people to **trade on behalf of
their clients**. This needs to be an efficient workflow providing all the
relevant information to the sales user"
cover_image: /images/uploads/fx_sales_screen2x.png
classification: webapp
tags:
- Webapp
- React
- Knockout
featured: false
---

View File

@@ -1,8 +1,7 @@
---
title: HeyLady!
displayed: true
description: A thriving online community supporting women to learn and practise
speaking English.
link: https://www.heylady.io/
cover_image: /images/uploads/heyladylogo.svg
classification: webapp
tags:
@@ -14,3 +13,5 @@ tags:
- PostgreSQL
featured: false
---
A thriving online community supporting women to learn and practise
speaking English.

View File

@@ -1,10 +0,0 @@
---
title: Livesport.tv
displayed: false
description: "*Livesport.tv* is a network of premium online sports channels,
featuring all the top sports competitions from around the world."
classification: website
tags:
- Freemarker
featured: false
---

View File

@@ -1,14 +0,0 @@
---
title: Manualogic
displayed: false
description: "*Manualogic* is a **single-page application** for product manual
creators. It contains **custom web editor** and management system of
**translatable pages, books** and **products.** Its main goal is to enable
customers to get manuals of their products in digital form."
classification: webapp
tags:
- Webapp
- Angular
- RxJS
featured: false
---

View File

@@ -1,14 +0,0 @@
---
title: responzIO
displayed: true
description: "***responzIO*** is a smart, easy-to-use monitoring and automation
system. The ultimate tool for various applications such as hydroponics,
aquariums, and gardens."
cover_image: /images/uploads/responzio.png
classification: embedded
tags:
- Webapp
- Embedded
- NodeJS
featured: false
---

View File

@@ -1,13 +0,0 @@
---
title: Signal Hub Manager
displayed: true
description: "*Signal Hub* is an end-to-end **Big Data analytics platform** for
large enterprises. It accelerates the process of extracting insights and
intelligence from large volumes of data, including data of different types and
in different formats."
classification: webapp
tags:
- Webapp
- React
featured: false
---

View File

@@ -1,10 +0,0 @@
---
title: Skosy
displayed: false
description: "*Skosy* is a web application whose purpose is to **automate the
writing of integration tests** for websites."
classification: webapp
tags:
- Webapp
featured: false
---

View File

@@ -1,13 +0,0 @@
---
title: Spreading the Web
displayed: true
description: A presentation about the rising number of use cases for utilizing
web technologies outside of the web platform such as native mobile
applications and robotics. 2015
cover_image: /images/uploads/screenshot-from-2024-08-06-18-48-02.png
classification: presentation
tags:
- Presentation
- NodeJS
featured: false
---

View File

@@ -1,14 +0,0 @@
---
title: The Grand Escape
displayed: true
description: A videogame where you need to steer your boat to avoid obstacles
and enemy bullets. The difficulty will be increased after a certain time and
new enemies will be spawned to make your escape harder.
link: https://michalvankodev.itch.io/the-grand-escape
cover_image: /images/uploads/logo.png
classification: videogame
tags:
- Rust
- Bevy
featured: true
---

View File

@@ -1,12 +0,0 @@
---
title: Unstoppable growth of front-end frameworks
displayed: true
description: A simple summary of the web front-end evolution. Describes how and
why new tools in the NodeJS ecosystem improve & why there is still something
to explore.
classification: presentation
tags:
- Presentation
- NodeJS
featured: false
---

View File

@@ -24,6 +24,7 @@ image = "0.25.2"
anyhow = "1.0.86"
rayon = "1.10.0"
syntect = "5.2.0"
indoc = "2.0.5"
[build]
rustflags = ["-Z", "threads=8"]
@@ -34,7 +35,7 @@ rustflags = ["-Z", "threads=8"]
# ]
[profile.dev]
debug = false
debug = true
opt-level = 0
# codegen-units = 16
# lto = "thin"

View File

@@ -46,7 +46,7 @@ wait_for_port:
# Kill the application running on port
kill:
kill $(lsof -t -i:{{port}})
kill $(pidof axum_server)
# Clean the dist folder
clean:
@@ -67,3 +67,6 @@ export: clean
just wait_for_port
just ssg
just kill
deploy:
rsync -avz -e ssh ./dist/ michalvanko@katelyn:.config/containers/systemd/michalvankodev-site/dist/

View File

@@ -1,6 +1,7 @@
use axum::{self};
use tower_http::services::ServeDir;
use tower_livereload::LiveReloadLayer;
use tracing::info;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
mod blog_posts;
@@ -34,6 +35,7 @@ async fn main() {
.nest_service("/fonts", ServeDir::new("../static/fonts"))
.nest_service("/generated_images", ServeDir::new("generated_images"))
.nest_service("/svg", ServeDir::new("../static/svg"))
// TODO manifest logos have bad link, #directory-swap
.nest_service(
"/config.yml",
ServeDir::new("../static/resources/config.yml"),
@@ -45,18 +47,18 @@ async fn main() {
// run our app with hyper, listening globally on port 3080
let port = std::option_env!("PORT").unwrap_or("3080");
let addr = format!("0.0.0.0:{}", port);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
info!("axum_server listening on http://{}", &addr);
axum::serve(listener, app).await.unwrap();
}
// TODO Socials
// TODO ul li article styles
// TODO header height difference
// - fotos
// TODO Colors
// TODO print css and other 404 css linked in base.html
// TODO go live pipeline
// background gradient color
// TODO Change DNS system
// THINK deploy to alula? rather then katelyn? can be change whenever
// TODO after release
// - contact
// OG tags
// Remove old web completely
// Restructure repository
// - projects page
// - linktree page

View File

@@ -64,7 +64,7 @@ pub async fn render_blog_post_list(
};
Ok(PostListTemplate {
title: "Posts".to_owned(),
title: "Blog posts".to_owned(),
posts,
tag,
header_props,

View File

@@ -3,3 +3,4 @@ pub mod blog_post_list;
pub mod blog_post_page;
pub mod contact;
pub mod index;
pub mod project_list;

View File

@@ -0,0 +1,30 @@
use askama::Template;
use axum::http::StatusCode;
use crate::{
components::site_header::HeaderProps,
post_utils::{post_listing::get_post_list, post_parser::ParseResult},
projects::project_model::ProjectMetadata,
};
#[derive(Template)]
#[template(path = "project_list.html")]
pub struct ProjectListTemplate {
pub title: String,
pub project_list: Vec<ParseResult<ProjectMetadata>>,
pub header_props: HeaderProps,
}
pub async fn render_projects_list() -> Result<ProjectListTemplate, StatusCode> {
let mut project_list = get_post_list::<ProjectMetadata>("../_projects").await?;
project_list.sort_by_key(|post| post.slug.to_string());
project_list.retain(|project| project.metadata.displayed);
project_list.reverse();
Ok(ProjectListTemplate {
title: "Showcase".to_owned(),
header_props: HeaderProps::default(),
project_list,
})
}

View File

@@ -5,6 +5,7 @@ use std::{
use anyhow::Context;
use image::{image_dimensions, ImageReader};
use indoc::formatdoc;
use super::{
export_format::ExportFormat, image_generator::generate_images,
@@ -18,16 +19,34 @@ pub fn generate_picture_markup(
width: u32,
height: u32,
alt_text: &str,
class_name: Option<&str>,
generate_image: bool,
) -> Result<String, anyhow::Error> {
let exported_formats = get_export_formats(orig_img_path);
let class_attr = if let Some(class) = class_name {
format!(r#"class="{class}""#)
} else {
"".to_string()
};
if exported_formats.is_empty() {
return Ok(formatdoc!(
r#"<img
src="{orig_img_path}"
width="{width}"
height="{height}"
{class_attr}
alt="{alt_text}"
>"#
));
}
let path_to_generated = get_generated_file_name(orig_img_path);
// TODO This should get removed when we move the project structure #move
let dev_only_img_path =
// TODO This should get removed when we move the project structure #directory-swap
let disk_img_path =
Path::new("../static/").join(orig_img_path.strip_prefix("/").unwrap_or(orig_img_path));
let orig_img_dimensions = image_dimensions(&dev_only_img_path)?;
let orig_img_dimensions = image_dimensions(&disk_img_path)?;
let resolutions = get_resolutions(orig_img_dimensions, width, height);
let path_to_generated_arc = Arc::new(path_to_generated);
@@ -39,8 +58,8 @@ pub fn generate_picture_markup(
if generate_image {
rayon::spawn(move || {
let orig_img = ImageReader::open(&dev_only_img_path)
.with_context(|| format!("Failed to read instrs from {:?}", &dev_only_img_path))
let orig_img = ImageReader::open(&disk_img_path)
.with_context(|| format!("Failed to read instrs from {:?}", &disk_img_path))
.unwrap()
.decode()
.unwrap();
@@ -66,7 +85,7 @@ pub fn generate_picture_markup(
.map(|format| {
let srcset = generate_srcset(&path_to_generated, format, &resolutions);
let format_type = format.get_type();
format!(
formatdoc!(
r#"<source
srcset="{srcset}"
type="{format_type}"
@@ -81,16 +100,17 @@ pub fn generate_picture_markup(
resolutions.first().expect("Should this error ever happen?"),
exported_formats.last().expect("Can this one ever happen?"),
);
let image_tag = format!(
let image_tag = formatdoc!(
r#"<img
src="{image_path}"
width="{width}"
height="{height}"
alt="{alt_text}"
{class_attr}
>"#
);
let result = format!(
let result = formatdoc!(
r#"<picture>
{source_tags}
{image_tag}
@@ -272,10 +292,12 @@ fn test_generate_srcset() {
#[test]
fn test_generate_picture_markup() {
use indoc::indoc;
let width = 300;
let height = 200;
let orig_img_path = "/images/uploads/2020-03-23_20-24-06_393.jpg";
let result = r#"<picture>
let result = indoc! {
r#"<picture>
<source
srcset="/generated_images/images/uploads/2020-03-23_20-24-06_393_300x200.avif 1x, /generated_images/images/uploads/2020-03-23_20-24-06_393_450x300.avif 1.5x, /generated_images/images/uploads/2020-03-23_20-24-06_393_600x400.avif 2x, /generated_images/images/uploads/2020-03-23_20-24-06_393_900x600.avif 3x, /generated_images/images/uploads/2020-03-23_20-24-06_393_1200x800.avif 4x"
type="image/avif"
@@ -290,10 +312,18 @@ fn test_generate_picture_markup() {
height="200"
alt="Testing image alt"
>
</picture>"#;
</picture>"#,
};
assert_eq!(
generate_picture_markup(orig_img_path, width, height, "Testing image alt", false)
.expect("picture markup has to be generated"),
generate_picture_markup(
orig_img_path,
width,
height,
"Testing image alt",
None,
false
)
.expect("picture markup has to be generated"),
result
);
}

View File

@@ -1,14 +1,16 @@
use core::panic;
use std::path::Path;
use axum::http::StatusCode;
use chrono::{DateTime, Utc};
use gray_matter::{engine::YAML, Matter};
use image::image_dimensions;
use indoc::formatdoc;
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag, TagEnd};
use serde::{de::DeserializeOwned, Deserialize, Deserializer};
use syntect::{highlighting::ThemeSet, html::highlighted_html_for_string, parsing::SyntaxSet};
use tokio::fs;
use tracing::debug;
use tracing::{debug, error, info};
use crate::picture_generator::{
picture_markup_generator::generate_picture_markup, resolutions::get_max_resolution,
@@ -71,6 +73,7 @@ pub async fn parse_post<'de, Metadata: DeserializeOwned>(
enum TextKind {
Text,
Heading(Option<String>),
Code(String),
}
@@ -87,6 +90,7 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
let syntax_set = SyntaxSet::load_defaults_newlines();
let theme_set = ThemeSet::load_defaults();
let theme = theme_set.themes.get("InspiredGitHub").unwrap();
let mut heading_ended: Option<bool> = None;
let parser = Parser::new_ext(markdown, options).map(|event| match event {
/*
@@ -102,7 +106,7 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
}) => {
if !dest_url.starts_with("/") {
return Event::Html(
format!(
formatdoc!(
r#"<img
alt="{title}"
src="{dest_url}"
@@ -123,23 +127,29 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
);
// Place image into the content with scaled reso to a boundary
let picture_markup =
generate_picture_markup(&dest_url, max_width, max_height, &title, generate_images)
.unwrap_or(format!(
r#"
let picture_markup = generate_picture_markup(
&dest_url,
max_width,
max_height,
&title,
None,
generate_images,
)
.unwrap_or(formatdoc!(
r#"
<img
alt="{alt}"
src="{src}"
/>"#,
alt = title,
src = dest_url,
));
alt = title,
src = dest_url,
));
debug!(
"Image link_type: {:?} url: {} title: {} id: {}",
link_type, dest_url, title, id
);
Event::Html(
format!(
formatdoc!(
r#"<figure>
{picture_markup}
<figcaption>
@@ -168,14 +178,55 @@ pub fn parse_html(markdown: &str, generate_images: bool) -> String {
.unwrap();
Event::Html(highlighted.into())
}
TextKind::Heading(provided_id) => {
let heading_id = provided_id.clone().unwrap_or({
text.to_lowercase()
.replace(|c: char| !c.is_alphanumeric(), "-")
});
debug!("heading_id: {}", heading_id.clone());
match heading_ended {
None => {
error!("Heading should have set state");
panic!("Heading should have set state");
}
Some(true) => Event::Html(text),
Some(false) => {
heading_ended = Some(true);
Event::Html(
formatdoc!(
r##"id="{heading_id}">
{text}"##
)
.into(),
)
}
}
}
_ => Event::Text(text),
},
Event::Start(Tag::Heading {
level,
id,
classes: _,
attrs: _,
}) => {
let id_str = id.map(|id| id.to_string());
debug!("heading_start: {:?}, level: {}", &id_str, level);
text_kind = TextKind::Heading(id_str);
heading_ended = Some(false);
Event::Html(format!("<{level} ").into())
}
Event::Start(_) => event,
Event::End(TagEnd::Image) => Event::Html("</figcaption></figure>".into()),
Event::End(TagEnd::CodeBlock) => {
text_kind = TextKind::Text;
Event::End(TagEnd::CodeBlock)
}
Event::End(TagEnd::Heading(heading_level)) => {
text_kind = TextKind::Text;
heading_ended = None;
Event::End(TagEnd::Heading(heading_level))
}
_ => event,
});

View File

@@ -1,9 +1,6 @@
use axum::http::StatusCode;
use crate::post_utils::{
post_listing::get_post_list,
post_parser::{parse_html, ParseResult},
};
use crate::post_utils::{post_listing::get_post_list, post_parser::ParseResult};
use super::project_model::ProjectMetadata;
@@ -13,10 +10,6 @@ pub async fn get_featured_projects() -> Result<Vec<ParseResult<ProjectMetadata>>
let featured_projects = project_list
.into_iter()
.filter(|post| post.metadata.featured)
.map(|mut post| {
post.metadata.description = parse_html(&post.metadata.description, false);
post
})
.collect();
Ok(featured_projects)

View File

@@ -3,7 +3,6 @@ use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct ProjectMetadata {
pub title: String,
pub description: String,
pub classification: String,
pub displayed: bool,
pub cover_image: Option<String>,
@@ -18,6 +17,7 @@ pub fn translate_classification(classification: &str) -> &str {
"website" => "Web site",
"presentation" => "Presentation",
"videogame" => "Video game",
"embedded" => "Embedded system",
any => any,
}
}

View File

@@ -3,6 +3,7 @@ use crate::{
pages::{
admin::render_admin, blog_post_list::render_blog_post_list,
blog_post_page::render_blog_post, contact::render_contact, index::render_index,
project_list::render_projects_list,
},
};
use axum::{extract::MatchedPath, http::Request, routing::get, Router};
@@ -16,6 +17,7 @@ pub fn get_router() -> Router {
.route("/blog/tags/:tag", get(render_blog_post_list))
.route("/blog/:post_id", get(render_blog_post))
.route("/contact", get(render_contact))
.route("/showcase", get(render_projects_list))
.route("/admin", get(render_admin))
.route("/feed.xml", get(render_rss_feed))
.layer(

View File

@@ -69,11 +69,11 @@
}
a {
@apply text-pink-600 underline underline-offset-2;
@apply text-pink-800 underline underline-offset-2 hover:transition hover:text-blue-500;
}
&:hover {
@apply transition text-blue-400;
}
strong {
@apply font-medium;
}
.article-body {
@@ -83,15 +83,23 @@ a {
h2 {
@apply px-4 text-xl font-semibold text-blue-900 mb-3 mt-4 max-w-read mx-auto md:text-2xl md:mb-6 md:mt-8 lg:mb-8 lg:mt-12 lg:text-4xl;
}
h3 {
@apply px-4 text-lg font-semibold text-blue-900 mb-3 mt-4 max-w-read mx-auto md:text-xl md:mb-6 md:mt-8 lg:mb-8 lg:mt-12 lg:text-3xl;
}
h4 {
@apply px-4 text-lg font-medium text-blue-900 mb-2 mt-3 max-w-read mx-auto md:text-lg md:mb-6 md:mt-8 lg:mb-8 lg:mt-12 lg:text-3xl;
}
p {
@apply px-4 my-2 text-slate-950 text-justify mx-auto max-w-read md:text-lg md:my-8 lg:text-readxl;
}
strong {
@apply font-medium;
}
pre {
@apply p-4 my-1 overflow-auto text-sm mx-auto max-w-read;
}
figure {
@apply p-4;
@@ -104,7 +112,7 @@ a {
}
table {
@apply text-sm mx-auto my-4 max-w-image table-auto border-collapse border-spacing-12 border border-gray-200 rounded md:text-base lg:text-xl lg:my-8;
@apply text-sm mx-auto my-4 max-w-image table-auto border-collapse border-spacing-12 border border-slate-200 rounded md:text-base lg:text-xl lg:my-8;
}
thead {
@@ -125,7 +133,7 @@ a {
}
blockquote {
@apply mx-6 py-1 px-2 bg-pink-50 lg:mx-auto max-w-note border-l-4 border-pink-500;
@apply mx-6 py-1 px-2 bg-pink-50 lg:mx-auto max-w-note border-l-4 border-pink-600;
p {
@apply my-2 md:my-4 text-slate-600 max-w-note;
@@ -133,14 +141,150 @@ a {
}
:not(pre) code {
@apply text-pink-950 rounded border border-blue-300 px-1 py-0.5 bg-blue-100 text-sm md:text-base lg:text-xl;
@apply text-pink-900 rounded border border-blue-300 px-1 py-0.5 bg-blue-100 text-sm md:text-base lg:text-xl;
}
pre code pre {
@apply mx-2 rounded lg:mx-auto lg:text-lg shadow-sm lg:max-w-note;
}
ul,
ol {
@apply pl-10 pr-6 my-2 text-slate-950 mx-auto max-w-read md:text-lg md:my-8 lg:text-readxl lg:pl-14;
& p {
@apply px-2;
}
}
ul {
@apply list-disc;
}
ol {
@apply list-decimal;
}
iframe {
@apply rounded shadow-md mx-auto lg:max-w-image;
}
}
article a {
@apply visited:text-purple-700;
}
.video-embed {
@apply m-4 mx-auto max-w-image aspect-video;
}
.social-card-twitch:hover {
transform: translate3d(0.6rem, -0.6rem, 0px);
box-shadow: -3px 3px 0px 3px #6441a5;
transition-delay: 75ms;
}
.social-card-youtube:hover {
@apply rounded-none;
transform: scale(1.02);
transition-delay: 100ms;
}
.social-card-instagram:hover {
filter: brightness(84%);
transition-delay: 100ms;
}
.social-card-tiktok {
position: relative;
&:hover {
animation: tiktok-glitch 1.5s infinite;
animation-delay: 200ms;
}
}
@keyframes tiktok-glitch {
0% {
box-shadow:
0px 0px 0 rgba(0, 255, 255, 0),
0px 0px 0 rgba(255, 0, 255, 0);
transform: translate(0, 0);
}
10% {
box-shadow:
-3px -3px 0 rgba(0, 255, 255, 0.8),
3px 3px 0 rgba(255, 0, 255, 0.8);
transform: translate(-1px, -1px);
}
15% {
box-shadow:
2px -2px 0 rgba(0, 255, 255, 0.6),
-2px 2px 0 rgba(255, 0, 255, 0.6);
transform: translate(2px, -2px);
}
20% {
box-shadow:
-1px 1px 0 rgba(0, 255, 255, 0.4),
1px -1px 0 rgba(255, 0, 255, 0.4);
transform: translate(1px, 1px);
}
25% {
box-shadow:
-4px 4px 0 rgba(0, 255, 255, 1),
4px -4px 0 rgba(255, 0, 255, 1);
transform: translate(-2px, 2px);
}
30% {
box-shadow:
3px -3px 0 rgba(0, 255, 255, 0.5),
-3px 3px 0 rgba(255, 0, 255, 0.5);
transform: translate(3px, -3px);
}
40% {
box-shadow:
-2px 2px 0 rgba(0, 255, 255, 0.9),
2px -2px 0 rgba(255, 0, 255, 0.9);
transform: translate(-1px, 1px);
}
50% {
box-shadow:
-1px -2px 0 rgba(0, 255, 255, 0.7),
2px -1px 0 rgba(255, 0, 255, 0.7);
transform: translate(1px, -1px);
}
60% {
box-shadow:
2px -2px 0 rgba(0, 255, 255, 0.3),
-2px 2px 0 rgba(255, 0, 255, 0.3);
transform: translate(2px, -2px);
}
75% {
box-shadow:
-3px 3px 0 rgba(0, 255, 255, 1),
3px -3px 0 rgba(255, 0, 255, 1);
transform: translate(-3px, 3px);
}
85% {
box-shadow:
-2px -2px 0 rgba(0, 255, 255, 0.2),
2px 2px 0 rgba(255, 0, 255, 0.2);
transform: translate(-2px, -2px);
}
100% {
box-shadow:
0px 0px 0 rgba(0, 255, 255, 0),
0px 0px 0 rgba(255, 0, 255, 0);
transform: translate(0, 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -38,6 +38,73 @@ module.exports = {
},
],
},
colors: {
// blue: {
// 50: "#ecf6fe",
// 100: "#d9edfc",
// 200: "#b3dbf9",
// 300: "#8ecaf6",
// 400: "#68b8f3",
// 500: "#42a6f0",
// 600: "#3585c0",
// 700: "#286490",
// 800: "#1F4E71",
// 900: "#173A54",
// 950: "#0F2637",
// },
blue: {
50: "#f1f7fe",
100: "#e1effd",
200: "#bddefa",
300: "#82c3f7",
400: "#42a6f0",
500: "#1789e0",
600: "#0a6cbf",
700: "#0a569a",
800: "#0c4980",
900: "#103e6a",
950: "#0b2746",
},
// pink: {
// 50: "#FFFBFE",
// 100: "#FFE4F9",
// 200: "#FECEF4",
// 300: "#FEB8EF",
// 400: "#fea6eb",
// 500: "#D38AC3",
// 600: "#B476A7",
// 700: "#96628B",
// 800: "#774E6E",
// 900: "#593A52",
// 950: "#3A2636",
// },
pink: {
50: "#fff4fd",
100: "#ffe7fb",
200: "#ffcff7",
300: "#fea6eb",
400: "#fc76dd",
500: "#f342ca",
600: "#d722a9",
700: "#b31889",
800: "#92166e",
900: "#771859",
950: "#500238",
},
purple: {
50: "#F8F5FC",
100: "#D5C2ED",
200: "#B28EDE",
300: "#8F5BCF",
400: "#6D30B9",
500: "#5F2AA2",
600: "#52248A",
700: "#441E73",
800: "#36185C",
900: "#281244",
950: "#1A0C2D",
},
},
},
},
plugins: [],

View File

@@ -1,7 +1,7 @@
<!doctype html>
<html lang="en">
<head>
<title>{% block title %} {{title}} @michalvankodev {% endblock %}</title>
<title>{% block title %} {{title}} {% endblock %} @michalvankodev</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<meta name="theme-color" content="#333333" />
@@ -13,19 +13,16 @@
rel="alternate"
type="application/rss+xml"
title="RSS feed for latest posts"
href="https://michalvanko.dev/feed.xml"
href="/feed.xml"
/>
<!-- Tailwind output file -->
<link rel="stylesheet" href="/styles/output.css" />
<link rel="stylesheet" href="/print.css" media="print" />
<link rel="stylesheet" href="/fonts.css" />
<link rel="manifest" href="/manifest.json" />
<link rel="stylesheet" href="/prism.css" />
<link rel="icon" type="image/svg+xml" href="/m-logo.svg" />
<link rel="icon" type="image/png" href="/m-logo-192.png" />
<link rel="icon" type="image/svg+xml" href="/images/m-logo.svg" />
<link rel="icon" type="image/png" href="/images/m-logo-192.png" />
</head>
<body class="bg-blue-50">
{% include "site_header.html" %}

View File

@@ -5,10 +5,10 @@
{% block content %}
<article class="mb-6">
<header class="px-4 max-w-read mx-auto">
<h1 class="text-3xl md:text-4xl lg:text-6xl lg:mt-20 text-blue-900 mb-3 font-semibold">{{title}}</h1>
<h1 class="text-3xl md:text-4xl lg:text-6xl lg:mt-20 text-blue-900 mb-3 font-bold">{{title}}</h1>
<aside class="flex justify-between flex-row">
{% include "post_tag_list.html" %}
<section class="created-at m-1 text-right text-sm text-gray-600 md:text-lg">
<section class="created-at m-1 text-right text-sm text-slate-600 md:text-lg">
<span>Published on</span>
<time datetime="{date}"> {{date|pretty_date}} </time>
</section>
@@ -21,6 +21,7 @@
</article>
<!-- TODO: Next recommendations for reading -->
<!-- TODO: Bact to all posts -->
{# footer #}
{% endblock %}

View File

@@ -9,7 +9,7 @@
{% else %}
<h1 class="m-5 text-4xl text-blue-950 font-extrabold md:text-6xl">
{% if let Some(t) = tag %}
<em>{{t}}</em>
#{{t}}
{% else %}
Blog posts
{% endif %}
@@ -25,13 +25,13 @@
</ul>
</section>
<hr class="border-blue-950 m-5 md:my-8">
<hr class="border-slate-300 m-5 md:my-8">
<ul class="mx-5">
{% for post in posts %}
<li>
{% include "components/blog_post_preview.html" %}
<hr class="border-blue-950 my-5 md:my-8">
<hr class="border-slate-300 my-5 md:my-8">
</li>
{% endfor %}
</ul>
@@ -42,15 +42,18 @@
</section> <!-- /#socials -->
<section id="showcase" class="hidden lg:block">
<h2 class="text-blue-950 font-semibold text-2xl m-5 md:text-4xl">Showcase</h2>
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl"><a href="/showcase" class="text-blue-950 no-underline">Showcase</a></h2>
<ul class="">
<ul class="mx-6">
{% for project in featured_projects %}
<li class="my-2">
<li class="my-4">
{% include "components/project_preview_card.html" %}
</li>
{% endfor %}
</ul>
<section class="text-center my-3 md:text-lg">
<a href="/showcase">check out more projects</a>
</section>
</section> <!-- /#showcase -->
</section> <!-- /#blog-container -->
{% endblock %}

View File

@@ -1,3 +1,3 @@
<div class="rounded-2xl w-[180px] h-[240px] bg-blue-100 border-4 border-blue-500 flex justify-center items-center">
<div class="w-[180px] h-[240px] bg-blue-100 flex justify-center items-center">
<span class="text-blue-500 text-8xl -translate-y-1.5">{{post.metadata.title|fmt("{:.1}")|lower}}</span>
</div>

View File

@@ -1,8 +1,8 @@
<article class="grid grid-cols-[max-content_1fr] grid-rows-[max-content_1fr_max-content] grid-flow-col gap-4 md:gap-x-8">
<aside class="row-span-3 self-center">
<article class="sm:grid sm:grid-cols-[max-content_1fr] sm:grid-rows-[max-content_1fr_max-content] sm:grid-flow-col sm:gap-4 md:gap-x-8 break-inside-avoid clear-both sm:clear-none">
<aside class="row-span-3 self-center float-start sm:float-none mr-3 mb-3 sm:ml-0 sm:mb-0">
{% match post.metadata.thumbnail %}
{% when Some with (orig_path) %}
{{ crate::picture_generator::picture_markup_generator::generate_picture_markup(orig_path, 180, 240, "Article thumbnail", true).unwrap_or("thumbnail not found".to_string())|safe }}
{{ crate::picture_generator::picture_markup_generator::generate_picture_markup(orig_path, 180, 240, "Article thumbnail", None, true).unwrap_or("thumbnail not found".to_string())|safe }}
{% when None %}
<div>
{% include "components/blog_post_default_thumbnail.html" %}
@@ -11,11 +11,11 @@
</aside>
<header>
<h3 class="text-lg font-bold mb-1 md:text-3xl">
<a rel="prefetch" href="/blog/{{post.slug}}" class="text-blue-950 no-underline">{{post.metadata.title}}</a>
<a rel="prefetch" href="/blog/{{post.slug}}" class="text-blue-950 visited:text-purple-700 no-underline">{{post.metadata.title}}</a>
</h3>
</header>
<section class="text-base leading-5 text-gray-800 md:text-xl text-justify">{{post.body|description_filter|safe}}</section>
<footer class="text-sm md:text-base lg:text-lg">
<section class="text-base leading-5 text-slate-800 md:text-xl text-justify">{{post.body|description_filter|safe}}</section>
<footer class="text-sm md:text-base lg:text-lg mt-3 sm:mt-0 clear-both sm:clear-none">
<ul class="inline-block">
{% for tag in post.metadata.tags %}
<li class="inline-block">

View File

@@ -1,4 +1,4 @@
<article class="border rounded-md bg-white m-4 p-4">
<section class="border rounded-md bg-white p-4 break-inside-avoid">
<header class="px-4 mb-3">
<h2 class="text-xl font-semibold text-blue-900 md:text-2xl">
{% match project.metadata.link %}
@@ -11,23 +11,24 @@
{% endmatch %}
</h2>
<section class="description text-slate-800 my-2 md:text-xl text-justify">
{{project.metadata.description|safe}}
{{project.body|safe}}
</section>
</header>
<!-- <hr class="border-blue-950 my-5"> -->
{% match project.metadata.cover_image %}
{% when Some with (source) %}
<figure class="mx-4 my-2">
{% let picture = crate::picture_generator::picture_markup_generator::generate_picture_markup(source, 420, 236, "Project cover", Some("max-h-[236px]"), true).unwrap_or("cover not found".to_string()) %}
<figure class="mx-4 my-2 flex justify-center">
{% match project.metadata.link %}
{% when Some with (href) %}
<a href="{{href}}">
<img src="{{source}}" class="object-contain w-full aspect-video"/>
{{picture|safe}}
</a>
{% when None %}
<img src="{{source}}" />
{{picture|safe}}
{% endmatch %}
<!-- TODO <figure> -->
<!-- TODO <figure> generate_image -->
</figure>
{% when None %}
{% endmatch %}
@@ -45,4 +46,4 @@
{% endfor %}
</ul>
</footer>
</article>
</section>

View File

@@ -1,15 +1,14 @@
{% macro social_card_start(svg, heading) %}
{% macro social_card_start(svg, url, heading, img, class) %}
<section class="border rounded-md bg-pink-200 m-4 p-4">
<a href="{{url}}" class="block no-underline border rounded-md bg-pink-200 m-4 p-4 max-w-[392px] {{class}}">
<header class="flex text-center justify-center items-center gap-2 mb-2">
<svg aria-hidden="true" class="h-7 w-7 fill-blue-950">
<use xlink:href="/svg/icons-sprite.svg#{{svg}}" />
</svg>
<h3 class="text-lg font-medium mb-1">{{heading|safe}}</h3>
<h3 class="text-lg font-medium mb-1 text-blue-950 visited:text-blue-950">{{heading|safe}}</h3>
</header>
{% let alt_text = format!("{svg} thumbnail") %}
{% endmacro %}
{% macro social_card_end() %}
</section>
{{ crate::picture_generator::picture_markup_generator::generate_picture_markup(img, 360, 128, alt_text, Some("h-auto mx-auto rounded-sm"), true).unwrap_or("thumbnail not found".to_string())|safe }}
</a>
{% endmacro %}

View File

@@ -1,6 +1,6 @@
{% macro talent_card(svg, heading, description) %}
<section class="flex border rounded bg-white m-4 p-3">
<section class="flex border rounded bg-white m-4 p-3 max-w-[32rem]">
<aside class="flex justify-center items-center pr-3">
<svg aria-hidden="true" class="fill-blue-950 h-12 w-12 md:h-16 md:w-16">
<use xlink:href="/svg/icons-sprite.svg#{{svg}}" />
@@ -10,7 +10,7 @@
<header>
<h3 class="text-lg font-medium mb-1 md:text-2xl">{{heading}}</h3>
</header>
<p class="text-sm leading-5 text-gray-800 md:text-lg">{{description|safe}}</p>
<p class="text-sm leading-5 text-slate-800 md:text-lg">{{description|safe}}</p>
</section>
</section>

View File

@@ -1,25 +1,27 @@
{% extends "base.html" %}
{% block content %}
<h1 class="mx-6 mt-3 text-4xl text-blue-950 font-extrabold">
Contact
</h1>
<section id="contact-page">
<h1 class="mx-6 mt-3 text-4xl text-blue-950 font-extrabold lg:mx-auto max-w-read">
Contact
</h1>
<ul class="mx-6">
{% for link in links %}
<li class="my-6">
<a
class="flex border-2 place-content-center items-center rounded-full border-blue-500 py-5 hover:bg-pink-200 transition-colors"
href="{{link.href}}"
title="{{link.title}}"
>
<svg aria-hidden="true" class="h-6 w-6 fill-blue-950 mx-2">
<use xlink:href="/svg/icons-sprite.svg#{{link.svg}}" />
</svg>
<span class="text-lg font-semibold">{{link.label}}</span>
</a>
</li>
{% endfor %}
</ul>
<ul class="mx-6">
{% for link in links %}
<li class="my-2 sm:my-4 lg:my-6 max-w-[32rem] mx-auto">
<a
class="flex border-2 place-content-center items-center rounded-full text-blue-900 border-blue-500 py-2 sm:py-4 hover:bg-pink-200 fill-blue-900 hover:fill-blue-400 transition-colors no-underline"
href="{{link.href}}"
title="{{link.title}}"
>
<svg aria-hidden="true" class="h-6 w-6 mx-2 self-start">
<use xlink:href="/svg/icons-sprite.svg#{{link.svg}}" />
</svg>
<span class="text-lg font-semibold">{{link.label}}</span>
</a>
</li>
{% endfor %}
</ul>
</section>
{% endblock %}

View File

@@ -7,25 +7,24 @@
{% block content %}
<section class="index-container lg:grid lg:grid-cols-2 xl:grid-cols-[1fr_2fr] lg:gap-y-8 lg:gap-x-32 max-w-maxindex mx-auto">
<section id="about-me">
<header class="index-header hidden">
<figure class="profile-pic">
<picture>
<img
alt="Portrait"
{# TODO generate `srcset` for optimal image #}
{# TODO Take a new photo #}
src="/images/profile-portugal-landscape.jpg"
/>
</picture>
</figure>
<!-- <header class="index-header hidden"> -->
<!-- <figure class="profile-pic"> -->
<!-- <picture> -->
<!-- <img -->
<!-- alt="Portrait" -->
<!-- {# TODO Take a new photo #} -->
<!-- src="/images/profile-portugal-landscape.jpg" -->
<!-- /> -->
<!-- </picture> -->
<!-- </figure> -->
<p class="motto">
<cite>“Let your ambition carry you.”</cite>
<span class="cite-owner">- La Flame</span>
</p>
</header>
<!-- <p class="motto"> -->
<!-- <cite>“Let your ambition carry you.”</cite> -->
<!-- <span class="cite-owner">- La Flame</span> -->
<!-- </p> -->
<!-- </header> -->
<h2 class="text-blue-950 font-semibold text-2xl m-5 md:text-4xl">About me</h2>
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">About me</h2>
<p class="mx-5 md:text-xl text-justify">
Welcome to my personal website. My name is
@@ -34,15 +33,15 @@
<em> <a href="https://en.wikipedia.org/wiki/Programmer">programmer</a> </em>
. I am developing software for more than half of my life and <strong>I love it!</strong> Sometimes I stream working on my side projects and building a <a href="https://discord.gg/2cGg7kwZEh">community of like minded people</a>. Here you can find blogs of my thoughts and journeys, as well as links to my socials where you can see other content.</p>
<section id="talent-cards">
<section id="talent-cards" class="flex flex-col items-center">
{% call tc::talent_card("code", "Web development", "Extensive expertise in creating performant, live web applications and websites") %}
{% call tc::talent_card("gamepad", "Game development", "Extensive expertise in creating performant, live web applications and websites") %}
{% call tc::talent_card("person-chalkboard", "Mentoring & Consulting", "I offer consulting sessions to assist you in developing <strong>higher-quality software</strong> and share insights from crafting robust, professional web applications. <a href=\"TODO callendly\">Schedule a session with me</a> and elevate your projects together.") %}
{% call tc::talent_card("person-chalkboard", "Mentoring & Consulting", "I offer consulting sessions to assist you in developing <strong>higher-quality software</strong> and share insights from crafting robust, professional web applications. <a href=\"https://calendly.com/michalvankosk/30min\">Schedule a session with me</a> and elevate your projects together.") %}
</section>
</section>
<section id="blog" class="lg:col-span-2 lg:row-start-2 xl:col-auto xl:row-start-auto xl:row-span-2">
<h2 class="text-blue-950 font-semibold text-2xl md:text-4xl m-5"><a href="/blog" class="text-blue-950 no-underline">Blog</a></h2>
<h2 class="text-blue-950 font-bold text-2xl md:text-4xl m-5"><a href="/blog" class="text-blue-950 no-underline">Blog</a></h2>
<section id="blog-tags">
<ul class="mx-5">
{% for tag in blog_tags %}
@@ -52,23 +51,26 @@
{% endfor %}
</ul>
</section>
<hr class="border-blue-950 m-5">
<hr class="border-slate-300 m-5">
<ul class="mx-5">
{% for post in featured_blog_posts %}
<li>
{% include "components/blog_post_preview.html" %}
<hr class="border-blue-950 my-5 md:my-8">
<hr class="border-slate-300 my-5 md:my-8">
</li>
{% endfor %}
</ul>
<section class="text-center my-3 md:text-lg">
<a href="/blog">see all blog posts</a>
</section>
</section>
<section id="socials">
{% include "sections/social.html" %}
</section>
<hr class="border-blue-950 m-5 lg:hidden">
<hr class="border-slate-300 m-5 lg:hidden">
<section id="showcase" class="col-span-2">
{% include "sections/showcase.html" %}

View File

@@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block content %}
<section id="project-list-container" class="max-w-maxindex mx-auto">
<section id="project-list">
{% if project_list.len() == 0 %}
<p class="no-posts">You've found void in the space.</p>
{% else %}
<h1 class="m-5 text-4xl text-blue-950 font-extrabold md:text-6xl">
Showcase
</h1>
<ul class="m-6 grid grid-flow-row gap-6 md:grid-cols-2 md:grid-rows-[masonry] md:justify-stretch md:items-stretch xl:grid-cols-3">
{% for project in project_list %}
<li>
{% include "components/project_preview_card.html" %}
</li>
{% endfor %}
</ul>
{% endif %}
</section> <!-- /#project-list -->
</section> <!-- /#project-list-container -->
{% endblock %}

View File

@@ -1,9 +1,13 @@
<h2 class="text-blue-950 font-semibold text-2xl m-5 md:text-4xl">Showcase</h2>
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl"><a href="/showcase" class="text-blue-950 no-underline">Showcase</a></h2>
<ul class="mx-5 md:grid md:grid-cols-2 md:grid-rows-[masonry] md:justify-stretch md:items-stretch xl:grid-cols-3">
<ul class="mx-6 grid grid-flow-row gap-6 md:grid-cols-2 md:grid-rows-[masonry] md:justify-stretch md:items-stretch xl:grid-cols-3">
{% for project in featured_projects %}
<li class="my-2">
<li>
{% include "components/project_preview_card.html" %}
</li>
{% endfor %}
</ul>
<section class="text-center my-3 md:text-lg">
<a href="/showcase">check out more projects</a>
</section>

View File

@@ -1,52 +1,14 @@
<h2 class="text-blue-950 font-semibold text-2xl m-5 md:text-4xl">Socials</h2>
{% call sc::social_card_start("twitch", "I stream (almost) regularly on <em>twitch.tv</em>") %}
<!-- <script src= "https://player.twitch.tv/js/embed/v1.js"></script> -->
<!-- <div id="twitch-player" class="h-64 aspect-video rounded overflow-hidden"></div> -->
<!-- <script type="text/javascript"> -->
<!-- var options = { -->
<!-- width: "100%", -->
<!-- height: "100%", -->
<!-- channel: "michalvankodev", -->
<!-- parent: ["localhost"] -->
<!-- }; -->
<!-- var player = new Twitch.Player("twitch-player", options); -->
<!-- player.setVolume(0.5); -->
</script>
{% call sc::social_card_end() %}
<h2 class="text-blue-950 font-bold text-2xl m-5 md:text-4xl">Socials</h2>
{% call sc::social_card_start("tiktok", "Highlights can be found on <em>TikTok</em>") %}
<!-- STYLES needed to overwrite tiktok embed css -->
<!-- <blockquote -->
<!-- class="h-64 aspect-video overflow-hidden p-0 m-0 tiktok-embed bg-pink-200" -->
<!-- cite="https://www.tiktok.com/@michalvankodev" -->
<!-- data-unique-id="michalvankodev" -->
<!-- data-embed-from="embed_page" -->
<!-- data-embed-type="creator" -->
<!-- style="max-width:780px; min-width:288px; margin: 0; padding: 0; border-radius: 8px" -->
<!-- > -->
<!-- <section> -->
<!-- <a target="_blank" href="https://www.tiktok.com/@michalvankodev?refer=creator_embed">@michalvankodev</a> -->
<!-- </section> -->
<!-- </blockquote> -->
<!-- <script async src="https://www.tiktok.com/embed.js"></script> -->
{% call sc::social_card_end() %}
<section class="grid grid-flow-row justify-center">
{% call sc::social_card_start("twitch", "https://twitch.tv/michalvankodev", "I stream (almost) regularly on <em>twitch.tv</em>", "images/social/twitch_wo.png", "social-card-twitch") %}
{% call sc::social_card_start("youtube", "Vlogs and highlights can be found on <em>YouTube</em>") %}
<!-- TODO create our own youtube widget which will populate this window on build -->
<!-- <iframe -->
<!-- class="h-64 aspect-video" -->
<!-- id="ytplayer" -->
<!-- type="text/html" -->
<!-- width="100%" -->
<!-- height="100%" -->
<!-- src="https://www.youtube.com/embed/?listType=playlist&list=PLjUl8tFKyR8rCsckLn93PAwQg6tf0cyBl&enablejsapi=1&color=white" -->
<!-- frameborder="0" -->
<!-- allowfullscreen -->
<!-- ></iframe> -->
{% call sc::social_card_end() %}
{% call sc::social_card_start("tiktok", "https://www.tiktok.com/@michalvankodev","Highlights can be found on <em>TikTok</em>", "images/social/tiktok_wo.png", "social-card-tiktok") %}
{% call sc::social_card_start("youtube", "https://www.youtube.com/@michalvankodev", "Videos and vlogs posted on <em>YouTube</em>", "images/social/youtube_wo.png", "social-card-youtube") %}
{% call sc::social_card_start("instagram", "Photos and stories shared on <em>Instagram</em>") %}
<!-- <blockquote class="instagram-media aspect-video h-64" data-instgrm-permalink="https://www.instagram.com/michalvankodev/" data-instgrm-version="12" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; height:256px; max-height:100%;"></blockquote><script async src="https://www.instagram.com/embed.js"></script> -->
{% call sc::social_card_end() %}
<div class="lg:hidden xl:block">
{% call sc::social_card_start("instagram", "https://www.instagram.com/michalvankodev/", "Photos and stories shared on <em>Instagram</em>", "images/social/instagram_plain.png", "social-card-instagram") %}
</div>
</section>

View File

@@ -1,4 +1,5 @@
<footer>
<footer class="my-4">
<hr class="mb-4 border-slate-300 mx-5">
<p
class="text-center"
xmlns:cc="http://creativecommons.org/ns#"
@@ -14,7 +15,7 @@
<a
rel="cc:attributionURL dct:creator"
property="cc:attributionName"
href="https://michalvanko.dev/"
href="/contact"
>Michal Vanko</a
>
is licensed under
@@ -26,16 +27,24 @@
>CC BY-NC-ND 4.0<img
src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1"
alt="cc"
class="inline-block h-6 mx-0.5" /><img
class="inline-block h-6 mx-0.5"
height="24"
width="24" /><img
src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1"
alt="by"
class="inline-block h-6 mx-0.5" /><img
class="inline-block h-6 mx-0.5"
height="24"
width="24" /><img
src="https://mirrors.creativecommons.org/presskit/icons/nc.svg?ref=chooser-v1"
alt="nc"
class="inline-block h-6 mx-0.5" /><img
class="inline-block h-6 mx-0.5"
height="24"
width="24" /><img
src="https://mirrors.creativecommons.org/presskit/icons/nd.svg?ref=chooser-v1"
alt="nd"
class="inline-block h-6 mx-0.5"
height="24"
width="24"
/></a>
<!-- TODO Display link to feed with icon -->
<a href="/feed.xml" class="hidden">RSS feed</a>

View File

@@ -1,20 +1,20 @@
<header class="min-h-full bg-blue-50 mb-5">
<nav class="flex">
{% match header_props.back_link %}
{% when Some with (link) %}
<a
class="p-3 text-lg font-medium drop-shadow-md"
href="{{link.href}}"
>
{{link.label}}
</a>
{% when None %}
{% endmatch %}
<aside class="flex logo-section flex-grow justify-end content-end">
<a class="logo p-3 text-base" href="/">
@michalvankodev
</a>
</aside>
</nav>
<hr class="border-blue-950 mx-5">
<nav class="flex">
{% match header_props.back_link %}
{% when Some with (link) %}
<a
class="px-3 py-2 text-lg font-medium print:hidden"
href="{{link.href}}"
>
{{link.label}}
</a>
{% when None %}
{% endmatch %}
<aside class="flex logo-section flex-grow justify-end content-end">
<a class="logo p-3 text-base" href="/">
@michalvankodev
</a>
</aside>
</nav>
<hr class="border-slate-300 mx-5">
</header>

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -96,7 +96,7 @@ collections:
widget: boolean,
default: true,
}
- { label: Description, name: description, widget: markdown }
- { label: Description, name: body, widget: markdown }
- {
label: Cover image,
name: cover_image,
@@ -141,7 +141,7 @@ collections:
widget: boolean,
default: true,
}
- { label: Description, name: description, widget: markdown }
- { label: Description, name: body, widget: markdown }
- {
label: Link,
name: link,