diff --git a/Cargo.lock b/Cargo.lock index ae995be..2ea43d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,46 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "askama" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "askama_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "askama_escape 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "askama_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "askama_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "askama_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "askama_escape" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "askama_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "askama_escape 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atty" version = "0.2.13" @@ -270,29 +310,6 @@ name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "handlebars" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pest 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "hashbrown" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "hermit-abi" version = "0.1.3" @@ -301,6 +318,11 @@ dependencies = [ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "humansize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "humantime" version = "1.3.0" @@ -381,9 +403,9 @@ dependencies = [ name = "mkbook" version = "0.1.0" dependencies = [ + "askama 0.8.0 (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)", - "handlebars 2.0.2 (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)", @@ -399,6 +421,14 @@ dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-traits" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num_cpus" version = "1.11.1" @@ -641,6 +671,16 @@ name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "1.0.8" @@ -696,6 +736,14 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "toml" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.5.5" @@ -835,6 +883,10 @@ dependencies = [ "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum askama 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3dc2a4b6d7f812d2b13d251ae792caecebd635d6401761162d4b71d5ebe1a010" +"checksum askama_derive 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ee2fff0f22ad5d215cace1227cd036c28e81e26206763bb837b6d0e766c87d" +"checksum askama_escape 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0de942230b5beedaa9e1d64df5b76fa1c97002e4c7982897be899cccf40621d" +"checksum askama_shared 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6dfa6b6d254fd066a8bbed9a8f913123e3f701db89216ad4f0aff04ad87718c" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" @@ -864,9 +916,8 @@ dependencies = [ "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -"checksum handlebars 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "91ef1ac30f2eaaa2b835fce73c57091cb6b9fc62b7eef285efbf980b0f20001b" -"checksum hashbrown 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1de41fb8dba9714efd92241565cdff73f78508c95697dd56787d3cba27e2353" "checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" +"checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" @@ -880,6 +931,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-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" "checksum onig_sys 69.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a8d4efbf5f59cece01f539305191485b651acb3785b9d5eef05749f0496514e" @@ -910,11 +962,13 @@ dependencies = [ "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" "checksum syn 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "661641ea2aa15845cddeb97dad000d22070bb5c1fb456b96c1cba883ec691e92" "checksum syntect 3.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "955e9da2455eea5635f7032fc3a229908e6af18c39600313866095e07db0d8b8" "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 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" "checksum typed-arena 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" diff --git a/Cargo.toml b/Cargo.toml index 315f65d..e82de1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ build = "build.rs" syntect = "3.3" comrak = "0.6" clap = "2.33" -handlebars = "2.0" +askama = "0.8" serde = { version = "1.0", features = ["derive"] } toml = "0.5" diff --git a/assets/icons.svg b/assets/icons.svg new file mode 100644 index 0000000..d50227b --- /dev/null +++ b/assets/icons.svg @@ -0,0 +1,12 @@ + diff --git a/demo/src/01-introduction.md b/demo/src/01-introduction.md new file mode 100644 index 0000000..d956632 --- /dev/null +++ b/demo/src/01-introduction.md @@ -0,0 +1,4 @@ +--- +title = "Introduction" +--- + diff --git a/demo/src/02-markdown.md b/demo/src/02-markdown.md new file mode 100644 index 0000000..f0ed0f3 --- /dev/null +++ b/demo/src/02-markdown.md @@ -0,0 +1,4 @@ +--- +title = "Markdown" +--- + diff --git a/demo/src/03-frontmatter.md b/demo/src/03-frontmatter.md new file mode 100644 index 0000000..6b13927 --- /dev/null +++ b/demo/src/03-frontmatter.md @@ -0,0 +1,28 @@ +--- +title = "Front Matter" +--- + +# 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. + +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](https://github.com/toml-lang/toml) format, so for example the front-matter (and first line) for this file looks like: + +```md +--- +title = "Front Matter" +--- + +# 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. +``` + +## Supported Keys + +The list of supported keys is subject to change, but for now it is as follows: + +title + +: A human-readable title for the document + diff --git a/demo/src/04-structure.md b/demo/src/04-structure.md new file mode 100644 index 0000000..25e4fbb --- /dev/null +++ b/demo/src/04-structure.md @@ -0,0 +1,4 @@ +--- +title = "Structure" +--- + diff --git a/demo/src/index.md b/demo/src/index.md deleted file mode 100644 index 3c0c18d..0000000 --- a/demo/src/index.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title = "Sparte plura" ---- - -# Sparte plura - -## Sine flumina - -Lorem markdownum procubuit animasse solos talia [flammis me -quia](http://www.dedit.io/fessa-nec), attollite. Tu dulcedine tanta mitescere -manebit movit qualia aequorei victa illo, ipsi collum, suum disque. Sperantemque -triumphos praecipitatur potentia mea? Vox aere vestibus coepit utque deinde, heu -infans non amor somnusque Peleus. - -1. Carminibus colle artibus ferunt fulgorem -2. Sonantia eripui -3. Divamque fixumque inter -4. Ducar mihi vertigine in spoliis -5. Fuit pendens talibus recentibus -6. Utrumque praesens adit maduere detorquet siqua - -Tibi ego proelia mihi, hic precor, nil alba sitientes decusque linquendus. -Coeptis dixi longa; *me avido*, et queat humi Achillem, ora bina. - -```c++ -webmaster(whitelist(processor, 3, deviceDos), columnMinisite * 5, 2); -if (newlineWidgetTask) { - toslink.tape = throughput; - file_touchscreen_carrier.risc(point); - checksumWais -= ringMegapixelSoftware; -} else { - solid_sdsl(standaloneEbookBasic, 2); - syntax_drive_dynamic.gif_storage(losslessInsertion); - drop_controller_net = dashboard; -} -postSocial = dac.led(streamingScrollQuicktime + logVlbLeaderboard(memory, - terminal_grep), folderMemory); -``` - -## Functo mei ore terra liquidas praesepibus sopore - -Iovis retorserunt tamen cumque **intus** equorum lacrimas inritat pluviaque -aliquas habebit videt: ignorat. Nec senex me **navigat mollit rogavi** -meditataque erat furibunda iter ales. Ciconia sibi mitra. Tamen postquam possunt -pariter contigerant **atris** expellitque odit; illam arva quae, partem medias? - -```haxe -var icio = ugc(linkedin(310547, systrayPrimary, gatewayWindows + domain)); -excelErgonomics -= userUrlKey(prebindingRefreshLaser); -if (fileCycleNumber(cmos(data / -2))) { - safeUrlMarkup(source(hard_io), checksum_youtube_file, - text_adc_myspace.eup(bankRup, -2, smb)); - dimmSafeKeystroke.ntfs += parity_thunderbolt; -} -``` - -Ceris securum: cuius Amoris feliciter longe, *de esset plura*. Saevam vis seque, -viderit tantosque feritate oriens quaesitisque aevi. Parvis quisquis qui: hausit -est, pedum in fessa tutaque: leto sub fugias *non*, Halcyoneus. Globos te umeri -struxerit Iuppiter vitae; est arator ecce, [sit -et](http://www.tingui-ausus.io/), Aeneae per digestum rudis. - -- Cedere nudae profitemur nec aurato adspice non -- Tibi artisque cornua nondum miratur ceperit -- Metuunt uva utque nondum potes super vocatus -- Velleris pecudes labens deseruitque regna -- Erant lacteus dubitati iuvenco - -Lyraeque carpere luxque quas virgineos se deus reddidit colonos adflavit. -Tepidos [superba](http://notavi-sibi.com/de-saucius.aspx), iubent omnia quereris -tumidam est petentes, lumine. - diff --git a/src/main.rs b/src/main.rs index 83134d0..ad8961b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,12 @@ use std::path::PathBuf; use std::{fs, io}; -use serde::Deserialize; pub const STYLESHEET: &'static str = include_str!(concat!(env!("OUT_DIR"), "/style.css")); -pub const TEMPLATE_PAGE: &'static str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/page.hbs")); 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")); mod cli; - -#[derive(Deserialize, Default)] -struct Metadata { - title: Option, -} +mod models; fn format_code(lang: &str, src: &str) -> Result> { use syntect::parsing::{SyntaxSet, SyntaxReference}; @@ -39,7 +34,7 @@ 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"); @@ -48,10 +43,10 @@ fn extract_metadata(src: &str) -> Result<(Option, String), Box Result<(Option, String), Box Result> { hardbreaks: false, smart: true, github_pre_lang: true, - default_info_string: Some("none".to_owned()), + default_info_string: None, unsafe_: true, ext_strikethrough: true, ext_tagfilter: false, @@ -131,26 +126,46 @@ fn format_markdown(src: &str) -> Result> { Ok(output) } -fn format_page(metadata: Option, content: &str, output: W) -> Result<(), Box> { - use handlebars::Handlebars; - - // create the handlebars registry - let mut handlebars = Handlebars::new(); - - // register the template. The template string will be verified and compiled. - handlebars.register_template_string("page", TEMPLATE_PAGE).expect("page is valid template"); +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>, + } - // generate our context - use std::collections::BTreeMap; - let mut data = BTreeMap::new(); - data.insert("content", content); + 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")) + } + else { + None + }; + let next_chapter = if this_index < chapters.len() - 1 { + Some(chapters.iter().nth(this_index + 1).expect("chapter n+1 exists")) + } + else { + None + }; - let metadata = metadata.unwrap_or_default(); - let title = metadata.title.unwrap_or_default(); - data.insert("title", &title); + // fill out our template + let template = PageTemplate { + title: &frontmatter.title, + content, + url, + chapters, + prev_chapter, + next_chapter, + }; - // and render - handlebars.render_to_write("page", &data, output)?; + // and render! + let s = template.render()?; + output.write_all(s.as_bytes())?; Ok(()) } @@ -173,6 +188,27 @@ fn main() -> Result<(), Box> { let dest = prefix.join("book"); std::fs::create_dir_all(&dest)?; + // load all our chapters + let mut chapters: Vec = Vec::default(); + for entry in src.read_dir()? { + let entry = entry?; + let path = entry.path(); + if let Some("md") = path.extension().map(std::ffi::OsStr::to_str).flatten() { + let name = path.file_stem().map(std::ffi::OsStr::to_str).flatten(); + if name.is_none() { continue; } + let name = name.unwrap(); + + let contents = fs::read_to_string(&path)?; + let (front, _) = extract_frontmatter(&contents)?; + let front = front.unwrap_or_default().into_front(name); + chapters.push(models::chapter::Chapter { + url: format!("/{}.html", name), + title: front.title, + }); + } + } + chapters.sort_by(|a, b| a.url.cmp(&b.url)); + // compile markdown for entry in src.read_dir()? { let entry = entry?; @@ -187,9 +223,10 @@ fn main() -> Result<(), Box> { let outfile = io::BufWriter::new(outfile); let contents = fs::read_to_string(&path)?; - let (meta, contents) = extract_metadata(&contents)?; + let (front, contents) = extract_frontmatter(&contents)?; + let front = front.unwrap_or_default().into_front(name); let contents = format_markdown(&contents)?; - format_page(meta, &contents, outfile)?; + format_page(front, &chapters, &format!("/{}.html", name), &contents, outfile)?; println!("Rendered `{}` into `{}`", path.display(), out.display()); } @@ -200,6 +237,8 @@ fn main() -> Result<(), Box> { println!("Wrote {}", dest.join("style.css").display()); fs::write(dest.join("favicon.ico"), ASSET_FAVICON)?; println!("Wrote {}", dest.join("favicon.ico").display()); + fs::write(dest.join("icons.svg"), ASSET_ICONS)?; + println!("Wrote {}", dest.join("icons.svg").display()); println!("Done!"); Ok(()) diff --git a/src/models.rs b/src/models.rs new file mode 100644 index 0000000..0dd4194 --- /dev/null +++ b/src/models.rs @@ -0,0 +1,2 @@ +pub mod chapter; +pub mod frontmatter; diff --git a/src/models/chapter.rs b/src/models/chapter.rs new file mode 100644 index 0000000..4991c62 --- /dev/null +++ b/src/models/chapter.rs @@ -0,0 +1,4 @@ +pub struct Chapter { + pub url: String, + pub title: String, +} diff --git a/src/models/frontmatter.rs b/src/models/frontmatter.rs new file mode 100644 index 0000000..0655310 --- /dev/null +++ b/src/models/frontmatter.rs @@ -0,0 +1,21 @@ +use serde::Deserialize; + +#[derive(Deserialize, Default)] +pub struct ParsedFrontMatter { + pub title: Option, +} + +pub struct FrontMatter { + pub title: String, +} + +impl ParsedFrontMatter { + pub fn into_front(&self, file_name: &str) -> FrontMatter { + FrontMatter { + title: match &self.title { + Some(title) => title.clone(), + None => file_name.to_owned(), + }, + } + } +} diff --git a/style/base.scss b/style/base.scss index c08d73a..213f98c 100644 --- a/style/base.scss +++ b/style/base.scss @@ -1,19 +1,19 @@ body { - margin: 2em auto; - max-width: 36em; - line-height: 1.6; + margin: 0; + line-height: 1.5; font-size: 14pt; color: #222222; background: #eeeeee; - padding: 0 0.5em; - font-family: "Georgia", Georgia, "Times New Roman", Times, serif; + padding: 0; + font-family: $font-serif; } h1, h2, h3 { + margin-top: 0; line-height: 1.2; - font-family: "Franklin Gothic Medium", "Arial Narrow", Arial, sans-serif; + font-family: $font-sansserif; } a { @@ -45,14 +45,39 @@ figure { code { margin: 0 2px; - padding: 0 5px; + padding: 0 2px; border: 1px solid #4c566a; border-radius: 3px; word-break: break-all; + font-family: $font-mono; + font-size: 0.75em; } pre { overflow-x: auto; - font-family: "Courier New", Courier, monospace; - padding: 0.25em 0.5em; + font-family: $font-mono; + padding: 0; +} + +dl { + display: grid; + grid-template-columns: auto 1fr; + + dt { + font-weight: 700; + margin: 0; + padding: 0.5em; + border-right: 1px solid #dddddd; + } + + dd { + margin: 0; + padding: 0.5em; + } + + dt, dd { + p { + margin: 0; + } + } } diff --git a/style/darktheme.scss b/style/darktheme.scss index eefcfb9..b38461f 100644 --- a/style/darktheme.scss +++ b/style/darktheme.scss @@ -12,6 +12,10 @@ } } + nav { + background: #18181d; + } + img { filter: grayscale(30%); } diff --git a/style/icons.scss b/style/icons.scss new file mode 100644 index 0000000..cb9cce0 --- /dev/null +++ b/style/icons.scss @@ -0,0 +1,22 @@ +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; +} \ No newline at end of file diff --git a/style/layout.scss b/style/layout.scss new file mode 100644 index 0000000..f874f49 --- /dev/null +++ b/style/layout.scss @@ -0,0 +1,105 @@ +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; + + nav.big { + background: #2c2c38; + padding: 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; + + &:hover { + color: #cf5ccd; + } + + &.current { + color: #cf5ccd; + + &:hover { + color: #fefefe; + } + } + } + } + + nav.small { + display: none; + width: 100%; + align-items: center; + justify-content: space-between; + background: #2c2c38; + padding: 0; + + >* { + margin: 0.5em; + } + + a { + text-decoration: none; + font-family: $font-sansserif; + color: #5babd1; + + &:hover { + color: #cf5ccd; + } + } + + span.title { + text-decoration: none; + font-family: $font-sansserif; + color: #fefefe; + } + + span.placeholder { + width: 1em; + height: 1em; + } + } + + article { + padding: 1em 2em 0 2em; + max-width: 38em; + min-width: 0; + min-height: 0; + + >* { + max-width: 100%; + } + } +} + +@media screen and (max-width: 768px) { + body { + grid-template-columns: 1fr; + grid-template-rows: auto 1fr; + + nav.big { + display: none; + } + + nav.small { + display: flex; + } + + article { + padding: 1em 0.5em 0 0.5em; + } + } +} diff --git a/style/print.scss b/style/print.scss index 2dac89e..2ae9f6f 100644 --- a/style/print.scss +++ b/style/print.scss @@ -26,4 +26,12 @@ * { overflow: hidden; } + + nav { + display: none; + } + + body { + display: block; + } } diff --git a/style/style.scss b/style/style.scss index 039edb4..07e1deb 100644 --- a/style/style.scss +++ b/style/style.scss @@ -1,3 +1,6 @@ +@import 'variables'; @import 'base'; +@import 'layout'; +@import 'icons'; @import 'darktheme'; @import 'print'; \ No newline at end of file diff --git a/style/variables.scss b/style/variables.scss new file mode 100644 index 0000000..0a99ba5 --- /dev/null +++ b/style/variables.scss @@ -0,0 +1,5 @@ +@import url('https://fonts.googleapis.com/css?family=Crimson+Pro|Poppins:700|Source+Code+Pro&display=swap'); + +$font-serif: 'Crimson Pro', "Georgia", Georgia, "Times New Roman", Times, serif !default; +$font-sansserif: 'Poppins', "Franklin Gothic Medium", "Arial Narrow", Arial, sans-serif !default; +$font-mono: 'Source Code Pro', "Courier New", Courier, monospace !default; diff --git a/templates/page.hbs b/templates/page.hbs deleted file mode 100644 index a2f759c..0000000 --- a/templates/page.hbs +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - {{ title }} - - - - - {{{content}}} - - \ No newline at end of file diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 0000000..19af53e --- /dev/null +++ b/templates/page.html @@ -0,0 +1,45 @@ + + + + + + {{ title }} + + + + + + +
{{ content|safe }}
+ + \ No newline at end of file