From 7249af10b8bc0b768fd153e1a4798451b949be15 Mon Sep 17 00:00:00 2001 From: Teascade Date: Mon, 16 Apr 2018 14:10:05 +0300 Subject: [PATCH] Add very basic site generator --- .gitignore | 1 + Cargo.lock | 333 ++++++++++++++++++ Cargo.toml | 12 + config.toml | 10 + content.md | 2 + public/css/default.css | 141 ++++++++ public/test.html | 50 +++ src/config.rs | 51 +++ src/config_toml.rs | 94 +++++ src/main.rs | 54 +++ src/renderer.rs | 29 ++ src/template.rs | 105 ++++++ src/templates/default-css.css | 141 ++++++++ .../navbar/navbar-image-item-template.html | 6 + .../navbar/navbar-item-template.html | 3 + src/templates/page-template.html | 48 +++ src/templates/test.png | Bin 0 -> 15869 bytes test_page.toml | 7 + 18 files changed, 1087 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 config.toml create mode 100644 content.md create mode 100644 public/css/default.css create mode 100644 public/test.html create mode 100644 src/config.rs create mode 100644 src/config_toml.rs create mode 100644 src/main.rs create mode 100644 src/renderer.rs create mode 100644 src/template.rs create mode 100644 src/templates/default-css.css create mode 100644 src/templates/navbar/navbar-image-item-template.html create mode 100644 src/templates/navbar/navbar-item-template.html create mode 100644 src/templates/page-template.html create mode 100644 src/templates/test.png create mode 100644 test_page.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6f89c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target/ \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..85ee2f0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,333 @@ +[[package]] +name = "aho-corasick" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getopts" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazy_static" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pulldown-cmark" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive_internals" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "structopt" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.1 (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 = "teascade-generator" +version = "0.1.0" +dependencies = [ + "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "thread_local" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "toml" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ucd-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "utf8-ranges" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" +"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum getopts 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "b900c08c1939860ce8b54dc6a89e26e00c04c380fd0e09796799bd7f12861e05" +"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" +"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" +"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118" +"checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" +"checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a" +"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" +"checksum regex-syntax 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bd90079345f4a4c3409214734ae220fd773c6f2e8a543d07370c6c1c369cfbfb" +"checksum serde 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "4c36359ac1a823e00db02a243376ced650f088dc1f6259bbf828e4668e3c7399" +"checksum serde_derive 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "f0477feff739386f5bca8e13fa43d96a4e834904d538f503906c8179f9205f50" +"checksum serde_derive_internals 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d30c4596450fd7bbda79ef15559683f9a79ac0193ea819db90000d7e1cae794" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum structopt 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbf9b178b64479997d9515aa4a6956ada20a9829fa03fee3bbcff5962e4e405" +"checksum structopt-derive 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "29040b33bfc5dae3a321f79cbcd86813a5f024e3040a31f057188e7f2b6228ba" +"checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" +"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" +"checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" +"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" +"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" +"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..550ba70 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "teascade-generator" +version = "0.1.0" +authors = ["Teascade "] + +[dependencies] +pulldown-cmark = "*" +toml = "*" +structopt = "*" +serde = "*" +serde_derive = "*" +regex = "*" diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..19052d5 --- /dev/null +++ b/config.toml @@ -0,0 +1,10 @@ +[website] +website_name = "Test Website Name!" +built_pages = ["test_page.toml"] +use_default_css = true +javascript = [] +css = [] +#favicon = "path.png" +#twitter_author = "@teascade" +#google_robots = "all" +#google_site_verification = "" \ No newline at end of file diff --git a/content.md b/content.md new file mode 100644 index 0000000..2e85b51 --- /dev/null +++ b/content.md @@ -0,0 +1,2 @@ +# Test Header! +Some test content! \ No newline at end of file diff --git a/public/css/default.css b/public/css/default.css new file mode 100644 index 0000000..ce5581f --- /dev/null +++ b/public/css/default.css @@ -0,0 +1,141 @@ +:root { + --color-main-bg: rgb(38, 40, 43); + + --color-navbar-bg: rgb(52, 52, 56); + --color-navbar-button: rgb(50, 50, 53); + --color-navbar-button-hover: rgb(43, 43, 46); + --color-navbar-button-focus: rgb(44, 44, 48); + --color-navbar-border-bottom: rgb(41, 41, 44); + + --color-content-bg: rgb(44, 44, 48); + --color-content-fg: rgb(164, 161, 172); + + --navbar-height: 3.5vw; + --navbar-width: 60%; + --navbar-padding-size: 0.5em; + --navbar-text-size: 24px; + + --content-width: 50%; + --content-text-size: 1.2em; + --content-top-padding: 2em; + --content-side-padding: 2em; + + --highlight-color: rgb(230, 134, 217); +} + +body, html { + width: 100%; + height: 100%; + padding: 0; + margin: 0; + font-family: Roboto, Helvetica, sans-serif; + background-color: var(--color-main-bg); +} + +#text { + width: 100%; + margin: 0; + padding: 0; +} + +/* Navbar styles */ + +nav { + width: 100%; + height: var(--navbar-height); + background-color: var(--color-navbar-bg); + border-bottom: solid 4px var(--color-navbar-border-bottom); +} + +nav ul { + height: 100%; + font-size: 0; + margin: 0; + padding: 0; + list-style-type: none; + width: var(--navbar-width); + margin-left: auto; + margin-right: auto; +} + +nav li { + height: 100%; + font-size: var(--navbar-text-size); + display: inline-block; + margin: 0; +} + +nav li a { + display: block; + height: 100%; + background-color: var(--color-navbar-button); + color: var(--highlight-color); + text-decoration: none; + padding-left: var(--navbar-padding-size); + padding-right: var(--navbar-padding-size); + transition: 0.15s; + border-left: solid 3px var(--color-navbar-button-hover); + position: relative; +} +nav li:not(.image) a { + display: flex; + justify-content: center; + flex-direction: column; +} + +nav li:first-child a { + border-left: solid 5px var(--color-navbar-button-hover); +} + +nav li:last-child a { + border-right: solid 5px var(--color-navbar-button-hover); +} + +nav ul a:hover { + background-color: var(--color-navbar-button-hover); +} + +nav ul a:focus { + outline: none; + background-color: var(--color-navbar-button-focus); +} + +/* Navbar images */ + +nav img { + height: var(--navbar-height); + vertical-align: top; + border: 50%; +} + +nav li.image { + position: relative; + bottom: calc(var(--navbar-height) / 4); + padding: 0; +} + +nav li.image a { + height: auto; +} + +/* Article styles */ + +article { + width: var(--content-width); + min-height: calc(100% - var(--navbar-height) - var(--content-top-padding) - 4px); + margin: auto; + padding-top: var(--content-top-padding); + padding-left: var(--content-side-padding); + padding-right: var(--content-side-padding); + background-color: var(--color-content-bg); + color: var(--color-content-fg); + font-size: var(--content-text-size); +} + +article > * { + margin: 0; +} + +article a { + color: var(--highlight-color); +} \ No newline at end of file diff --git a/public/test.html b/public/test.html new file mode 100644 index 0000000..ee72fff --- /dev/null +++ b/public/test.html @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test Page + + + + + + +
+

Test Header!

+

Some test content!

+ +
+ + + \ No newline at end of file diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..864dfc8 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,51 @@ +use config_toml::{GlobalConfigToml, PageConfigToml}; +use std::io::Error; +use std::path::PathBuf; + +#[derive(Clone)] +pub struct SinglePageConfigs { + pub global_config: GlobalConfigToml, + pub page_config: PageConfigToml, + pub page_config_path: PathBuf, +} + +#[derive(Debug)] +pub struct Config { + pub global_config: GlobalConfigToml, + pub page_config_paths: Vec, + pub page_configs: Vec, +} + +impl Config { + pub fn new() -> Result { + let global_config = GlobalConfigToml::get_config()?; + let mut page_configs = Vec::new(); + let mut page_config_paths = Vec::new(); + for page_config in global_config.clone().website.built_pages { + let path = PathBuf::from(page_config); + let config = PageConfigToml::get_from(&path)?; + page_configs.push(config); + page_config_paths.push(path); + } + Ok(Config { + global_config, + page_config_paths, + page_configs, + }) + } + + pub fn get_configs_for(&self, idx: u32) -> Result { + if let (Some(page_config), Some(page_path)) = ( + self.page_configs.get(idx as usize), + self.page_config_paths.get(idx as usize), + ) { + Ok(SinglePageConfigs { + global_config: self.global_config.clone(), + page_config: page_config.clone(), + page_config_path: page_path.clone(), + }) + } else { + Err(format!("Failed to get page config, idx: {}", idx)) + } + } +} diff --git a/src/config_toml.rs b/src/config_toml.rs new file mode 100644 index 0000000..4544977 --- /dev/null +++ b/src/config_toml.rs @@ -0,0 +1,94 @@ +use toml; + +use std::path::PathBuf; +use std::fs::File; +use std::io::{Error, ErrorKind, Read}; + +#[derive(Deserialize, Clone, Debug)] +pub struct GlobalConfigToml { + pub website: WebsiteConfig, +} + +#[derive(Deserialize, Clone, Debug)] +pub struct WebsiteConfig { + pub website_name: String, + pub built_pages: Vec, + pub use_default_css: bool, + pub javascript: Vec, + pub css: Vec, + pub favicon: Option, + pub twitter_author: Option, + pub google_robots: Option, + pub google_site_verification: Option, +} + +impl GlobalConfigToml { + pub fn get_config() -> Result { + let mut file = File::open("config.toml")?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + match toml::from_str(&contents) { + Ok(config) => Ok(config), + Err(err) => { + if let Some((line, col)) = err.line_col() { + Err(Error::new( + ErrorKind::Other, + format!("Erronous config.toml at {}:{}", line, col), + )) + } else { + Err(Error::new( + ErrorKind::Other, + format!("Failed to parse config.toml correctly. Check variable names!"), + )) + } + } + } + } +} + +#[derive(Deserialize, Clone, Debug)] +pub struct PageConfigToml { + pub page: PageConfig, +} + +impl PageConfigToml { + pub fn get_from(path: &PathBuf) -> Result { + let path_str = match path.to_str() { + Some(path_str) => path_str, + None => "", + }; + + let mut file = File::open(path.as_path())?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + match toml::from_str(&contents) { + Ok(config) => Ok(config), + Err(err) => { + if let Some((line, col)) = err.line_col() { + Err(Error::new( + ErrorKind::Other, + format!("Erronous toml: {} at {}:{}", path_str, line, col), + )) + } else { + Err(Error::new( + ErrorKind::Other, + format!( + "Failed to parse toml correctly: {}. Check variable names!", + path_str + ), + )) + } + } + } + } +} + +#[derive(Deserialize, Clone, Debug)] +pub struct PageConfig { + pub title: String, + pub description: String, + pub content_path: String, + pub favicon: Option, + pub javascript: Option>, + pub css: Option>, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d26af7c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,54 @@ +extern crate pulldown_cmark; +extern crate regex; +#[macro_use] +extern crate serde_derive; +extern crate toml; + +mod config_toml; +mod config; +mod template; +mod renderer; + +use config::Config; +use template::Template; + +use std::error::Error; +use std::fs::{create_dir_all, File}; +use std::io::prelude::*; + +const DEFAULT_CSS: &'static str = include_str!("templates/default-css.css"); +const PAGE_TEMPLATE: &'static str = include_str!("templates/page-template.html"); + +fn main() { + println!("Fetching configs"); + let config = match Config::new() { + Ok(config) => config, + Err(err) => panic!(err.description().to_owned()), + }; + + if config.global_config.website.use_default_css { + println!("Adding public/css/default.css"); + create_dir_all("public/css").ok(); + let mut file = File::create("public/css/default.css").unwrap(); + file.write_all(DEFAULT_CSS.as_bytes()).ok(); + } + + println!("Generating page template"); + let template = Template::new(PAGE_TEMPLATE); + + println!("Rendering public/test.html"); + let rendered = match config.get_configs_for(0) { + Ok(page_config) => template.render(&Template::page_data_from( + page_config.clone(), + renderer::render_markdown_content(&page_config).unwrap(), + )), + Err(err) => panic!(err), + }; + + println!("Writing public/test.html"); + create_dir_all("public").ok(); + let mut file = File::create("public/test.html").unwrap(); + file.write_all(rendered.as_bytes()).ok(); + + println!("Done!"); +} diff --git a/src/renderer.rs b/src/renderer.rs new file mode 100644 index 0000000..dedf957 --- /dev/null +++ b/src/renderer.rs @@ -0,0 +1,29 @@ +use std::fs::File; +use std::io::Read; + +use pulldown_cmark::{html, Parser}; + +use config::SinglePageConfigs; + +pub fn render_markdown_content(config: &SinglePageConfigs) -> Result { + if let Some(parent) = config.page_config_path.parent() { + let path = parent.with_file_name(&config.page_config.page.content_path); + match File::open(&path) { + Ok(mut file) => { + let mut content = String::new(); + file.read_to_string(&mut content).ok(); + let parser = Parser::new(&content); + + let mut markdown = String::new(); + html::push_html(&mut markdown, parser); + Ok(markdown) + } + Err(_) => Err(format!("Failed to open file: {}", path.to_str().unwrap())), + } + } else { + Err(format!( + "Failed to get page config parent: {}", + config.page_config_path.to_str().unwrap() + )) + } +} diff --git a/src/template.rs b/src/template.rs new file mode 100644 index 0000000..e84ad0f --- /dev/null +++ b/src/template.rs @@ -0,0 +1,105 @@ +use regex::{Captures, Regex}; + +use std::collections::HashMap; + +use config::SinglePageConfigs; + +type Data = HashMap; + +macro_rules! hashmap { + ( $( $x:expr => $y:expr ),* ) => { + { + let mut temp_map = HashMap::new(); + $( + temp_map.insert($x, $y); + )* + temp_map + } + }; +} + +pub struct Template { + template: String, + regex: Regex, + if_regex: Regex, +} + +impl Template { + pub fn new>(template: T) -> Template { + Template { + template: template.into(), + regex: Regex::new(r"\{\{(?P\w+)\}\}").unwrap(), + if_regex: Regex::new( + r"\{\{if (?P\w+)\}\}(?P(?:.|\n)*?)\{\{endif\}\}", + ).unwrap(), + } + } + + pub fn render(&self, data: &Data) -> String { + let template_clone = self.template.clone(); + + let result = (&*self.if_regex + .replace_all(&template_clone, |caps: &Captures| { + if let Some(value) = data.get(&caps["boolean"]) { + if value == "true" { + caps["rendered"].to_owned() + } else { + String::new() + } + } else { + String::new() + } + })).to_owned(); + + let result = (&*self.regex.replace_all(&result, |caps: &Captures| { + if let Some(text) = data.get(&caps["to_replace"]) { + format!("{}", text) + } else { + String::new() + } + })).to_owned(); + + result + } + + pub fn page_data_from(config: SinglePageConfigs, content: String) -> Data { + let favicon = config.page_config.page.favicon.unwrap_or( + config + .global_config + .website + .favicon + .unwrap_or(String::new()), + ); + let google_robots = config + .global_config + .website + .google_robots + .unwrap_or(String::new()); + let google_verification = config + .global_config + .website + .google_site_verification + .unwrap_or(String::new()); + let twitter_author = config + .global_config + .website + .twitter_author + .unwrap_or(String::new()); + + let map = hashmap!( + "website_name".to_owned() => config.global_config.website.website_name, + "page_title".to_owned() => config.page_config.page.title, + "favicon".to_owned() => favicon.to_owned(), + "google_robots".to_owned() => google_robots, + "google_verification".to_owned() => google_verification, + "twitter_author".to_owned() => twitter_author, + + "use_default_css".to_owned() => config.global_config.website.use_default_css.to_string(), + "navbar".to_owned() => "true".to_owned(), + + "content".to_owned() => content + ); + + map + } +} diff --git a/src/templates/default-css.css b/src/templates/default-css.css new file mode 100644 index 0000000..ce5581f --- /dev/null +++ b/src/templates/default-css.css @@ -0,0 +1,141 @@ +:root { + --color-main-bg: rgb(38, 40, 43); + + --color-navbar-bg: rgb(52, 52, 56); + --color-navbar-button: rgb(50, 50, 53); + --color-navbar-button-hover: rgb(43, 43, 46); + --color-navbar-button-focus: rgb(44, 44, 48); + --color-navbar-border-bottom: rgb(41, 41, 44); + + --color-content-bg: rgb(44, 44, 48); + --color-content-fg: rgb(164, 161, 172); + + --navbar-height: 3.5vw; + --navbar-width: 60%; + --navbar-padding-size: 0.5em; + --navbar-text-size: 24px; + + --content-width: 50%; + --content-text-size: 1.2em; + --content-top-padding: 2em; + --content-side-padding: 2em; + + --highlight-color: rgb(230, 134, 217); +} + +body, html { + width: 100%; + height: 100%; + padding: 0; + margin: 0; + font-family: Roboto, Helvetica, sans-serif; + background-color: var(--color-main-bg); +} + +#text { + width: 100%; + margin: 0; + padding: 0; +} + +/* Navbar styles */ + +nav { + width: 100%; + height: var(--navbar-height); + background-color: var(--color-navbar-bg); + border-bottom: solid 4px var(--color-navbar-border-bottom); +} + +nav ul { + height: 100%; + font-size: 0; + margin: 0; + padding: 0; + list-style-type: none; + width: var(--navbar-width); + margin-left: auto; + margin-right: auto; +} + +nav li { + height: 100%; + font-size: var(--navbar-text-size); + display: inline-block; + margin: 0; +} + +nav li a { + display: block; + height: 100%; + background-color: var(--color-navbar-button); + color: var(--highlight-color); + text-decoration: none; + padding-left: var(--navbar-padding-size); + padding-right: var(--navbar-padding-size); + transition: 0.15s; + border-left: solid 3px var(--color-navbar-button-hover); + position: relative; +} +nav li:not(.image) a { + display: flex; + justify-content: center; + flex-direction: column; +} + +nav li:first-child a { + border-left: solid 5px var(--color-navbar-button-hover); +} + +nav li:last-child a { + border-right: solid 5px var(--color-navbar-button-hover); +} + +nav ul a:hover { + background-color: var(--color-navbar-button-hover); +} + +nav ul a:focus { + outline: none; + background-color: var(--color-navbar-button-focus); +} + +/* Navbar images */ + +nav img { + height: var(--navbar-height); + vertical-align: top; + border: 50%; +} + +nav li.image { + position: relative; + bottom: calc(var(--navbar-height) / 4); + padding: 0; +} + +nav li.image a { + height: auto; +} + +/* Article styles */ + +article { + width: var(--content-width); + min-height: calc(100% - var(--navbar-height) - var(--content-top-padding) - 4px); + margin: auto; + padding-top: var(--content-top-padding); + padding-left: var(--content-side-padding); + padding-right: var(--content-side-padding); + background-color: var(--color-content-bg); + color: var(--color-content-fg); + font-size: var(--content-text-size); +} + +article > * { + margin: 0; +} + +article a { + color: var(--highlight-color); +} \ No newline at end of file diff --git a/src/templates/navbar/navbar-image-item-template.html b/src/templates/navbar/navbar-image-item-template.html new file mode 100644 index 0000000..71ed53a --- /dev/null +++ b/src/templates/navbar/navbar-image-item-template.html @@ -0,0 +1,6 @@ +
  • + + {{image-alt}} + + +
  • \ No newline at end of file diff --git a/src/templates/navbar/navbar-item-template.html b/src/templates/navbar/navbar-item-template.html new file mode 100644 index 0000000..ff5b06d --- /dev/null +++ b/src/templates/navbar/navbar-item-template.html @@ -0,0 +1,3 @@ +
  • + {{name}} +
  • \ No newline at end of file diff --git a/src/templates/page-template.html b/src/templates/page-template.html new file mode 100644 index 0000000..408e2f5 --- /dev/null +++ b/src/templates/page-template.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + {{if use_default_css}} + {{endif}} {{css_links}}{{javascript_links}} + + + + {{page_title}} + + + + {{if navbar}} + + {{endif}} +
    + {{content}} +
    + + + \ No newline at end of file diff --git a/src/templates/test.png b/src/templates/test.png new file mode 100644 index 0000000000000000000000000000000000000000..e263d2b1654215dea27c9e711541390851a2d4d4 GIT binary patch literal 15869 zcmeIZi9b~D8$Nz!?7OIBSGKeud$OjoD@!QZNsE2ojZ{*R?6Q`nWDD812_?1p4=6BEM`}-q)ukY*U)x5@J&U4Orp8L7)>$>jiMBOmZWTNMyM-YTbTkDDuf}r77 zG(t-aZ=3$V_Ti1v>$0{9E&K%2Iy{5VbnaTWyb$E5D*1)#6;pf-|H5 zT*qT|;dk%X25+aBbN)*YdQ%2r|i`euwm2zWF*_>t7_WiW*jZ!V{D77Hw1?EL~ zMYwdhHU**Ru3Z$?2zL`FYp4B4NSi*KJ)9pU!CoKnO62cP<_0PPTS{C4Nm@JZ*HsmF z-m*&@v{Chcceeiu{1L?a*f`jc%v!roBHBh$FDaU6f@u^K|EA^ZrM7T7rGNSUT98xv zE@NvXi_Dks;a^vgb#rEVI?v+L zQou&H{H-cyP8eRfG4I%}H8!J7gyk*|I|g4*b0U6GKu9cR4b@E5G<^63j%eDdYbAUP{6DfvQLTsYC>~T8})LHpyBE&u1ebL4cMKEOqCM8Q|Wi@<%utw z3j+nk#d+`Ep)s!oH9dPS&PzHENme-zdkS0_ayo~nvN`!lZ~-Oyj83g4geoN!Zm~es zKw*l~DtP^B6LG6FKAEGZOV7%R?|r%_y^D*B^JEk6Gp3t7UOz6R2mk9xNJ!}W_3PK( zc|lAmYUz7yCdQrEeXlu-LNrg2R-1k?hyU>O#(eL*2EXDx83AukPtO!N7p{PS0O#TI zS?q(yc@!K z+-%q8Ltg{stzRaQ7X&olm0a8nYrE3-4<&Is{ z#ba)onlu(w&bF;jnIJ}P+`M`9=FQ*X`oZJ~kh#xhoUb>PVjnY=?$tB(U695}D|F=Z z{X70sPheZubZuxvGS4zt@1l1mf+Qg54zhY^_$YfQZ&M3ngd|b4?`zl*eI)l+=Qm0L zD;A`U4bvO4>P4|nSX_KC*PGQFC#>sF-paL}!spK))@No0E*AVqzZmZpx{((ApJ1e1 z;^OoRiTs+Hl+MmhaSar^h#v0w*%F)8vD%EZEJ^}Wa~R9~<_a_F@j&W2R|=w4;4@?! zdBNd(tV!f+6LmJs%|g?yC27C4iEibcvDVpS>z-Wg_+7%hYHx<$M8~DIhJm#gs@Q|I z=H4vgrl`vP&sUy%v&j|iQ!Sxk-$H9^Ywvh<%hE7WA?ZP@waUjX?aSkKQT&n}dPqw_ z?_wByAw6V!xa0X%BZ+S@ylH)n%dK;tTA63?tt@L5`tx>*jSGMO+H5Zm-w9f&)XP*# zT??MS^dn8a)GK*EJ4N1&kHb3nLe}07r=c1Tnk!n8 zQ94a`;*#$=wR$(s&oU zOYixp^eCFBidKnl&!0a}STt*%?$?!Aap{`0V&ar~tSiy@xxIO5qgOS{Fz1?2=ovrj zmv5cWtSas7^J}wh?AP^-PRH#_dH(m^Br?`plgH!Am|R9A>TdU&uTi3z->--54>UcX zTt*YaaGrLFTO~9^+9K4*!I6o(xKNxkZs1^I@@vZzhQ05aDlN~Co!wtAbBFYH!DG5D zf}V1AeqP%C+soB%60<^yHH{Vu@>5h$kQ)o;DD!X%(zb-cn4>>IT`XzZf6#<~BH=x$ z=hS=}&*pyKT?Vgg!^i5UCxAJPS8``AOWg~))NF2L)pM1@vJDC|I?TqvfSJS6+c5bz za**NGbCHo>rD4OE3OmUnu$l|A&CiaA={wk?S#xP{1@AMbHwUiIf90@VUYM1`S>a@G zXY95%X=dI{#{1&L0ild$Sbr!ioTZAHp52d-kudnZL;WVDs{Y+W?Ow(CR z#4|B5k#|7&Jmr7?{YORzn?nrl69Mx+Swad_HQv}GqTzIEyLwAYUmF^fVlJ)L2t7Gf zAQ8#yYyMF~tFED;p+*-K^og$~Wyo?}{mEv0ZuF2)i};g3-_@}Wv!;KEd=iVw7Y{cF z>iu>o${g^!Gc0FN_}TC$Z+IX@56m+*7Z|_4Yq@?ztSIm{R}#}BMvCPDG=VFHxgqm| z|NcpO0()6`BTWNwe`(Bj)FoUc8-Cu`hamaavnCD;9@2SywS=`x{WbWa1c_}Cl$TGo zZQUQM5)`B=3j35ae-;)upVza-wpe#|a6B3k^eQ2EOT$d(-Hg?+RcOM&`Ez1kkKghcG;K6i~g)80W z-19`d9z{SuxQ2dVJ$_+T0Ch@%k*j8g{S4mV9vOYDDGlyxcDry#Mx*!yjd) zn%g?w$*Z<_F<*=P5Jq8=qMW z#Ws@L+VL^Td=^VV+#hVF4xEAf7?@7-*!-#Kw|0a}J^kNmfgSmr(sv_~T=$M|d>BR@ z1mXKjXdS717*u#?=qiM>@q2@nG8uQ4c%kbTe+jUEp%l22P9cz&>U@VPR$T*OO=IYD zWSP18;y6{Hy_<5m^0|1%*;NcI@iZ$Y;cj^u4J;ex(W6KH-Tey=m7h0Qo;-OHJ|W{e zR>Q|YyA_K(_B^Zt;Io-}VSa8-%C_aP!`D+{7=)wc^Y>YozKNj}SnfhDEtPYs^);dbmkt-E_Cf~@ z44y6%lDVxym__c+d=oSpnw@>(`NXV+>a=#I!Lhh)Z{pj?=$C=NMzhpe=+v@1wL9*@ ztb`C-9A+E@#g@xuQM7&BsRp7&;!E3)wih?$2=*)(>UX`?Ct$jR%YA4CTG=q-^45`x zr8$vd>~DGMyL-ZFr^vgJGYAR1siq|nto+yLgA*X$@QGg*bbtT+`LlZcH559%iygZ$tq zMxjsS8+^6x&jh6??nq>AD5SHl&HzODH+D^ZY~%Lx=pS8O^(7nZ@?-8AIQhX}zj!wN z_@l1{Euth*H?kC;ZFKdIjiu+_5TZ&fRz^^6S}mdd`=*+j?cr~}0mYgJ%qKiXmAEG72RaHrY8SyFPl9uk?LKm)@U0(h#Tj(@bba}vrIy;er z=W;roBl&iAkfUZ_s^>({Cdmx$eyLWy&Db_xXR=tWxXs#s%!+Q%z$NzXTORcH?(Rd? zm6a75xxXE~z4MT*76zRX5)-#}?^I_{3S!@(fVTjq(w$UB-e%o6>zI6-`OI#dtm3nJ z`s`!V@;c$o(N|89DmK_B_j>#kRz45xZ=D1TdGX#rR^nMRU$<>J_r5pSPXGXun}QHM zB=^SfcixT;T* z>Qjp+Cz54UY`>O|f~cS9RoJ@D<0Pl!M~|Mn@8-r|Mv8)}L#ALc3e(R*L;AK?Mp@nv zZkd?;YNT{$!2degmgY`gC@1tM`2@PkgvO8eh2F2BGda&)mY}nZ!jp2DHfo{Y|c{tknNY_|Gxf@>_BB#K;=U5`e$2&yQIqkrERUHf6-{P2rSj zdN7g^XQ}$J;o9|QN9FWC|GZ)WTp^YE_Ka*)kc#f7cwkKHX!o$7prGVp28sG*sm!3& zi%%Ij`@1ZHs(n{RB76@lKH;(xLtoIX?~@Jzj0&$x=v`H)PE0EASv|)`a(k=%{S3xL z7!mz%Zf+tmE$txG-hSrhxOKzg$3p+r+W8YI!8Y8-nl^eaS>7eNjn%yF@9$sjCiY@f zl*rhYJX{_f<|zH|TMcLd?6|6sJnA%3(%IXal@=dgae*x=Sb{Ced$`|?bemM`HP5^| z-Rv?dr=zRO@`DJ{fg=F)@wD?4nf!gIxF46*rq~2f?zC$35z7V$S|5LXlLupuVfgp& zUrA4QcVkI$v5SM-Y}9p?J%`n1Qm8o4XbnZ&+0ur~uCA-$lr}%#U0oipuMVmRyOGupfT#<21-dA7HlF`(nhq*%VM}mQ}|0Y9Ut>c8WHpWMj95Sx3jD3z+6|i z>osdy!r*An%I0cf+kevKxg(}P|E7Pw~`YR*Z)|!`MAk3r1)vk=$wLr z@4=RxAabTaG%~&D4i&2;s>XjZr~Y1OXlRHGSM=Qc{NVh5|6SSm_m9iV({luM>h<@& zn3xzt*jzb~&7S&%dBGx%ML#10`<0b@`PorSaWnojszu858%~H%< zZgB|?H-E8r^uJJ5v($P{;0+_J2NP!a=i{>oLS~je$AH4~ZQ38@;HUtOOu<0{q*(4c zUboskhYsV}j1Wx$>!JLC@RV=Dth_+ zIX!%s_MP1M@4MhD2dHqte>M20KMxqpBp4e^xe@2w03LO!kvPx=rKR&g+b`lb>ergO z8UkpT*4>aM#)ae~Z;;2=FN6poqeB|fE{rpMBA>OmYW|&Di+WK_*Z-@)xOSy#lsQm^ zE7}MsY)FXm&wB}P-f$t1x$A_ebFM|d1B4;?N+~XiU6k)1WmEV)SJ%0>k$ms+@}At2 z>HHSW9k7s>TxWs^Rn1Hd3Mflo2HYq{Exxw$mDW(MB5fcgp)FR($FygnC<7TXc--jG zNyq}}Oj#u>KECy}CSM1py&WmPM5ynV6Ue%kccgi-7%w_w_qgS1_d%Yq>(- z)oo5@SH6sXgzMEB9C7|)q3W5K#3Iy63De@!IoIo`@>0x8iVAc zqoaRhTuRWEX=O`OhKnq8f}-?uXy`lKQ^3}5vx34U4}T|V z{MCN{sxUV)*u9v#^3=kycmC(VfL-izi*Rk&pOB=7< zzb^*x^_*qh=l83`Ln1;zPZtI08AI0pe8wumZz7q3Y7(MKF`*NjFlB>gHE?Nric%S|h?t(X+g=^-nu=r@aeWiF{|OWqipr&mn+<#z z$aWW8ZkKK()BMg<4jQU(Vgaqp8A^`q!#_97&A-oerxg?xF?xQPbVIpI<8M#DV-khE zciVuUP7PoIl+QcfKd*MY(CV}so;b9&ww@e4w?_d2DG%hh{jFMkeSNr4V^_-{7BHi~4uvL=Gil`J8Tij5N*Wyc7N zlgQ%kL2zSk?qgsn>vL&tMtAP$kyXn#2ikq8bEo9W*6#Ov(=>l^K(ktPP0PBU%1%%k zRw1g!(qVjje3UkRNH^>+7Lu80fz~@Yij8e$D~fHQOBhK2x1~E##mc-B78IgrosOS< zeQLLE-TGriIBAY{hg#hK^XC_cn{N>;D&M7hl{D<_3H@Ip1<=bAJ408O8)~Zt1VgK@ z9w(3EYeUtyWJikTmIv|fIGJzfQVBce=%>j!Zhci{i~8i)m83HIl3P9o^qkkDuJtYg ze*4{6>g=N*(4A4ocuGcgo*|XvO@EL^Q@mj?gk)8^3LAnMv<_3wVZ%V84kaB-^8cGR zxf$}wwjX3T?x!qkm4d=0IWdaQx!oGpW9^OhK>;)ddS3vu3~ckjc>p#JOW1C!0Dm== zxt??8Kw$Q>A)+YFF`Sv*mRntZ+=?-FtcfdlLDza3vZS_=LKL8w+n_+bG>QEAmD~`4 zeP_)3)JTXc+OwV45A=xG%0}eH@$+_H9#K(*1S#W>;=`i4YbGz4do1(~QLws}vKP|z zE%k7LW-ZDUy&bfmZ~kPRa`flV*NdzPmm&E=*W1J0I#q~Heugn5{DfJX_-jD zSTF{%sLJmn@6Wkky-EcQ5?#2U!B)9+F^+F3u&5J<0106i-|w3ZZQ}jR0Xnz(5(Zw=q8eXH{1(HKyvn z<|aDAppeP$Vc7DD68TZ*vwls1k1#x%}8L7@^sXUNCI^3CLks_==kh1%UlSXN= zc+l)+f%fUp_S(Hs6zO<9+FJUUPR;ks`SW=|`wH7hgQeE|xwq!@E_X_aAPZBx*}Wrg zgoTAU%jDHg1R-~*Q!>z21SwNvW8>H@(n_0{X+GYQSuOAxx)(LMR`_$^x+<^VrJhef zN#E7ha$@G@?!I~R1_>;d&n)7@C>-MSi5ppK>x~^tD1}|Kclr5qYGdns z6;8bdI_Ww^|NePCc<`Wndzz(eorJW6^c!aBA^q)k*}V%jU%!5RTXHMxv|m5@*w2E{ z`e8B>(#dmPBcop?BUD+0`8t1|7&~V3r@Hfr?CZ9xR0;`Yo0%f>!k4crBZq;_bZXbW zZx54TB=tr9bHY`QrTqzXYJpLgeyjc{MD@R=5!hjUm5{bnYEoBNQj!mQtq@9#GwtUh z)do>+s_Ee~jXx+p9z6I2`5nafbjKQ&^C+!x9!zNCBp>E8hx_s+ds%sTnmy@f=<7l! zd4yvNk$zonyb`{$iAx!er65ePglQREIqVx{#a`rTJ;FncmHfxqw| zQa#xddr~9rsF7sj7Bbd7zjwkoo=3meYBnf*BqZ|ISZjYorXQ_9a08?)}pBO@S-{_yX3 zs|>gYiiyK!#$_${(>jkfFKy7{to{aN2NDT;fM#U1rzi(TdH&A{MUd3@(1FO$&oAOV zSo`~zc7%ehsh)ueT5*B5SsH$uMQ91NSQGyx3q4`i6WKyGuQdPr3<65G6Pa;o#|1abR+Z%&+sKpK$@RLz7S3B&{u}$0PS6?4`kGfO~ zpG>fkDxkrh#QZ1^TM4TQg#Jpzj}U(b^TfyQ2|}60{T5@k7&Ge-*?WUlEsp(CYC(vU z=B-%hEvTbE+ppFa185Yhr7A z60qO;8(F)B$(L{q#8CdyFFCJNIstpe9nsC=LEjjXk&$^E$gpzdp_`l2LVv{B+uOL% zY`HH}r#qA^@|tPF|21)ee6rrIO1z*wwJ9FYhM1uop0>2K+$c`at^$MrrC|P!9mW?u zcJK6I?qsUjcqQW3{%~_%m6SR%@=FBIj5@7%>NPENLb~AU0rd;(CQ;s3#&vZ0m!Dt{0r5`>#8yOiX${s;eLgO=(d2SP6kk)tBR!8RWo!DP0Y+_ z?gz5R)@`-uaKXxb=J8jt%7Nxw7AU(yDsMsp7g1X6>tXJ+0=&nLABVO@Y@#0t`oqwR zRL~mOqWmEC?g!)%ufue6rZua|&!6!T`~(3AtaK#Ten)i(Xv?YBTBehS65vUIVj`d) zM(40LM|%s}lU^Eb;XJZkAat-wGaa#S2PCK-;x zVUrURJq!{Jthx8O@r{&`IjsL)ChmQt>#HO)#aCg3na9*|+RBKsFak~IUfAdwmX?;L z6NeH&9hHN&Deni$elrYC%{_3 z1gVP>6H(1NFf9d}N(UdCPhqdqeJX1wD*`a%Z0uLAF`;+}nhUIIHNZ}V9zG;CgSF#x z^-e9lf3r135Ic8tOQ*EywmEu?qtp<>T=ihMmyJy=e-QPFMnDPpy#K*Ub;ZBcW?*f= zQ(wIQVOj1%! zo+`3RV=AN|^d#M>XxWPeB|^D6R2IR1_*XL`5T~~YDwjZm74&+G>Ng^Xo=3*T#R=&9 z9XY#KcrH29$(r{$Ek0y=nH|C7B3PJ5K4R|4yfMtf@S_K$%kKnchWLC)Vn55yGdS2x^f`0p4D%jF*^=<(B*K*W=Z|S-7;&#CZI>hZTEJYY z(>*{R{RA@Xa(u$UdVNfB! zn#0uZEst-72$6xPY#hNfQ!UQOrprD&4l%;dvS8Ebat*pQ)EdCvOvMjI>wL~`%+rWZ zkyN9O5nm(h80Tub-FX2fP^q)Z9XjGr{2!_*1$ZNocL&6!o|BkgAV-kxG2Y(Z4!_64 zK%pVaeWi^L$CW6Rl5w#Kca`iH#06GcrmCvsns&cRfQ}=Z9kHoG85>HX3|b`81R+Lb zrVhCvSMg=;^J_Uu&^UwwLwUc6X;=M`h7p%eGpwgYg887$*i{k`?3cE!h~Fxai+oO5sEARHS{nPB-3%qN#;tZ-=D(mEHhtC_$s z@c#NdI2C9KQ;ke06T@ERb4-MbwtfA&bW}k|Tp<*nV`s|?Q!MrMnaK0d_t=Tb_pp#f z1X*0B!R}$jLS(@h5%C2J&lWk@5g#)~0x!y!&I0t8tixp|ikkuqH6~)k7ik18 z0ubx~W?;m7&FDPnGEk~1U5J-#T+ywDPw_AFGL>k?ICG~SQ8ADWVcgXj$_N+Z@87>~ znXUTm{hK{^`}6608(-V14wI&(P&eF&bpC=`tu%c87z@|hyk0pGMA786*5ly{CtyWM zvX-PYIhX0YWGniS2A*9dl-zzCRvWWu$&J5iCmki)_K;9$efV6=I3HhG8dBUo*bY#i zyq~{BOFHBmWV~5ql^TH@$?46gB$$w#yaBCIN?w&Er0L2zx>$5aW~dC)uVZcB*k?pu zKpSG_x0$sg^v9!K>D+RJey|dt&6MRJ!7k+s+5AboL*S<*o)14Mrjd%Aex}NfUnQq@ zvgYcvGEzy$tUWBi7KQ(1eBH5It{L<$!d7WmUeP&xeu&qz#Qko9Di4z)J3{5dj-OQL zGBq{z><~`I6A#uz6@(n?0MR8vwt;kY6kKsi(EmTI-Q_@ApOhm?Y^Bj*9z91-QqNJ{ zzhh|lh1{S3MTd<$QOXohoNjWyS?b^L2QDtN;4IAl{8{F&MHR}7y>M3)KrUuKhiRAR z(;2N9-b=pjpvF~zR@Qbc?`4J8ynz)sfr`x6fG~dpakW3|7_%%kQ4LpuE)a!KE^Wl# zRLizVguwEG1D*8cOPDR~?FB*X?tHEt1_HPead!sXUFZK=+y+D0@J)I{I$Lyh_>g*$ zXts*h42WbNvx#O#R;z4|Tg5p!5%04?-vDTM*}SWH>PTkj{sM6`_G*;NP^F};x>#Wf zaxZ&=T7^o6`hJ6LEA`!;^p2$S7HnJz{bn*;3vRiMjTxXPmW75iZ!hg#aKHZO zveEHQ0!mJJgbLe*{VotKYLNc@g+$Z$+WE}1p$`eZfaTi3s05MTA}c2c6Z`6u*L)AL zVI>mF&weznJV6j7i#KleKc;!_PWEa3 z)Z`ati*nff+6>gEoiFGmw4>n)OmSv(^x9}WL}vF7#Z{~_8uAa+?L7~sGQP@`&D=b zM=wzPAU)E9HoJZSYmEJDLUT&nNS+&076fscnKM4{pDklk+!V{KqWQC(lPmnccWG#%TgZ|HBd?;jq>52zAkc^709z zy7xxD5c*8I9#bNA8>mbnIaQk*JdhoOL3o^BBN5lY$vkEgQk0NL_6UJ%FMd%n;S1UG z3$PEIw|@Ke92!b26qCZyq6b?d#-Lu>Vw=&b_N)vvyx@i;j87i&g7z&4Ubuhcac;i* zdD?Hxg9Zd`&iW&iKk*$%pr(~ook12H2^oaUjSHAHIJ1k3qrqHQ1}z#0ewu@soVEvhB=nH<5I>(t zs{gSc3~23RLNPUF&M&!}N%ttC2|o)LE*hEc`w5DmxdO_PX7q zyyo;gJ*5s`8FSS@r3pP4on(u;V#NErUkj|AAQk>c_FRAXaN3X*`X+pmqB<_z{;;tl z>Fl3K%p}T8coB&gy*>x^^UNf1yE;{_+l3)Fldy0-3=nBc0P?&LNGy=ES8&OF{rzzt zTCZRZFlm^KC`B!EM_n{hMjGVCgb60G3S9yBv6)qX@Z|0{`5H)poGQWo@0f-2GUl_w zG9eSbS7dx*0>F>VX=XbUO-xNi_T39H{3r&tknB&L`II)zO1Lg;=`;NkdMRYX@$Z{v z|8u6p`vq|NUEwpJZv_4;>C}^M68V<(zO%E#;r>>UMRg2RJ_p7l#iDXDgcFhp&{Twkj>jAE0Pv|yz zkzXVC-wQAG8TDU1b%booG($VoKyZMv0jA-j$Br4^x$~nZLp#HsgKF-`XxW-@1AgZ_ zWwz5iyDnvkU`HhzWKVyx9Z_Yg;QIRdtFSTc$DInz2jgo)^?q(2Tl)mMIy&Cw=RbvB zfg$ut$aS&IKs7VaE~s^ksv&DR;ypJzrb%DOlw$t?d!$3fA7+dUVu4D{!pm{)N9xg| zjJFf*#e|9ApEb6!S~bGTBt!FaqA^J9%o#ht#9)2TmnySh8e>*cKYkR?c}@iR@{+nq z&A9~IZgi{-=DY@~L@i8V$_4wUThs|jpr{TIT@L|Sd;)q4cJ~(WaM08fL&L5-^VuMA z;9Huqegi5#l&9Kd!}HqJv*!Q!V3@Nz)UE;mO}YjMMJ>F}>lgLalXqrOkqk4uz zu(%Z3;3wwcZ@r9ox=`TzNe7Gq{<9gi%aSf5GCSvP=p2IXY6C1!1QGiK?%9BVi=e#O zfSR&ARx9lsb&IA<_JG9n<6GIG`h21dKI2N!fB5Yox1o3y>pkzsfl5MOXJ4A0d_CeWl}4v#m!Ws= zNDWHi?ANl*2mgJ4t(T>eu`ux73)mys;qL+MYUu~RKA1%2GY*W5bb^uP&7!2#ZBT`v zs{-H|R=%ev;}W@N=e6>L+2%;u3!-zmyK$xonNk?hhEhXU!{i1#+DOPk!( zaM;i5x0P)|z+fe7KvjY&LGH3XqI;}_8@gTq8!vFU>JkRof9N_O`&x(uB zkvmR4C+_FL>W$a?Re*Q{VxJH006MsmzEeASbpFvpS4kJM6)UH{QwnTdLnX86QkNhx z`Ys9-#y4(Y$#!C};dYXp&7bcNFb6Ad581ECvLetx1gR+Ny3dKXKvLkbh@cMu7GPy* z#Q)X%t>r@(09z+PuaLn)ufwWc9+whNE6@$n`b9H(U79(a|STnIk0Ie#|n_EtzN3s zSLGv8SMp6!FRz^X_4~J>v9SgSBp~2eHkB3^>sPPtXLCjA<3_`NTE~yL|Kf)h&aAr~ z-4{k&7{X6g{%&*f|Lvua2PRh#@>2rvvITnwjQ&JBUZl3z^Lu4=^#-}B-}gD`{P1B; zNlDE9ejwz_g2KX_w{NGgUI$@4*_U?xxiHmHKJyajB;>qUv=#-(lM_$T{M6!<#HT~z zPuY#R4C%?P>`;Qgh+YczF&%q~g2yR%9KeRKyUr8v-)p^eg>~!_WL(GPp|T!*t#|$X zMyuc|_zQn~d;8=;pb|!0ZnP#xERvf^b|mq>%>HLG z_sDFXS(~BEx>GnK^HR9E6uvCPx>qFHl#}Nx%;DQG|r$Vy8F))OA2#B3f4wL~?&RsYeBd64V zYL6uE{x|_9D)RsD?d`qfl70SGBT@=lUGV)ceQz~5FM9qLKJo0)BlNWxo&eklxWJ2& z+Me*p+1p`!0ZhMI#x6hxKpqIFGnRke4Z;jTD1-UF4N@FfWJK7a`bI`{zBN89$^A?g0Gc+r`Pe^94us!m6CQNnf zr*W*9r#sf=2jmkk|!RMJ|*(_qrh8D`W+E*(w=`h>em!0XR zQ``%bCWk-)&iq&L2ka=EA-XKtt6 zderV~Os<|Gi}U!-P}N#l;?UO+>7>q^^g!&Lzp?7l=CkP@j~Ut7FvMQKI@s6@jOKm0 z$&!Gc&1iCczeF|3yLR-p2%@7Y_)M5W^T?8H3a#ELIa`mGrc(BukgV-AF|mT%QPHJq zr+n2XNg?wLAyj1yAsWx^5~!chVWh_0>qIEoF&+nen%?`0O#?fEK?I?^c-k8kYh~{T zb~5;nsR@@c1pjWzdT4y4D1lxC>HqQ+ukI-sV=mEh+HQlLS?~|Vg{S84B<&^#e`Z*5 z>pREiPaQP%>bvgVFMeV^_2ybO^HBYoVa3{fok8#5@ESQr44L S8+cS3(Y|VMrR1{BqyGn6^m_{c literal 0 HcmV?d00001 diff --git a/test_page.toml b/test_page.toml new file mode 100644 index 0000000..57960b8 --- /dev/null +++ b/test_page.toml @@ -0,0 +1,7 @@ +[page] +title = "Test Page" +description = "This is a test page." +content_path = "content.md" +#favicon = "optional_favicon.png" +#javascript = ["Additional JavaScript"] +#css = ["Additional CSS"] \ No newline at end of file