mirror of
https://gitlab.com/Anson-Projects/anson-stuff/zinetest.git
synced 2025-06-15 13:36:39 +00:00
Add Navigation and Embrace pico.css
This commit is contained in:
parent
a6f828e8ea
commit
ef11297c25
200
Cargo.lock
generated
200
Cargo.lock
generated
@ -2,6 +2,18 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "Ansons-Zine"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"feed-rs",
|
||||
"maud",
|
||||
"rayon",
|
||||
"reqwest",
|
||||
"scraper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
@ -24,7 +36,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom 0.2.12",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
@ -156,6 +168,31 @@ version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "cssparser"
|
||||
version = "0.31.2"
|
||||
@ -165,7 +202,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa",
|
||||
"phf 0.8.0",
|
||||
"phf",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -211,6 +248,12 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.33"
|
||||
@ -366,17 +409,6 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.12"
|
||||
@ -385,7 +417,7 @@ checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -621,7 +653,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
|
||||
dependencies = [
|
||||
"log",
|
||||
"phf 0.10.1",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"string_cache",
|
||||
"string_cache_codegen",
|
||||
@ -678,7 +710,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@ -813,24 +845,15 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
"phf_macros",
|
||||
"phf_shared",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -839,18 +862,8 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
|
||||
dependencies = [
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_generator"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
|
||||
dependencies = [
|
||||
"phf_shared 0.8.0",
|
||||
"rand 0.7.3",
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -859,33 +872,24 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
|
||||
dependencies = [
|
||||
"phf_shared 0.10.0",
|
||||
"rand 0.8.5",
|
||||
"phf_shared",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.8.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
|
||||
checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
|
||||
dependencies = [
|
||||
"phf_generator 0.8.0",
|
||||
"phf_shared 0.8.0",
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro-hack",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher 0.3.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.10.0"
|
||||
@ -982,20 +986,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"libc",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc",
|
||||
"rand_pcg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@ -1003,18 +993,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.5.1",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1024,16 +1004,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1042,25 +1013,27 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.12",
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
name = "rayon"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.2.1"
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1241,7 +1214,7 @@ dependencies = [
|
||||
"fxhash",
|
||||
"log",
|
||||
"new_debug_unreachable",
|
||||
"phf 0.10.1",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
"precomputed-hash",
|
||||
"servo_arc",
|
||||
@ -1352,7 +1325,7 @@ dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"phf_shared 0.10.0",
|
||||
"phf_shared",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
]
|
||||
@ -1363,8 +1336,8 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
|
||||
dependencies = [
|
||||
"phf_generator 0.10.0",
|
||||
"phf_shared 0.10.0",
|
||||
"phf_generator",
|
||||
"phf_shared",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
@ -1577,7 +1550,7 @@ version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
|
||||
dependencies = [
|
||||
"getrandom 0.2.12",
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1601,12 +1574,6 @@ dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
@ -1859,14 +1826,3 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zine"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"feed-rs",
|
||||
"maud",
|
||||
"reqwest",
|
||||
"scraper",
|
||||
]
|
||||
|
@ -1,7 +1,11 @@
|
||||
[package]
|
||||
name = "zine"
|
||||
version = "0.1.0"
|
||||
name = "Ansons-Zine"
|
||||
description = "Aggregate feed of RSS feeds I enjoy in the form of a newspaper."
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
authors = ["Anson Biggs"]
|
||||
homepage = "https://zine.ansonbiggs.com"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -11,3 +15,4 @@ reqwest = { version = "0.11.24", features = ["blocking"] }
|
||||
maud = "0.26.0"
|
||||
chrono = "0.4.33"
|
||||
scraper = "0.18.1"
|
||||
rayon = "1.8.1"
|
||||
|
@ -1,8 +1,8 @@
|
||||
http://www.aaronsw.com/2002/feeds/pgessays.rss
|
||||
https://bcantrill.dtrace.org/feed/
|
||||
https://ciechanow.ski/atom.xml
|
||||
https://factorio.com/blog/rss
|
||||
https://feeds.feedburner.com/berkes
|
||||
https://hnrss.org/favorites?id=MisterBiggs
|
||||
https://magnuschatt.medium.com/feed
|
||||
https://notes.ansonbiggs.com/rss/
|
||||
https://orbitalindex.com/feed.xml
|
||||
|
79
output/minimal-theme-switcher.js
Normal file
79
output/minimal-theme-switcher.js
Normal file
@ -0,0 +1,79 @@
|
||||
/*!
|
||||
* Minimal theme switcher
|
||||
*
|
||||
* Pico.css - https://picocss.com
|
||||
* Copyright 2019-2024 - Licensed under MIT
|
||||
*/
|
||||
|
||||
const themeSwitcher = {
|
||||
// Config
|
||||
_scheme: "auto",
|
||||
menuTarget: "details.dropdown",
|
||||
buttonsTarget: "a[data-theme-switcher]",
|
||||
buttonAttribute: "data-theme-switcher",
|
||||
rootAttribute: "data-theme",
|
||||
localStorageKey: "picoPreferredColorScheme",
|
||||
|
||||
// Init
|
||||
init() {
|
||||
this.scheme = this.schemeFromLocalStorage;
|
||||
this.initSwitchers();
|
||||
},
|
||||
|
||||
// Get color scheme from local storage
|
||||
get schemeFromLocalStorage() {
|
||||
return window.localStorage?.getItem(this.localStorageKey) ?? this._scheme;
|
||||
},
|
||||
|
||||
// Preferred color scheme
|
||||
get preferredColorScheme() {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
},
|
||||
|
||||
// Init switchers
|
||||
initSwitchers() {
|
||||
const buttons = document.querySelectorAll(this.buttonsTarget);
|
||||
buttons.forEach((button) => {
|
||||
button.addEventListener(
|
||||
"click",
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
// Set scheme
|
||||
this.scheme = button.getAttribute(this.buttonAttribute);
|
||||
// Close dropdown
|
||||
document.querySelector(this.menuTarget)?.removeAttribute("open");
|
||||
},
|
||||
false
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
// Set scheme
|
||||
set scheme(scheme) {
|
||||
if (scheme == "auto") {
|
||||
this._scheme = this.preferredColorScheme;
|
||||
} else if (scheme == "dark" || scheme == "light") {
|
||||
this._scheme = scheme;
|
||||
}
|
||||
this.applyScheme();
|
||||
this.schemeToLocalStorage();
|
||||
},
|
||||
|
||||
// Get scheme
|
||||
get scheme() {
|
||||
return this._scheme;
|
||||
},
|
||||
|
||||
// Apply scheme
|
||||
applyScheme() {
|
||||
document.querySelector("html")?.setAttribute(this.rootAttribute, this.scheme);
|
||||
},
|
||||
|
||||
// Store scheme to local storage
|
||||
schemeToLocalStorage() {
|
||||
window.localStorage?.setItem(this.localStorageKey, this.scheme);
|
||||
},
|
||||
};
|
||||
|
||||
// Init
|
||||
themeSwitcher.init();
|
78
output/modal.js
Normal file
78
output/modal.js
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Modal
|
||||
*
|
||||
* Pico.css - https://picocss.com
|
||||
* Copyright 2019-2024 - Licensed under MIT
|
||||
*/
|
||||
|
||||
// Config
|
||||
const isOpenClass = "modal-is-open";
|
||||
const openingClass = "modal-is-opening";
|
||||
const closingClass = "modal-is-closing";
|
||||
const scrollbarWidthCssVar = "--pico-scrollbar-width";
|
||||
const animationDuration = 400; // ms
|
||||
let visibleModal = null;
|
||||
|
||||
// Toggle modal
|
||||
const toggleModal = (event) => {
|
||||
event.preventDefault();
|
||||
const modal = document.getElementById(event.currentTarget.dataset.target);
|
||||
if (!modal) return;
|
||||
modal && (isModalOpen(modal) ? closeModal(modal) : openModal(modal));
|
||||
};
|
||||
|
||||
// Is modal open
|
||||
const isModalOpen = (modal) => modal.hasAttribute("open") && modal.getAttribute("open") !== "false";
|
||||
|
||||
// Open modal
|
||||
const openModal = (modal) => {
|
||||
const { documentElement: html } = document;
|
||||
const scrollbarWidth = getScrollbarWidth();
|
||||
if (scrollbarWidth) {
|
||||
html.style.setProperty(scrollbarWidthCssVar, `${scrollbarWidth}px`);
|
||||
}
|
||||
html.classList.add(isOpenClass, openingClass);
|
||||
setTimeout(() => {
|
||||
visibleModal = modal;
|
||||
html.classList.remove(openingClass);
|
||||
}, animationDuration);
|
||||
modal.setAttribute("open", true);
|
||||
};
|
||||
|
||||
// Close modal
|
||||
const closeModal = (modal) => {
|
||||
visibleModal = null;
|
||||
const { documentElement: html } = document;
|
||||
html.classList.add(closingClass);
|
||||
setTimeout(() => {
|
||||
html.classList.remove(closingClass, isOpenClass);
|
||||
html.style.removeProperty(scrollbarWidthCssVar);
|
||||
modal.removeAttribute("open");
|
||||
}, animationDuration);
|
||||
};
|
||||
|
||||
// Close with a click outside
|
||||
document.addEventListener("click", (event) => {
|
||||
if (visibleModal === null) return;
|
||||
const modalContent = visibleModal.querySelector("article");
|
||||
const isClickInside = modalContent.contains(event.target);
|
||||
!isClickInside && closeModal(visibleModal);
|
||||
});
|
||||
|
||||
// Close with Esc key
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape" && visibleModal) {
|
||||
closeModal(visibleModal);
|
||||
}
|
||||
});
|
||||
|
||||
// Get scrollbar width
|
||||
const getScrollbarWidth = () => {
|
||||
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
||||
return scrollbarWidth;
|
||||
};
|
||||
|
||||
// Is scrollbar visible
|
||||
const isScrollbarVisible = () => {
|
||||
return document.body.scrollHeight > screen.height;
|
||||
};
|
@ -1,39 +0,0 @@
|
||||
.cards-container {
|
||||
columns: 3 280px;
|
||||
/* Creates 3 columns, each at least 280px wide */
|
||||
column-gap: 16px;
|
||||
/* Adjusts the gap between columns */
|
||||
}
|
||||
|
||||
.card {
|
||||
break-inside: avoid-column;
|
||||
/* Prevents cards from being split across columns */
|
||||
margin-bottom: 8px;
|
||||
/* Adds space between cards vertically */
|
||||
width: 100%;
|
||||
/* Ensures card fills the column width */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.card-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
display: block;
|
||||
|
||||
}
|
||||
|
||||
.card-link p {
|
||||
text-decoration: none;
|
||||
color: var(--h6-color);
|
||||
text-align: justify;
|
||||
text-justify: inter-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Times New Roman', serif;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 0;
|
||||
}
|
158
src/main.rs
158
src/main.rs
@ -14,6 +14,8 @@ use std::fs::write;
|
||||
use std::fs::DirBuilder;
|
||||
use std::path::Path;
|
||||
|
||||
use rayon::prelude::*;
|
||||
|
||||
fn fetch_feed(url: &str) -> Result<Vec<Entry>, Box<dyn Error>> {
|
||||
let content = get(url)?.text()?;
|
||||
let feed = parser::parse(content.as_bytes())?;
|
||||
@ -54,21 +56,42 @@ fn create_html_card(entry: &Entry) -> Markup {
|
||||
);
|
||||
|
||||
let cleaned_description = strip_html_tags(&description);
|
||||
let truncated_description = truncate_description(&cleaned_description, 500); // Truncate description to 100 characters
|
||||
let truncated_description = truncate_description(&cleaned_description, 500);
|
||||
|
||||
let main_url = get_root_url(link.href.as_str());
|
||||
|
||||
html! {
|
||||
a.card-link href=(link.href) target=("_blank") {
|
||||
div.card {
|
||||
h2 { (title) }
|
||||
article {
|
||||
header {
|
||||
hgroup {
|
||||
h2 { (title) }
|
||||
a href=(format!("http://{}", main_url)) { (main_url) }
|
||||
}
|
||||
}
|
||||
body {
|
||||
@if !image_src.is_empty() {
|
||||
img src=(image_src) alt="Entry image";
|
||||
}
|
||||
p { (truncated_description) }
|
||||
}
|
||||
footer {
|
||||
a class="grid" href=(link.href) style="--pico-text-decoration: none;" {
|
||||
button class="outline secondary" { "Read Post" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_root_url(input_url: &str) -> &str {
|
||||
let mut url = input_url;
|
||||
|
||||
url = url.strip_prefix("https://").unwrap_or(url);
|
||||
url = url.strip_prefix("http://").unwrap_or(url);
|
||||
|
||||
url.split_once('/').unwrap().0
|
||||
}
|
||||
|
||||
fn truncate_description(description: &str, max_length: usize) -> String {
|
||||
let description_trimmed = description.trim();
|
||||
if description_trimmed.len() > max_length {
|
||||
@ -100,23 +123,116 @@ fn strip_html_tags(html: &str) -> String {
|
||||
text_content.trim().to_string()
|
||||
}
|
||||
|
||||
fn generate_html(entries: Vec<Entry>) -> Markup {
|
||||
fn generate_header() -> Markup {
|
||||
html! {
|
||||
header {
|
||||
nav {
|
||||
ul {
|
||||
li { h1 { "Anson's Aggregated Feed" }}
|
||||
}
|
||||
ul {
|
||||
li { button data-target="about" onclick="toggleModal(event)" { "About" } }
|
||||
li {
|
||||
details class="dropdown" {
|
||||
summary role="button" class="outline secondary" { "Theme" }
|
||||
ul {
|
||||
li { a href="#" data-theme-switcher="auto" { "Auto" }}
|
||||
li { a href="#" data-theme-switcher="light" { "Light" }}
|
||||
li { a href="#" data-theme-switcher="dark" { "Dark" }}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn about_modal(entries: Vec<Entry>) -> Markup {
|
||||
// Get link for each entry, which is a blog post then,
|
||||
// convert it to a url to the main page of the blog
|
||||
let mut links = entries
|
||||
.iter()
|
||||
.map(|entry| entry.links.first().unwrap().href.as_str())
|
||||
.map(get_root_url)
|
||||
.collect::<std::collections::HashSet<&str>>()
|
||||
.into_iter()
|
||||
.collect::<Vec<&str>>();
|
||||
|
||||
// Alphabetical to be fair to everytone :)
|
||||
links.sort();
|
||||
|
||||
html! {
|
||||
dialog id="about" {
|
||||
article {
|
||||
header {
|
||||
a href="#" aria-label="Close" rel="prev" {}
|
||||
p { strong { "About" }}
|
||||
}
|
||||
p {
|
||||
"When looking for a RSS reader I came across "
|
||||
a href="https://news.russellsaw.io/" {"news.russellsaw.io"}
|
||||
" I thought the idea of building my own personalised newspaper was cool. \
|
||||
So, I decided to build a clone using my own subscribed RSS feeds."
|
||||
}
|
||||
p {
|
||||
"This page updates daily at 8:11ish AM Mountain Time. The following blogs are"
|
||||
" in the subscription list:"
|
||||
}
|
||||
ul {
|
||||
@for link in links {
|
||||
li {a href=(link) {(link)}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_footer() -> Markup {
|
||||
html! {
|
||||
footer class="container" {
|
||||
small {
|
||||
p {
|
||||
a href="https://ansonbiggs.com" { "Anson Biggs" }
|
||||
" - 2024 - "
|
||||
a href="gitlab.com" { "Source Code" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_index(entries: Vec<Entry>) -> Markup {
|
||||
let num_columns = 3;
|
||||
let chunk_size = (entries.len() as f32 / num_columns as f32).ceil() as usize;
|
||||
html! {
|
||||
(maud::DOCTYPE)
|
||||
html {
|
||||
head {
|
||||
title { "Anson's Zine" }
|
||||
link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css";
|
||||
meta charset="utf-8";
|
||||
meta name="viewport" content="width=device-width, initial-scale=1";
|
||||
link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.blue.min.css";
|
||||
link rel="stylesheet" href="style.css";
|
||||
}
|
||||
body {
|
||||
h1 { "Anson's Aggregated Feed" }
|
||||
div class="cards-container" {
|
||||
@for entry in entries {
|
||||
{(create_html_card(&entry))}
|
||||
body { main class="container" {
|
||||
{(generate_header())}
|
||||
div class="grid" {
|
||||
@for column_entries in entries.chunks(chunk_size) {
|
||||
div {
|
||||
@for entry in column_entries {
|
||||
{(create_html_card(&entry))}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
{(generate_footer())}
|
||||
{(about_modal(entries))}
|
||||
script src="modal.js" {}
|
||||
script src="minimal-theme-switcher.js" {}
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -125,21 +241,27 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
let binding = fs::read_to_string("feeds.txt").unwrap();
|
||||
let feed_urls: Vec<&str> = binding.lines().collect();
|
||||
|
||||
let mut entries: Vec<Entry> = Vec::new();
|
||||
let raw_entries: Vec<Result<Vec<Entry>, String>> = feed_urls
|
||||
.into_par_iter()
|
||||
.map(|url| {
|
||||
fetch_feed(url).map_err(|e| format!("Failed to fetch or parse feed {}: {}", url, e))
|
||||
})
|
||||
.collect();
|
||||
|
||||
for url in feed_urls {
|
||||
match fetch_feed(url) {
|
||||
// Flatten the entries and filter out the errors
|
||||
let mut entries: Vec<Entry> = Vec::new();
|
||||
for entry in raw_entries {
|
||||
match entry {
|
||||
Ok(mut feed_entries) => entries.append(&mut feed_entries),
|
||||
Err(e) => println!("Failed to fetch or parse feed {}: {}", url, e),
|
||||
Err(e) => println!("{}", e),
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any entries that don't have a timestamp, and then sort by timestamps
|
||||
entries.retain(|entry| entry.published.is_some() || entry.updated.is_some());
|
||||
entries
|
||||
.sort_by_key(|entry| Reverse(entry.published.unwrap_or(entry.updated.unwrap_or_default())));
|
||||
|
||||
let html_string = generate_html(entries).into_string();
|
||||
let html_string = generate_index(entries).into_string();
|
||||
|
||||
let output_path = Path::new("output/index.html");
|
||||
DirBuilder::new()
|
||||
|
Loading…
x
Reference in New Issue
Block a user