Compare commits
13 commits
a49f7dc2dc
...
98fd99f702
Author | SHA1 | Date | |
---|---|---|---|
98fd99f702 | |||
b46b20e693 | |||
e54cd44714 | |||
a3161d822d | |||
0f6f2f1fc4 | |||
2a4ae9f273 | |||
aed4fa2bff | |||
856770c2ae | |||
29cf3e3e00 | |||
b02f715c5a | |||
3e5ac643a7 | |||
1097ee5194 | |||
61170953fe |
9 changed files with 410 additions and 249 deletions
|
@ -1,8 +1,10 @@
|
||||||
use crate::misc::date::Date;
|
use crate::misc::date::Date;
|
||||||
use base64::engine::general_purpose;
|
use base64::engine::general_purpose;
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use comrak::nodes::{AstNode, NodeValue};
|
use comrak::nodes::{AstNode, NodeCode, NodeMath, NodeValue};
|
||||||
use comrak::{format_html, parse_document, Arena, ComrakOptions, ListStyleType, Options};
|
use comrak::{
|
||||||
|
format_html, parse_document, Anchorizer, Arena, ComrakOptions, ListStyleType, Options,
|
||||||
|
};
|
||||||
use lol_html::html_content::ContentType;
|
use lol_html::html_content::ContentType;
|
||||||
use lol_html::{element, rewrite_str, HtmlRewriter, RewriteStrSettings, Settings};
|
use lol_html::{element, rewrite_str, HtmlRewriter, RewriteStrSettings, Settings};
|
||||||
use ramhorns::Content;
|
use ramhorns::Content;
|
||||||
|
@ -117,6 +119,7 @@ impl Metadata {
|
||||||
pub struct File {
|
pub struct File {
|
||||||
pub metadata: Metadata,
|
pub metadata: Metadata,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
|
pub toc_data: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options used for parser and compiler MD --> HTML
|
/// Options used for parser and compiler MD --> HTML
|
||||||
|
@ -151,7 +154,7 @@ pub fn get_options<'a>() -> ComrakOptions<'a> {
|
||||||
// options.render.broken_link_callback = ...;
|
// options.render.broken_link_callback = ...;
|
||||||
|
|
||||||
// Renderer
|
// Renderer
|
||||||
options.render.hardbreaks = false; // could be true? change by metadata could be good for compatibility
|
options.render.hardbreaks = true;
|
||||||
options.render.github_pre_lang = false;
|
options.render.github_pre_lang = false;
|
||||||
options.render.full_info_string = true;
|
options.render.full_info_string = true;
|
||||||
options.render.width = 0; // 0 mean disabled?
|
options.render.width = 0; // 0 mean disabled?
|
||||||
|
@ -286,7 +289,7 @@ pub fn read_md(
|
||||||
) -> File {
|
) -> File {
|
||||||
let arena = Arena::new();
|
let arena = Arena::new();
|
||||||
|
|
||||||
let opt = options.map_or_else(get_options, |specific_opt| specific_opt);
|
let mut opt = options.map_or_else(get_options, |specific_opt| specific_opt);
|
||||||
let root = parse_document(&arena, raw_text, &opt);
|
let root = parse_document(&arena, raw_text, &opt);
|
||||||
|
|
||||||
// Find metadata
|
// Find metadata
|
||||||
|
@ -295,6 +298,11 @@ pub fn read_md(
|
||||||
let mermaid_name = "mermaid";
|
let mermaid_name = "mermaid";
|
||||||
hljs_replace(root, mermaid_name);
|
hljs_replace(root, mermaid_name);
|
||||||
|
|
||||||
|
if let TypeFileMetadata::Blog = metadata_type {
|
||||||
|
// Change by metadata could be good for compatibility
|
||||||
|
opt.render.hardbreaks = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Convert to HTML
|
// Convert to HTML
|
||||||
let mut html = vec![];
|
let mut html = vec![];
|
||||||
format_html(root, &opt, &mut html).unwrap();
|
format_html(root, &opt, &mut html).unwrap();
|
||||||
|
@ -307,6 +315,8 @@ pub fn read_md(
|
||||||
html_content = custom_img_size(&html_content);
|
html_content = custom_img_size(&html_content);
|
||||||
(html_content, mail_obfsucated) = mail_obfuscation(&html_content);
|
(html_content, mail_obfsucated) = mail_obfuscation(&html_content);
|
||||||
|
|
||||||
|
let toc = toc_to_html(&generate_toc(root));
|
||||||
|
|
||||||
let mut final_metadata = Metadata {
|
let mut final_metadata = Metadata {
|
||||||
info: metadata,
|
info: metadata,
|
||||||
mermaid: check_mermaid(root, mermaid_name),
|
mermaid: check_mermaid(root, mermaid_name),
|
||||||
|
@ -319,6 +329,7 @@ pub fn read_md(
|
||||||
File {
|
File {
|
||||||
metadata: final_metadata,
|
metadata: final_metadata,
|
||||||
content: html_content,
|
content: html_content,
|
||||||
|
toc_data: toc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,3 +514,87 @@ fn mail_obfuscation(html: &str) -> (String, bool) {
|
||||||
(new_html, modified)
|
(new_html, modified)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct TOCEntry {
|
||||||
|
id: String,
|
||||||
|
title: String,
|
||||||
|
depth: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_toc<'a>(root: &'a AstNode<'a>) -> Vec<TOCEntry> {
|
||||||
|
/// See <https://github.com/kivikakk/comrak/blob/b67d406d3b101b93539c37a1ca75bff81ff8c149/src/html.rs#L446>
|
||||||
|
fn collect_text<'a>(node: &'a AstNode<'a>, output: &mut String) {
|
||||||
|
match node.data.borrow().value {
|
||||||
|
NodeValue::Text(ref literal)
|
||||||
|
| NodeValue::Code(NodeCode { ref literal, .. })
|
||||||
|
| NodeValue::Math(NodeMath { ref literal, .. }) => {
|
||||||
|
*output = literal.to_string();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
for n in node.children() {
|
||||||
|
if !output.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_text(n, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut toc = vec![];
|
||||||
|
|
||||||
|
let mut anchorizer = Anchorizer::new();
|
||||||
|
|
||||||
|
// Collect headings first to avoid mutable borrow conflicts
|
||||||
|
let headings: Vec<_> = root
|
||||||
|
.children()
|
||||||
|
.filter_map(|node| {
|
||||||
|
if let NodeValue::Heading(ref nch) = &node.data.borrow().value {
|
||||||
|
Some((*nch, node))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Now process each heading
|
||||||
|
for (nch, node) in headings {
|
||||||
|
let mut title = String::with_capacity(20);
|
||||||
|
collect_text(node, &mut title);
|
||||||
|
|
||||||
|
toc.push(TOCEntry {
|
||||||
|
id: anchorizer.anchorize(title.clone()),
|
||||||
|
title,
|
||||||
|
depth: nch.level,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toc
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toc_to_html(toc: &[TOCEntry]) -> String {
|
||||||
|
if toc.is_empty() {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut html = Vec::with_capacity(20 + 20 * toc.len());
|
||||||
|
|
||||||
|
html.extend_from_slice(b"<ul>");
|
||||||
|
|
||||||
|
for entry in toc {
|
||||||
|
// TODO: Use depth
|
||||||
|
html.extend_from_slice(
|
||||||
|
format!(
|
||||||
|
"<li><a href=\"{}\">{} (dbg/depth/{})</a></li>",
|
||||||
|
entry.id, entry.title, entry.depth
|
||||||
|
)
|
||||||
|
.as_bytes(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
html.extend_from_slice(b"</ul>");
|
||||||
|
|
||||||
|
String::from_utf8(html).unwrap()
|
||||||
|
}
|
||||||
|
|
|
@ -81,5 +81,6 @@ fn read_pdf(data: Vec<u8>) -> File {
|
||||||
style="width: 100%; height: 79vh";
|
style="width: 100%; height: 79vh";
|
||||||
>"#
|
>"#
|
||||||
),
|
),
|
||||||
|
toc_data: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,7 +186,6 @@ fn get_posts(location: &str) -> Vec<Post> {
|
||||||
struct BlogPostTemplate {
|
struct BlogPostTemplate {
|
||||||
navbar: NavBar,
|
navbar: NavBar,
|
||||||
post: Option<File>,
|
post: Option<File>,
|
||||||
toc: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/blog/p/{id}")]
|
#[get("/blog/p/{id}")]
|
||||||
|
@ -199,7 +198,7 @@ pub async fn page(path: web::Path<(String,)>, config: web::Data<Config>) -> impl
|
||||||
|
|
||||||
fn build_post(file: &str, config: Config) -> String {
|
fn build_post(file: &str, config: Config) -> String {
|
||||||
let mut post = None;
|
let mut post = None;
|
||||||
let (infos, toc) = get_post(
|
let infos = get_post(
|
||||||
&mut post,
|
&mut post,
|
||||||
file,
|
file,
|
||||||
&config.fc.name.unwrap_or_default(),
|
&config.fc.name.unwrap_or_default(),
|
||||||
|
@ -214,18 +213,12 @@ fn build_post(file: &str, config: Config) -> String {
|
||||||
..NavBar::default()
|
..NavBar::default()
|
||||||
},
|
},
|
||||||
post,
|
post,
|
||||||
toc,
|
|
||||||
},
|
},
|
||||||
infos,
|
infos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_post(
|
fn get_post(post: &mut Option<File>, filename: &str, name: &str, data_dir: &str) -> InfosPage {
|
||||||
post: &mut Option<File>,
|
|
||||||
filename: &str,
|
|
||||||
name: &str,
|
|
||||||
data_dir: &str,
|
|
||||||
) -> (InfosPage, String) {
|
|
||||||
let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}");
|
let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}");
|
||||||
let ext = ".md";
|
let ext = ".md";
|
||||||
|
|
||||||
|
@ -234,13 +227,8 @@ fn get_post(
|
||||||
&TypeFileMetadata::Blog,
|
&TypeFileMetadata::Blog,
|
||||||
);
|
);
|
||||||
|
|
||||||
let default = (
|
let default = (filename, &format!("Blog d'{name}"), Vec::new());
|
||||||
filename,
|
let (title, desc, tags) = match post {
|
||||||
&format!("Blog d'{name}"),
|
|
||||||
Vec::new(),
|
|
||||||
String::new(),
|
|
||||||
);
|
|
||||||
let (title, desc, tags, toc) = match post {
|
|
||||||
Some(data) => (
|
Some(data) => (
|
||||||
match &data.metadata.info.blog.as_ref().unwrap().title {
|
match &data.metadata.info.blog.as_ref().unwrap().title {
|
||||||
Some(text) => text,
|
Some(text) => text,
|
||||||
|
@ -254,16 +242,10 @@ fn get_post(
|
||||||
Some(tags) => tags.clone(),
|
Some(tags) => tags.clone(),
|
||||||
None => default.2,
|
None => default.2,
|
||||||
},
|
},
|
||||||
match &data.metadata.info.blog.as_ref().unwrap().toc {
|
|
||||||
// TODO: Generate TOC
|
|
||||||
Some(true) => String::new(),
|
|
||||||
_ => default.3,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
None => default,
|
None => default,
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
|
||||||
InfosPage {
|
InfosPage {
|
||||||
title: Some(format!("Post: {title}")),
|
title: Some(format!("Post: {title}")),
|
||||||
desc: Some(desc.clone()),
|
desc: Some(desc.clone()),
|
||||||
|
@ -273,9 +255,7 @@ fn get_post(
|
||||||
.chain(tags.iter().map(|t| t.name.as_str()))
|
.chain(tags.iter().map(|t| t.name.as_str()))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)),
|
)),
|
||||||
},
|
}
|
||||||
toc,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[routes]
|
#[routes]
|
||||||
|
|
|
@ -1,21 +1,13 @@
|
||||||
|
@import "../markdown.css";
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
:root {
|
:root {
|
||||||
--code-font-color: #333333;
|
|
||||||
--code-bg-color: #eeeeee;
|
|
||||||
--quote-border-color: #9852fa;
|
|
||||||
--quote-bg-color: #d8d6d6;
|
|
||||||
--separator-color: #cccccc;
|
|
||||||
--tag-bg-color: #d2e0f0;
|
--tag-bg-color: #d2e0f0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--code-font-color: #eeeeee;
|
|
||||||
--code-bg-color: #333333;
|
|
||||||
--quote-border-color: #bd93f9;
|
|
||||||
--quote-bg-color: #273341;
|
|
||||||
--separator-color: #414558;
|
|
||||||
--tag-bg-color: #242e38;
|
--tag-bg-color: #242e38;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,11 +16,6 @@
|
||||||
--max-width: 750px;
|
--max-width: 750px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Page */
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
max-width: var(--max-width);
|
max-width: var(--max-width);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
@ -70,49 +57,8 @@ main {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Anchors */
|
|
||||||
:is(h1, h2, h3, h4, h5, h6):hover a.anchor::before {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.anchor::before {
|
|
||||||
content: "#";
|
|
||||||
visibility: hidden;
|
|
||||||
padding-right: 0.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.anchor {
|
|
||||||
text-decoration: none;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Links in headers */
|
|
||||||
:is(h1, h2, h3, h4, h5, h6) a {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Separators */
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
height: 1px;
|
|
||||||
background: var(--separator-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Quotes */
|
|
||||||
blockquote {
|
|
||||||
margin: 1em 0;
|
|
||||||
padding: 0.1em 10px;
|
|
||||||
border-left: 6px solid;
|
|
||||||
border-color: var(--quote-border-color);
|
|
||||||
background: var(--quote-bg-color);
|
|
||||||
border-top-right-radius: 5px;
|
|
||||||
border-bottom-right-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Images */
|
/* Images */
|
||||||
img {
|
img {
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
max-width: var(--max-width);
|
max-width: var(--max-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,115 +68,6 @@ code {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Little snippet of code (not blocks) */
|
|
||||||
kbd,
|
|
||||||
code:not(.hljs):not(:has(svg)) {
|
|
||||||
background: var(--code-bg-color);
|
|
||||||
border-radius: 3px;
|
|
||||||
color: var(--code-font-color);
|
|
||||||
box-shadow: 0 1px 1px black;
|
|
||||||
font-size: calc(var(--font-size) * 0.8);
|
|
||||||
padding: 2px 4px;
|
|
||||||
vertical-align: 1.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code blocks */
|
|
||||||
.hljs {
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs::-webkit-scrollbar {
|
|
||||||
width: 7px;
|
|
||||||
height: 9px;
|
|
||||||
background: var(--background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs::-webkit-scrollbar-thumb {
|
|
||||||
background-color: var(--font-color);
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Marge for numbers */
|
|
||||||
.hljs-ln-n {
|
|
||||||
margin-right: 0.4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Numbers in codeblocks */
|
|
||||||
.hljs-ln-numbers {
|
|
||||||
text-align: right;
|
|
||||||
color: var(--font-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fix scroll in codeblocks with line numbering */
|
|
||||||
table.hljs-ln {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Background for copy code button */
|
|
||||||
.hljs-copy-button {
|
|
||||||
background-color: var(--background) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Light theme for the copy code button */
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
.hljs-copy-button {
|
|
||||||
background-color: var(--font-color) !important;
|
|
||||||
filter: invert(100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide last line in codeblocks if empty */
|
|
||||||
.hljs-ln
|
|
||||||
> tbody
|
|
||||||
> tr:last-child:has(td:last-child > span::-moz-only-whitespace) {
|
|
||||||
visibility: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Temporary fix for layout.css.has-selector.enabled available only on
|
|
||||||
* Firefox under certain circumstances */
|
|
||||||
.hljs-ln > tbody > tr:last-child {
|
|
||||||
visibility: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reference to footnotes */
|
|
||||||
.footnote-ref a {
|
|
||||||
text-decoration: underline dotted;
|
|
||||||
font-size: calc(var(--font-size) * 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footnote */
|
|
||||||
section.footnotes * {
|
|
||||||
font-size: calc(var(--font-size) * 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* When multiple ref */
|
|
||||||
a.footnote-backref sup {
|
|
||||||
font-size: calc(var(--font-size) * 0.6);
|
|
||||||
}
|
|
||||||
a.footnote-backref sup::before {
|
|
||||||
content: "(";
|
|
||||||
}
|
|
||||||
a.footnote-backref sup::after {
|
|
||||||
content: ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footnotes links */
|
|
||||||
a.footnote-backref {
|
|
||||||
font-family: "Segoe UI", "Segoe UI Symbol", system-ui;
|
|
||||||
text-decoration: underline dotted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Footnotes block separation from article */
|
|
||||||
section.footnotes {
|
|
||||||
margin: 3px;
|
|
||||||
border-top: 2px dotted var(--separator-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mermaid diagrams */
|
|
||||||
pre:has(code.language-mermaid) {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Table of content */
|
/* Table of content */
|
||||||
nav#toc {
|
nav#toc {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -246,36 +83,3 @@ nav#toc {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
|
||||||
/* Better colors for paper */
|
|
||||||
blockquote {
|
|
||||||
border-color: black;
|
|
||||||
background: var(--background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
background: var(--background);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Force line numbering to be on top */
|
|
||||||
td.hljs-ln-line {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Break code */
|
|
||||||
code.hljs {
|
|
||||||
white-space: break-spaces;
|
|
||||||
hyphens: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hide arrows of backref */
|
|
||||||
a.footnote-backref {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No underline for footnotes */
|
|
||||||
.footnote-ref > a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
@import "markdown.css";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--max-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
max-width: var(--max-width);
|
||||||
|
}
|
||||||
|
|
||||||
/* Filetree */
|
/* Filetree */
|
||||||
aside {
|
aside {
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -48,6 +58,4 @@ aside li.directory {
|
||||||
|
|
||||||
main img {
|
main img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
}
|
}
|
||||||
|
|
248
static/css/markdown.css
Normal file
248
static/css/markdown.css
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--code-font-color: #333333;
|
||||||
|
--code-bg-color: #eeeeee;
|
||||||
|
--quote-border-color: #9852fa;
|
||||||
|
--quote-bg-color: #d8d6d6;
|
||||||
|
--separator-color: #cccccc;
|
||||||
|
--tag-bg-color: #d2e0f0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--code-font-color: #eeeeee;
|
||||||
|
--code-bg-color: #333333;
|
||||||
|
--quote-border-color: #bd93f9;
|
||||||
|
--quote-bg-color: #273341;
|
||||||
|
--separator-color: #414558;
|
||||||
|
--tag-bg-color: #242e38;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page */
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Anchors */
|
||||||
|
main :is(h1, h2, h3, h4, h5, h6):hover a.anchor::before {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
main a.anchor::before {
|
||||||
|
content: "#";
|
||||||
|
visibility: hidden;
|
||||||
|
padding-right: 0.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
main a.anchor {
|
||||||
|
text-decoration: none;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links in headers */
|
||||||
|
:is(h1, h2, h3, h4, h5, h6) a {
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Images */
|
||||||
|
main img {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Separators */
|
||||||
|
hr {
|
||||||
|
border: 0;
|
||||||
|
height: 1px;
|
||||||
|
background: var(--separator-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quotes */
|
||||||
|
blockquote {
|
||||||
|
margin: 1em 0;
|
||||||
|
padding: 0.1em 10px;
|
||||||
|
border-left: 6px solid;
|
||||||
|
border-color: var(--quote-border-color);
|
||||||
|
background: var(--quote-bg-color);
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Little snippet of code (not blocks) */
|
||||||
|
kbd,
|
||||||
|
code:not(.hljs):not(:has(svg)) {
|
||||||
|
background: var(--code-bg-color);
|
||||||
|
border-radius: 3px;
|
||||||
|
color: var(--code-font-color);
|
||||||
|
box-shadow: 0 1px 1px black;
|
||||||
|
font-size: calc(var(--font-size) * 0.8);
|
||||||
|
padding: 2px 4px;
|
||||||
|
vertical-align: 1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
.hljs {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs::-webkit-scrollbar {
|
||||||
|
width: 7px;
|
||||||
|
height: 9px;
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--font-color);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Marge for numbers */
|
||||||
|
.hljs-ln-n {
|
||||||
|
margin-right: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Numbers in codeblocks */
|
||||||
|
.hljs-ln-numbers {
|
||||||
|
text-align: right;
|
||||||
|
color: var(--font-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix scroll in codeblocks with line numbering */
|
||||||
|
table.hljs-ln {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Background for copy code button */
|
||||||
|
.hljs-copy-button {
|
||||||
|
background-color: var(--background) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light theme for the copy code button */
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.hljs-copy-button {
|
||||||
|
background-color: var(--font-color) !important;
|
||||||
|
filter: invert(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide last line in codeblocks if empty */
|
||||||
|
.hljs-ln
|
||||||
|
> tbody
|
||||||
|
> tr:last-child:has(td:last-child > span::-moz-only-whitespace) {
|
||||||
|
visibility: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Temporary fix for layout.css.has-selector.enabled available only on
|
||||||
|
* Firefox under certain circumstances */
|
||||||
|
.hljs-ln > tbody > tr:last-child {
|
||||||
|
visibility: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reference to footnotes */
|
||||||
|
.footnote-ref a {
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
font-size: calc(var(--font-size) * 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footnote */
|
||||||
|
section.footnotes * {
|
||||||
|
font-size: calc(var(--font-size) * 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When multiple ref */
|
||||||
|
a.footnote-backref sup {
|
||||||
|
font-size: calc(var(--font-size) * 0.6);
|
||||||
|
}
|
||||||
|
a.footnote-backref sup::before {
|
||||||
|
content: "(";
|
||||||
|
}
|
||||||
|
a.footnote-backref sup::after {
|
||||||
|
content: ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footnotes links */
|
||||||
|
a.footnote-backref {
|
||||||
|
font-family: "Segoe UI", "Segoe UI Symbol", system-ui;
|
||||||
|
text-decoration: underline dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footnotes block separation from content */
|
||||||
|
section.footnotes {
|
||||||
|
margin: 3px;
|
||||||
|
border-top: 2px dotted var(--separator-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mermaid diagrams */
|
||||||
|
pre:has(code.language-mermaid) {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables */
|
||||||
|
table:not(.hljs-ln) {
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
table:not(.hljs-ln) th,
|
||||||
|
table:not(.hljs-ln) td {
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid var(--separator-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
table:not(.hljs-ln)th {
|
||||||
|
border-bottom: 2px solid var(--separator-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No borders on the outer edges of the table */
|
||||||
|
table:not(.hljs-ln) tr:last-child td {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table:not(.hljs-ln) tr:first-child th {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table:not(.hljs-ln) tr td:first-child,
|
||||||
|
table:not(.hljs-ln) tr th:first-child {
|
||||||
|
border-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table:not(.hljs-ln) tr td:last-child,
|
||||||
|
table:not(.hljs-ln) tr th:last-child {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
/* Better colors for paper */
|
||||||
|
blockquote {
|
||||||
|
border-color: black;
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
background: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force line numbering to be on top */
|
||||||
|
td.hljs-ln-line {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Break code */
|
||||||
|
code.hljs {
|
||||||
|
white-space: break-spaces;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide arrows of backref */
|
||||||
|
a.footnote-backref {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No underline for footnotes */
|
||||||
|
.footnote-ref > a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
|
@ -80,11 +80,16 @@ const deepestNodeOpened = (path, options) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const svgDarkTheme = () => {
|
const Mode = {
|
||||||
|
Light: 1,
|
||||||
|
Dark: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const svgChangeTheme = (mode) => {
|
||||||
for (const item of document.getElementsByTagName("img")) {
|
for (const item of document.getElementsByTagName("img")) {
|
||||||
if (!item.src.startsWith("data:image/svg+xml;base64,")) {
|
if (!item.src.startsWith("data:image/svg+xml;base64,")) {
|
||||||
// Exclude image who aren't SVG and base64 encoded
|
// Exclude image who aren't SVG and base64 encoded
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Convert to grayscale */
|
/** Convert to grayscale */
|
||||||
|
@ -129,9 +134,19 @@ const svgDarkTheme = () => {
|
||||||
const totalGrayscale = grayscaleValues.reduce((acc, val) => acc + val, 0);
|
const totalGrayscale = grayscaleValues.reduce((acc, val) => acc + val, 0);
|
||||||
const averageGrayscale = totalGrayscale / grayscaleValues.length;
|
const averageGrayscale = totalGrayscale / grayscaleValues.length;
|
||||||
|
|
||||||
if (averageGrayscale < 128) {
|
const treshold = 128;
|
||||||
|
|
||||||
|
if (averageGrayscale < treshold && mode === Mode.Dark) {
|
||||||
item.style = "filter: invert(1);";
|
item.style = "filter: invert(1);";
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (averageGrayscale > treshold && mode === Mode.Light) {
|
||||||
|
item.style = "filter: invert(1);";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.style = "";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -160,8 +175,16 @@ window.addEventListener("load", () => {
|
||||||
uncollapse(last_openeded);
|
uncollapse(last_openeded);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix SVG images in dark mode
|
// Fix SVG images
|
||||||
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
svgChangeTheme(
|
||||||
svgDarkTheme();
|
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
}
|
? Mode.Dark
|
||||||
|
: Mode.Light
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window
|
||||||
|
.matchMedia("(prefers-color-scheme: dark)")
|
||||||
|
.addEventListener("change", (event) =>
|
||||||
|
svgChangeTheme(event.matches ? Mode.Dark : Mode.Light)
|
||||||
|
);
|
||||||
|
|
|
@ -2,17 +2,17 @@ window.addEventListener("load", () => {
|
||||||
const macros = {};
|
const macros = {};
|
||||||
for (const item of new Map(
|
for (const item of new Map(
|
||||||
Object.entries({
|
Object.entries({
|
||||||
|
B: "mathbb{B}",
|
||||||
N: "mathbb{N}",
|
N: "mathbb{N}",
|
||||||
R: "mathbb{R}",
|
R: "mathbb{R}",
|
||||||
Z: "mathbb{Z}",
|
Z: "mathbb{Z}",
|
||||||
B: "mathbb{B}",
|
|
||||||
O: "Theta",
|
O: "Theta",
|
||||||
|
Tau: "mathrm{T}",
|
||||||
|
u: "mu",
|
||||||
ra: "rightarrow",
|
ra: "rightarrow",
|
||||||
la: "leftarrow",
|
la: "leftarrow",
|
||||||
RA: "Rightarrow",
|
RA: "Rightarrow",
|
||||||
LA: "Leftarrow",
|
LA: "Leftarrow",
|
||||||
u: "mu",
|
|
||||||
Tau: "mathrm{T}",
|
|
||||||
lb: "llbracket",
|
lb: "llbracket",
|
||||||
rb: "rrbracket",
|
rb: "rrbracket",
|
||||||
})
|
})
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
<main>
|
<main>
|
||||||
{{^post}}
|
{{^post}}
|
||||||
<p>This post doesn't exist... sorry</p>
|
<p>This post doesn't exist... sorry</p>
|
||||||
{{/post}} {{#post}} {{&toc}}
|
{{/post}} {{#post}} {{#metadata}} {{#info}} {{#blog}} {{#toc}}
|
||||||
|
<aside>{{&toc_data}}</aside>
|
||||||
|
{{/toc}} {{/blog}} {{/info}} {{/metadata}}
|
||||||
<article>{{&content}}</article>
|
<article>{{&content}}</article>
|
||||||
{{/post}}
|
{{/post}}
|
||||||
</main>
|
</main>
|
||||||
|
|
Loading…
Reference in a new issue