diff --git a/Cargo.lock b/Cargo.lock index 2ea43d5..a17bc31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,6 +187,16 @@ name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "chrono" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "clang-sys" version = "0.28.1" @@ -404,8 +414,10 @@ name = "mkbook" version = "0.1.0" dependencies = [ "askama 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "comrak 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "sass-rs 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", "syntect 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -421,6 +433,15 @@ dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num-traits" version = "0.2.10" @@ -563,6 +584,11 @@ dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "regex" version = "1.3.1" @@ -736,6 +762,16 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.4.10" @@ -902,6 +938,7 @@ dependencies = [ "checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" "checksum cexpr 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" "checksum clang-sys 0.28.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum comrak 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea4c29f52463abf5c7a3ae33dd9b404e2031af82f547cfe65bfac17ba785ea2e" @@ -931,6 +968,7 @@ dependencies = [ "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" "checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" "checksum onig 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4e723fc996fff1aeab8f62205f3e8528bf498bdd5eadb2784d2d31f30077947" @@ -948,6 +986,7 @@ dependencies = [ "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" @@ -968,6 +1007,7 @@ dependencies = [ "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01d1404644c8b12b16bfcffa4322403a91a451584daaaa7c28d3152e6cbc98cf" "checksum twoway 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6b40075910de3a912adbd80b5d8bad6ad10a23eeb1f5bf9d4006839e899ba5bc" diff --git a/Cargo.toml b/Cargo.toml index e82de1c..506a8b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ clap = "2.33" askama = "0.8" serde = { version = "1.0", features = ["derive"] } toml = "0.5" +lazy_static = "1.4" +chrono = "0.4" [build-dependencies] sass-rs = "0.2" diff --git a/README.md b/README.md index f074f64..7f29d0c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mkbook -**mkbook** is my simpler alternative to [mdbook](https://crates.io/crates/mdbook) which is a great tool, but for which I really dislike some of the decisions they took, such as relying on javascript for highlighting and navigation, and including a lot of bells and whistles such as javascript-based search. +_mkbook_ is my simpler alternative to [mdbook](https://crates.io/crates/mdbook) which is a great tool, but for which I really dislike some of the decisions they took, such as relying on javascript for highlighting and navigation, and including a lot of bells and whistles such as javascript-based search. This tool aims to work somewhat similarly to _mdbook_, but is generally intended to be a more minimal alternative that is customized more towards my needs and desires than anything else. -Still very WIP, but it can convert `.md` files into fancy-looking `.html` files, demo it by building the `mkbook` book by running: `cargo run -- build -i docs-src -o docs` and then serving the `docs` directory. Alternatively, view these generated docs [here](https://hamaluik.github.io/mkbook/01-introduction.html). +Still very WIP, but it can convert `.md` files into fancy-looking `.html` files, demo it by building the _mkbook_ book by running: `cargo run -- build -i docs-src -o docs` and then serving the `docs` directory. Alternatively, view these generated docs [here](https://hamaluik.github.io/mkbook/). diff --git a/assets/icons.svg b/assets/icons.svg index d50227b..8ec8e56 100644 --- a/assets/icons.svg +++ b/assets/icons.svg @@ -8,5 +8,9 @@ arrow-right + +arrow-up + + diff --git a/docs-src/mkbook.toml b/docs-src/mkbook.toml new file mode 100644 index 0000000..c12461d --- /dev/null +++ b/docs-src/mkbook.toml @@ -0,0 +1,8 @@ +title = "The mkbook Book" +author = "Kenton Hamaluik" +url = "https://hamaluik.github.io/mkbook/" +description = """ +_mkbook_ is my simpler alternative to [mdbook](https://crates.io/crates/mdbook) which is a great tool, but for which I really dislike some of the decisions they took, such as relying on javascript for highlighting and navigation, and including a lot of bells and whistles such as javascript-based search. + +This tool aims to work somewhat similarly to _mdbook_, but is generally intended to be a more minimal alternative that is customized more towards my needs and desires than anything else. +""" \ No newline at end of file diff --git a/docs/01-introduction.html b/docs/01-introduction.html index e170080..f1bbaae 100644 --- a/docs/01-introduction.html +++ b/docs/01-introduction.html @@ -3,31 +3,52 @@ - Introduction + The mkbook Book | Introduction + + + + + + + -

mkbook is my simpler alternative to mdbook which is a great tool, but for which I really dislike some of the decisions they took, such as relying on javascript for highlighting and navigation, and including a lot of bells and whistles such as javascript-based search.

+
+

mkbook is my simpler alternative to mdbook which is a great tool, but for which I really dislike some of the decisions they took, such as relying on javascript for highlighting and navigation, and including a lot of bells and whistles such as javascript-based search.

This tool aims to work somewhat similarly to mdbook, but is generally intended to be a more minimal alternative that is customized more towards my needs and desires than anything else.

mkbook may be installed using Cargo (cargo install --force --path . in the mkbook repo directory), and after that it presents a command-line interface:

@@ -78,6 +100,21 @@
     -i, --in <in>      an optional directory to take the book sources from [default: src]
     -o, --out <out>    an optional directory to render the contents into [default: book]
 
-
+ + + +
+ + Next chapter: “Markdown” + + + + + + +
+ + +
\ No newline at end of file diff --git a/docs/02-markdown.html b/docs/02-markdown.html index dcd7d82..0f1d913 100644 --- a/docs/02-markdown.html +++ b/docs/02-markdown.html @@ -3,26 +3,41 @@ - Markdown + The mkbook Book | Markdown + + + + + + + -

mkbook nominally utilizes CommonMark with some GFM extensions through the use of the comrak crate. In using comrak, a specific set of options are used, which are listed here:

+
+

mkbook nominally utilizes CommonMark with some GFM extensions through the use of the comrak crate. In using comrak, a specific set of options are used, which are listed here:

 let options: ComrakOptions = ComrakOptions {
     hardbreaks: false,
@@ -84,6 +100,21 @@
 
-
+ + + +
+ + Next chapter: “Front Matter” + + + + + + +
+ + +
\ No newline at end of file diff --git a/docs/03-frontmatter.html b/docs/03-frontmatter.html index ac82828..e473357 100644 --- a/docs/03-frontmatter.html +++ b/docs/03-frontmatter.html @@ -3,26 +3,41 @@ - Front Matter + The mkbook Book | Front Matter + + + + + + + -

Each .md file can optionally contain a header with metadata describing the document. If the header isn’t present, default values will be used which may look ugly.

+
+

Each .md file can optionally contain a header with metadata describing the document. If the header isn’t present, default values will be used which may look ugly.

To insert a header into a .md file, insert three dashes (---), followed by a new-line, followed by the front matter contents, followed by a newline, then another three dashes and a new-line. The metadata is in the TOML format, so for example the front-matter (and first line) for this file looks like:

 ---
@@ -60,6 +76,21 @@
 

A human-readable title for the document

-
+ + + +
+ + Next chapter: “Structure” + + + + + + +
+ + +
\ No newline at end of file diff --git a/docs/04-structure.html b/docs/04-structure.html index c9be18f..98660a2 100644 --- a/docs/04-structure.html +++ b/docs/04-structure.html @@ -3,26 +3,41 @@ - Structure + The mkbook Book | Structure + + + + + + + -

mkbook currently only supports two types of assets to use in rendering: assets (images, etc), and documents (markdown files).

+
+

mkbook currently only supports two types of assets to use in rendering: assets (images, etc), and documents (markdown files).

Assets

 unimplemented!()
@@ -51,6 +67,11 @@
 └── etc...
 

An index and navigation will be automatically generated from these files, taking the information for each file from it’s front-matter.

-
+ + + + + +
\ No newline at end of file diff --git a/docs/icons.svg b/docs/icons.svg index d50227b..8ec8e56 100644 --- a/docs/icons.svg +++ b/docs/icons.svg @@ -8,5 +8,9 @@ arrow-right + +arrow-up + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..15b6028 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,46 @@ + + + + + + The mkbook Book + + + + + + + + + + + +
+

The mkbook Book

+

by Kenton Hamaluik

+ +
+
+

mkbook is my simpler alternative to mdbook which is a great tool, but for which I really dislike some of the decisions they took, such as relying on javascript for highlighting and navigation, and including a lot of bells and whistles such as javascript-based search.

+

This tool aims to work somewhat similarly to mdbook, but is generally intended to be a more minimal alternative that is customized more towards my needs and desires than anything else.

+ +
+ + + + \ No newline at end of file diff --git a/docs/style.css b/docs/style.css index ea96046..da0d1a2 100644 --- a/docs/style.css +++ b/docs/style.css @@ -1 +1 @@ -@import url("https://fonts.googleapis.com/css?family=Crimson+Pro|Poppins:700|Source+Code+Pro&display=swap");body{margin:0;line-height:1.5;font-size:14pt;color:#222222;background:#eeeeee;padding:0;font-family:"Crimson Pro","Georgia",Georgia,"Times New Roman",Times,serif}h1,h2,h3{margin-top:0;line-height:1.2;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif}a{color:#222222;text-decoration:underline}a:hover{color:#8a2888;text-decoration:none}figure{display:block;text-align:center;overflow-x:auto}figure img,figure video{max-width:100%}figure figcaption{display:block;font-size:0.75em;text-align:center}code{margin:0 2px;padding:0 2px;border:1px solid #4c566a;border-radius:3px;word-break:break-all;font-family:"Source Code Pro","Courier New",Courier,monospace;font-size:0.75em}pre{overflow-x:auto;font-family:"Source Code Pro","Courier New",Courier,monospace;padding:0.5em}dl{display:grid;grid-template-columns:auto 1fr}dl dt{font-weight:700;margin:0;padding:0.5em;border-right:1px solid #dddddd}dl dd{margin:0;padding:0.5em}dl dt p,dl dd p{margin:0}html,body{width:100%;min-height:100vh}body{display:grid;grid-template-columns:auto 1fr;grid-template-rows:1fr;justify-items:stretch;align-items:stretch}body nav.big{background:#2c2c38;padding:1em;display:flex;flex-direction:column}body nav.big a{width:100%;font-size:1.5em;text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin:0 0 0.25em 0;color:#5babd1}body nav.big a:hover{color:#cf5ccd}body nav.big a.current{color:#cf5ccd}body nav.big a.current:hover{color:#fefefe}body nav.small{display:none;width:100%;align-items:center;justify-content:space-between;background:#2c2c38;padding:0}body nav.small>*{margin:0.5em}body nav.small a{text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#5babd1}body nav.small a:hover{color:#cf5ccd}body nav.small span.title{text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#fefefe}body nav.small span.placeholder{width:1em;height:1em}body article{padding:1em 2em 0 2em;max-width:38em;min-width:0;min-height:0}body article>*{max-width:100%}@media screen and (max-width: 768px){body{grid-template-columns:1fr;grid-template-rows:auto 1fr}body nav.big{display:none}body nav.small{display:flex}body article{padding:1em 0.5em 0 0.5em}}span.icon{display:flex;align-items:center}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:1em;height:1em;stroke-width:0;stroke:currentColor;fill:currentColor}.icon-arrow-left{width:0.875em}.icon-arrow-right{width:0.875em}@media (prefers-color-scheme: dark){body{background-color:#222222;color:#eeeeee}a{color:#eeeeee}a:hover{color:#5babd1}nav{background:#18181d}img{filter:grayscale(30%)}dl dt{border-right:1px solid #333333}}@media print{body{background:#ffffff;color:#000000}a{color:#000000;text-decoration:underline}h2,h3{break-after:avoid-page}figure{break-inside:avoid}p{orphans:2;widows:2}*{overflow:hidden}nav{display:none}body{display:block}} +@import url("https://fonts.googleapis.com/css?family=Crimson+Pro|Poppins:700|Source+Code+Pro&display=swap");body{margin:0;line-height:1.5;font-size:14pt;color:#222222;background:#eeeeee;padding:0;font-family:"Crimson Pro","Georgia",Georgia,"Times New Roman",Times,serif}h1,h2,h3{margin-top:0;line-height:1.2;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif}a{color:#222222;text-decoration:underline}a:hover{color:#8a2888;text-decoration:none}figure{display:block;text-align:center;overflow-x:auto}figure img,figure video{max-width:100%}figure figcaption{display:block;font-size:0.75em;text-align:center}code,kbd{margin:0 2px;padding:0 2px;border:1px solid #4c566a;border-radius:3px;word-break:break-all;font-family:"Source Code Pro","Courier New",Courier,monospace;font-size:0.75em}kbd{color:#222222;background:#eeeeee;box-shadow:0px 2px 4px rgba(0,0,0,0.5)}pre{overflow-x:auto;font-family:"Source Code Pro","Courier New",Courier,monospace;padding:0.5em}dl{display:grid;grid-template-columns:auto 1fr}dl dt{font-weight:700;margin:0;padding:0.5em;border-right:1px solid #dddddd}dl dd{margin:0;padding:0.5em}dl dt p,dl dd p{margin:0}p{margin-top:0}footer{color:#444444}footer p{margin-right:0.5em}html,body{width:100%;min-height:100vh}body{display:grid;grid-template-columns:auto 1fr;grid-template-rows:1fr;justify-items:stretch;align-items:stretch}body nav.big{background:#2c2c38;padding:0.5em 1em 1em;display:flex;flex-direction:column}body nav.big header{margin:0 0 1em;display:flex;flex-direction:column;align-items:stretch;border-bottom:2px solid #888888}body nav.big header h1{font-size:1.5em;font-weight:700;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0;text-align:left;color:#eeeeee}body nav.big header h1 a{color:#eeeeee;text-decoration:none}body nav.big header h1 a:hover{color:#cf5ccd}body nav.big header h2{font-size:1em;font-weight:600;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0;text-align:right}body nav.big ol{margin:0 2em;padding:0}body nav.big ol li{width:100%;font-size:1.25em;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#eeeeee}body nav.big ol li a{text-decoration:none;margin:0 0 0.25em 0;color:#5babd1}body nav.big ol li a:hover{color:#cf5ccd}body nav.big ol li a.current{color:#cf5ccd}body nav.big ol li a.current:hover{color:#fefefe}body nav.small{display:none;width:100%;align-items:center;justify-content:space-between;background:#2c2c38;padding:0}body nav.small>*{margin:0.5em}body nav.small a{text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#5babd1}body nav.small a:hover{color:#cf5ccd}body nav.small span.title{text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#fefefe}body nav.small span.placeholder{width:1em;height:1em}body>*{min-width:0;min-height:0}body article{max-width:38em;padding:0.5em 2em 0 2em}body article>*{max-width:100%}body .next-chapter{width:100%}body .next-chapter a{display:flex;align-items:center;justify-content:flex-end;margin-right:0.5em;font-size:1.2em;text-decoration:none;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;color:#5babd1}body .next-chapter a:hover{color:#cf5ccd}body .next-chapter a span:first-child{margin-right:0.5em}body footer{width:100%;text-align:right;display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-end}@media screen and (max-width: 768px){body{grid-template-columns:1fr;grid-template-rows:auto 1fr}body nav.big{display:none}body nav.small{display:flex}body article{padding:1em 0.5em 0 0.5em}}body.toc{display:flex;flex-direction:column;justify-content:flex-start;align-items:center}body.toc>*{width:36em}body.toc header{margin-top:1em;display:flex;flex-direction:column;align-items:center}body.toc header h1{font-size:3em;font-weight:700;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0}body.toc header h2{font-size:1em;font-weight:600;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif;margin-bottom:0}body.toc header time{font-size:1em;font-weight:400;font-family:"Crimson Pro","Georgia",Georgia,"Times New Roman",Times,serif}body.toc nav{background:none;display:flex;flex-direction:column;align-items:stretch}body.toc nav h1{text-align:left;font-size:2.5em;font-weight:600;margin-bottom:0}body.toc nav ol{margin:0.25em 0 0}body.toc nav ol li{font-size:1.5em;font-family:"Poppins","Franklin Gothic Medium","Arial Narrow",Arial,sans-serif}body.toc nav ol li a{text-decoration:none;margin:0 0 0.25em 0;color:#5babd1}body.toc nav ol li a:hover{color:#cf5ccd}body.toc footer{flex:1}@media screen and (max-width: 768px){body.toc>*{width:100%}body.toc header h1,body.toc header h2,body.toc header time{text-align:center}body.toc article,body.toc nav{padding:0}body.toc article>*,body.toc nav>*{margin-left:0.5em;margin-right:0.5em}body.toc nav h1{font-size:1.75em}}span.icon{display:flex;align-items:center}[class^="icon-"],[class*=" icon-"]{display:inline-block;width:1em;height:1em;stroke-width:0;stroke:currentColor;fill:currentColor}.icon-arrow-left{width:0.875em}.icon-arrow-right{width:0.875em}.icon-arrow-up{width:0.875em}@media (prefers-color-scheme: dark){body{background-color:#222222;color:#eeeeee}a{color:#eeeeee}a:hover{color:#5babd1}nav{background:#18181d}img{filter:grayscale(30%)}dl dt{border-right:1px solid #333333}footer{color:#cccccc}}@media print{body{background:#ffffff;color:#000000}body:not(.toc) nav{display:none}a{color:#000000;text-decoration:underline}h2,h3{break-after:avoid-page}figure{break-inside:avoid}p{orphans:2;widows:2}*{overflow:hidden}body{display:block}} diff --git a/src/filters.rs b/src/filters.rs new file mode 100644 index 0000000..3cd2871 --- /dev/null +++ b/src/filters.rs @@ -0,0 +1,13 @@ +use chrono::prelude::*; + +pub fn rfc3339_utc(d: &DateTime) -> askama::Result { + Ok(d.to_rfc3339()) +} + +pub fn human_date(d: &DateTime) -> askama::Result { + Ok(d.format("%b %e, %Y").to_string()) +} + +pub fn year(d: &DateTime) -> askama::Result { + Ok(d.format("%Y").to_string()) +} diff --git a/src/main.rs b/src/main.rs index 0ec60b9..b241d35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,52 @@ +#[macro_use] +extern crate lazy_static; + use std::path::PathBuf; use std::{fs, io}; +use comrak::ComrakOptions; +use syntect::{parsing::SyntaxSet, highlighting::{ThemeSet, Theme}}; +use askama::Template; pub const STYLESHEET: &'static str = include_str!(concat!(env!("OUT_DIR"), "/style.css")); pub const ASSET_FAVICON: &'static [u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/favicon.ico")); pub const ASSET_ICONS: &'static [u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/icons.svg")); +lazy_static! { + static ref HIGHLIGHT_SYNTAX_SETS: SyntaxSet = SyntaxSet::load_defaults_newlines(); + static ref HIGHLIGHT_THEME_SETS: ThemeSet = ThemeSet::load_defaults(); + static ref HIGHLIGHT_THEME: &'static Theme = &HIGHLIGHT_THEME_SETS.themes["base16-eighties.dark"]; + static ref COMRAK_OPTIONS: ComrakOptions = ComrakOptions { + hardbreaks: false, + smart: true, + github_pre_lang: false, + default_info_string: None, + unsafe_: true, + ext_strikethrough: true, + ext_tagfilter: false, + ext_table: true, + ext_autolink: true, + ext_tasklist: true, + ext_superscript: true, + ext_header_ids: Some("header".to_owned()), + ext_footnotes: true, + ext_description_lists: true, + ..ComrakOptions::default() + }; +} + mod cli; mod models; +mod filters; + +use models::frontmatter::{ParsedFrontMatter, FrontMatter}; +use models::book::{ParsedBook, Book}; fn format_code(lang: &str, src: &str) -> Result> { - use syntect::parsing::{SyntaxSet, SyntaxReference}; - use syntect::highlighting::{ThemeSet}; + use syntect::parsing::SyntaxReference; use syntect::html::highlighted_html_for_string; - let ss = SyntaxSet::load_defaults_newlines(); - let ts = ThemeSet::load_defaults(); - let theme = &ts.themes["base16-eighties.dark"]; - let syntax: Option<&SyntaxReference> = if lang.len() > 0 { - let syntax = ss.find_syntax_by_token(lang); + let syntax = HIGHLIGHT_SYNTAX_SETS.find_syntax_by_token(lang); if syntax.is_none() { eprintln!("warning: language `{}` not recognized, formatting code block as plain text!", lang); } @@ -27,14 +55,14 @@ fn format_code(lang: &str, src: &str) -> Result Result<(Option, String), Box> { +fn extract_frontmatter(src: &str) -> Result<(Option, String), Box> { if src.starts_with("---\n") { let slice = &src[4..]; let end = slice.find("---\n"); @@ -45,7 +73,7 @@ fn extract_frontmatter(src: &str) -> Result<(Option Result<(Option Result<(Option Result> { - use comrak::{Arena, parse_document, format_html, ComrakOptions}; + use comrak::{Arena, parse_document, format_html}; use comrak::nodes::{AstNode, NodeValue}; - let options: ComrakOptions = ComrakOptions { - hardbreaks: false, - smart: true, - github_pre_lang: false, - default_info_string: None, - unsafe_: true, - ext_strikethrough: true, - ext_tagfilter: false, - ext_table: true, - ext_autolink: true, - ext_tasklist: true, - ext_superscript: true, - ext_header_ids: Some("header".to_owned()), - ext_footnotes: true, - ext_description_lists: true, - ..ComrakOptions::default() - }; - let arena = Arena::new(); let root = parse_document( &arena, src, - &options); + &COMRAK_OPTIONS); fn iter_nodes<'a, F>(node: &'a AstNode<'a>, f: &F) -> Result<(), Box> where F : Fn(&'a AstNode<'a>) -> Result<(), Box> { @@ -121,24 +131,45 @@ fn format_markdown(src: &str) -> Result> { })?; let mut output: Vec = Vec::with_capacity((src.len() as f64 * 1.2) as usize); - format_html(root, &options, &mut output).expect("can format HTML"); + format_html(root, &COMRAK_OPTIONS, &mut output).expect("can format HTML"); let output = String::from_utf8(output).expect("valid utf-8 generated HTML"); Ok(output) } -fn format_page(frontmatter: models::frontmatter::FrontMatter, chapters: &Vec, url: &str, content: &str, mut output: W) -> Result<(), Box> { - use askama::Template; - #[derive(Template)] - #[template(path = "page.html")] - struct PageTemplate<'a, 'b, 'c, 'd, 'e, 'f> { - title: &'a str, - content: &'b str, - url: &'f str, - chapters: &'c Vec, - prev_chapter: Option<&'d models::chapter::Chapter>, - next_chapter: Option<&'e models::chapter::Chapter>, - } +#[derive(Template)] +#[template(path = "index.html")] +struct IndexTemplate<'a, 'b> { + book: &'a Book, + chapters: &'b Vec, +} + +fn generate_index(book: &Book, chapters: &Vec, mut output: W) -> Result<(), Box> { + // fill out our template + let template = IndexTemplate { + book, + chapters, + }; + + // and render! + let s = template.render()?; + output.write_all(s.as_bytes())?; + + Ok(()) +} + +#[derive(Template)] +#[template(path = "page.html")] +struct PageTemplate<'a, 'b, 'c, 'd, 'e, 'f, 'g> { + title: &'a str, + content: &'b str, + url: &'f str, + chapters: &'c Vec, + prev_chapter: Option<&'d models::chapter::Chapter>, + next_chapter: Option<&'e models::chapter::Chapter>, + book: &'g Book, +} +fn format_page(book: &Book, frontmatter: FrontMatter, chapters: &Vec, url: &str, content: &str, mut output: W) -> Result<(), Box> { let this_index = chapters.iter().enumerate().find(|(_, chap)| chap.url == url).map(|(i, _)| i).expect("chapter exists"); let prev_chapter = if this_index > 0 { Some(chapters.iter().nth(this_index - 1).expect("chapter n-1 exists")) @@ -161,6 +192,7 @@ fn format_page(frontmatter: models::frontmatter::FrontMatter, chap chapters, prev_chapter, next_chapter, + book, }; // and render! @@ -184,6 +216,19 @@ fn main() -> Result<(), Box> { let dest = PathBuf::from(dest); std::fs::create_dir_all(&dest)?; + // load our book + let book_toml_path = src.join("mkbook.toml"); + let parsed_book: Option = if book_toml_path.exists() { + let contents = fs::read_to_string(&book_toml_path)?; + let contents: ParsedBook = toml::from_str(&contents)?; + Some(contents) + } + else { + None + }; + let parsed_book = parsed_book.unwrap_or_default(); + let book: Book = parsed_book.into(); + // load all our chapters let mut chapters: Vec = Vec::default(); for entry in src.read_dir()? { @@ -205,6 +250,13 @@ fn main() -> Result<(), Box> { } chapters.sort_by(|a, b| a.url.cmp(&b.url)); + // generate our index + let index_out_path = dest.join("index.html"); + let index_out = fs::File::create(&index_out_path)?; + let index_out = io::BufWriter::new(index_out); + generate_index(&book, &chapters, index_out)?; + println!("Rendered index into `{}`", index_out_path.display()); + // compile markdown for entry in src.read_dir()? { let entry = entry?; @@ -222,7 +274,7 @@ fn main() -> Result<(), Box> { let (front, contents) = extract_frontmatter(&contents)?; let front = front.unwrap_or_default().into_front(name); let contents = format_markdown(&contents)?; - format_page(front, &chapters, &format!("{}.html", name), &contents, outfile)?; + format_page(&book, front, &chapters, &format!("{}.html", name), &contents, outfile)?; println!("Rendered `{}` into `{}`", path.display(), out.display()); } diff --git a/src/models.rs b/src/models.rs index 0dd4194..e508183 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,2 +1,3 @@ +pub mod book; pub mod chapter; pub mod frontmatter; diff --git a/src/models/book.rs b/src/models/book.rs new file mode 100644 index 0000000..f3cee3f --- /dev/null +++ b/src/models/book.rs @@ -0,0 +1,46 @@ +use serde::Deserialize; +use chrono::prelude::*; + +#[derive(Deserialize, Default)] +pub struct ParsedBook { + pub title: Option, + pub author: Option, + pub pubdate: Option, + pub url: Option, + pub description: Option, +} + +pub struct Book { + pub title: String, + pub author: String, + pub pubdate: DateTime, + pub url: String, + pub description: String, +} + +impl From for Book { + fn from(pb: ParsedBook) -> Book { + Book { + title: match pb.title { + Some(title) => title.clone(), + None => "My Cool Book".to_owned(), + }, + author: match pb.author { + Some(author) => author.clone(), + None => "Anonymous".to_owned(), + }, + pubdate: match pb.pubdate { + Some(pubdate) => DateTime::from(DateTime::parse_from_rfc3339(&pubdate.to_string()).expect("valid rfc3339 datetime")), + None => Utc::now(), + }, + url: match pb.url { + Some(url) => url.clone(), + None => "".to_owned(), + }, + description: match pb.description { + Some(description) => super::super::format_markdown(&description).expect("book description is valid markdown"), + None => "".to_owned(), + }, + } + } +} diff --git a/style/base.scss b/style/base.scss index b7cfc69..91daf1a 100644 --- a/style/base.scss +++ b/style/base.scss @@ -43,7 +43,7 @@ figure { } } -code { +code, kbd { margin: 0 2px; padding: 0 2px; border: 1px solid #4c566a; @@ -53,6 +53,12 @@ code { font-size: 0.75em; } +kbd { + color: #222222; + background: #eeeeee; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5); +} + pre { overflow-x: auto; font-family: $font-mono; @@ -81,3 +87,14 @@ dl { } } } + +p { + margin-top: 0; +} + +footer { + color: #444444; + p { + margin-right: 0.5em; + } +} diff --git a/style/darktheme.scss b/style/darktheme.scss index c843f4b..28a72a9 100644 --- a/style/darktheme.scss +++ b/style/darktheme.scss @@ -25,4 +25,8 @@ border-right: 1px solid #333333; } } + + footer { + color: #cccccc; + } } diff --git a/style/icons.scss b/style/icons.scss index cb9cce0..040aece 100644 --- a/style/icons.scss +++ b/style/icons.scss @@ -19,4 +19,8 @@ span.icon { .icon-arrow-right { width: 0.875em; +} + +.icon-arrow-up { + width: 0.875em; } \ No newline at end of file diff --git a/style/layout.scss b/style/layout.scss index f874f49..b5e833c 100644 --- a/style/layout.scss +++ b/style/layout.scss @@ -12,28 +12,70 @@ body { nav.big { background: #2c2c38; - padding: 1em; + padding: 0.5em 1em 1em; display: flex; flex-direction: column; - - a { - width: 100%; - font-size: 1.5em; - text-decoration: none; - font-family: $font-sansserif; - margin: 0 0 0.25em 0; - color: #5babd1; + + header { + margin: 0 0 1em; + display: flex; + flex-direction: column; + align-items: stretch; + border-bottom: 2px solid #888888; - &:hover { - color: #cf5ccd; + h1 { + font-size: 1.5em; + font-weight: 700; + font-family: $font-sansserif; + margin-bottom: 0; + text-align: left; + + color: #eeeeee; + + a { + color: #eeeeee; + text-decoration: none; + + &:hover { + color: #cf5ccd; + } + } + } + + h2 { + font-size: 1em; + font-weight: 600; + font-family: $font-sansserif; + margin-bottom: 0; + text-align: right; } + } - &.current { - color: #cf5ccd; + ol { + margin: 0 2em; + padding: 0; + li { + width: 100%; + font-size: 1.25em; + font-family: $font-sansserif; + color: #eeeeee; + a { + text-decoration: none; + margin: 0 0 0.25em 0; + color: #5babd1; + + &:hover { + color: #cf5ccd; + } + + &.current { + color: #cf5ccd; - &:hover { - color: #fefefe; + &:hover { + color: #fefefe; + } + } } } } @@ -72,17 +114,53 @@ body { height: 1em; } } + + >* { + min-width: 0; + min-height: 0; + } article { - padding: 1em 2em 0 2em; max-width: 38em; - min-width: 0; - min-height: 0; + padding: 0.5em 2em 0 2em; >* { max-width: 100%; } } + + .next-chapter { + width: 100%; + + a { + display: flex; + align-items: center; + justify-content: flex-end; + margin-right: 0.5em; + + font-size: 1.2em; + text-decoration: none; + font-family: $font-sansserif; + color: #5babd1; + + &:hover { + color: #cf5ccd; + } + + span:first-child { + margin-right: 0.5em; + } + } + } + + footer { + width: 100%; + text-align: right; + display: flex; + flex-direction: column; + align-items: flex-end; + justify-content: flex-end; + } } @media screen and (max-width: 768px) { diff --git a/style/print.scss b/style/print.scss index 2ae9f6f..8aa995e 100644 --- a/style/print.scss +++ b/style/print.scss @@ -2,6 +2,12 @@ body { background: #ffffff; color: #000000; + + &:not(.toc) { + nav { + display: none; + } + } } a { @@ -27,10 +33,6 @@ overflow: hidden; } - nav { - display: none; - } - body { display: block; } diff --git a/style/style.scss b/style/style.scss index 07e1deb..bda2090 100644 --- a/style/style.scss +++ b/style/style.scss @@ -1,6 +1,7 @@ @import 'variables'; @import 'base'; @import 'layout'; +@import 'toc'; @import 'icons'; @import 'darktheme'; @import 'print'; \ No newline at end of file diff --git a/style/toc.scss b/style/toc.scss new file mode 100644 index 0000000..17d0013 --- /dev/null +++ b/style/toc.scss @@ -0,0 +1,101 @@ +body.toc { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + + >* { + width: 36em; + } + + header { + margin-top: 1em; + display: flex; + flex-direction: column; + align-items: center; + + h1 { + font-size: 3em; + font-weight: 700; + font-family: $font-sansserif; + margin-bottom: 0; + } + + h2 { + font-size: 1em; + font-weight: 600; + font-family: $font-sansserif; + margin-bottom: 0; + } + + time { + font-size: 1em; + font-weight: 400; + font-family: $font-serif; + } + } + + nav { + background: none; + display: flex; + flex-direction: column; + align-items: stretch; + + h1 { + text-align: left; + font-size: 2.5em; + font-weight: 600; + margin-bottom: 0; + } + + ol { + margin: 0.25em 0 0; + li { + font-size: 1.5em; + font-family: $font-sansserif; + + a { + text-decoration: none; + margin: 0 0 0.25em 0; + color: #5babd1; + + &:hover { + color: #cf5ccd; + } + } + } + } + } + + footer { + flex: 1; + } +} + +@media screen and (max-width: 768px) { + body.toc { + >* { + width: 100%; + } + + header { + h1, h2, time { + text-align: center; + } + } + + article, nav { + padding: 0; + >* { + margin-left: 0.5em; + margin-right: 0.5em; + } + } + + nav { + h1 { + font-size: 1.75em; + } + } + } +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..f9fbe76 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,36 @@ + + + + + + {{ book.title }} + + + + + + + + + + + +
+

{{ book.title }}

+

by {{ book.author }}

+ +
+
+ {{ book.description|safe }} +
+ +

© {{ book.pubdate|year }} {{ book.author }}

+ + \ No newline at end of file diff --git a/templates/page.html b/templates/page.html index d419bda..d96fd0a 100644 --- a/templates/page.html +++ b/templates/page.html @@ -3,20 +3,33 @@ - {{ title }} + {{ book.title }} | {{ title }} + + + + + + + -
{{ content|safe }}
+
\ No newline at end of file