From addce4f87c17874183f73d4203dbba58f13da01c Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Mon, 3 Feb 2025 21:51:09 -0500 Subject: [PATCH 01/10] render markdown --- lib/Config.rakumod | 16 +++++++++++----- lib/DB/MarkdownPost.rakumod | 5 ++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/Config.rakumod b/lib/Config.rakumod index fc1f057..73c4831 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -10,14 +10,20 @@ method generate-post(Str:D $title, Str:D $content, BlogMeta:D $meta) { head [ meta :charset; meta :name, :content; - # meta :name, :content; - title ""; + title "{$meta.title} — $title"; + # TODO: Add style sheets + # TODO: Add description + # TODO: Add header links ]; - my $body; + my $body = + body [ + div :class, [ + $content + ] + ]; + # TODO: Setup footer # my $footer; - die "Not done yet"; - my $html = html :lang, [ $head, $body diff --git a/lib/DB/MarkdownPost.rakumod b/lib/DB/MarkdownPost.rakumod index e395949..f901ce3 100644 --- a/lib/DB/MarkdownPost.rakumod +++ b/lib/DB/MarkdownPost.rakumod @@ -2,7 +2,6 @@ use v6.e.PREVIEW; use Pandoc; use JSON::Class:auth; - use DB::Post; #| A plain markdown post @@ -13,10 +12,10 @@ unit class MarkdownPost does Post is json(:pretty); has Bool:D $.markdown = True; method title(--> Str:D) { - markdown-title($!source) + markdown-title $!source } # Simply provide our source file to pandoc method render-html(--> Str:D) { - die "Not implemented"; + markdown-to-html $!source } From 56833369ce0724cc6aead6f43d32c72decb332f5 Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Mon, 3 Feb 2025 23:43:19 -0500 Subject: [PATCH 02/10] Add ipkg path to idrispost --- blog | 7 +++++++ lib/DB/IdrisPost.rakumod | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/blog b/blog index e260329..9b4dd8c 100755 --- a/blog +++ b/blog @@ -25,6 +25,9 @@ my IO::Path:D $default-db-dir = #| The default output directory my IO::Path:D $default-site-dir = $default-blog-dir.add('site/'); +#| The default idris ipkg +my IO::Path:D $default-ipkg = $default-blog-dir.add('projects/Idris/Idris.ipkg'); + #| Initalize the database multi MAIN( "db", @@ -92,6 +95,8 @@ multi MAIN( "idris", #| The path to the idris file IO::Path(Str) $source, + #| The path to the ipkg file + IO::Path(Str) :$ipkg = $default-ipkg, #| The path of the database file IO::Path(Str) :$db-dir = $default-db-dir, #| The date/time the post should be recorded as posted at @@ -99,6 +104,7 @@ multi MAIN( #| Should the post be hidden from the archive? Bool :$hidden = False, ) { + say $default-ipkg; my $db = read-db $db-dir; my $id = $db.insert-post: @@ -106,6 +112,7 @@ multi MAIN( source => $source.absolute.IO, posted-at => $posted-at, hidden => $hidden, + ipkg => $ipkg.absolute.IO, ); $db.write: $db-dir; say 'Post inserted with id ', $id; diff --git a/lib/DB/IdrisPost.rakumod b/lib/DB/IdrisPost.rakumod index 5dd6f9a..e36da57 100644 --- a/lib/DB/IdrisPost.rakumod +++ b/lib/DB/IdrisPost.rakumod @@ -13,6 +13,14 @@ unit class IdrisPost does Post is json(:pretty); #| cheaty way has Bool:D $.idris = True; +#| Location of the ipkg for the package containing the post +has IO::Path:D $.ipkg + is required + is json( + :to-json(*.Str), + :from-json(*.IO) + ); + method title(--> Str:D) { markdown-title($!source) } @@ -20,5 +28,10 @@ method title(--> Str:D) { # Use katla to highlight our file, mangle the resulting output, and then pass it # through to pandoc for html generation method render-html(--> Str:D) { + # Do a pack build to make sure we have the needed files + # TODO: Figure out how to only do this once + + # Run through katla + # Send output die "Not implemented"; } From 2c38910ccd8b64a6c64d2d7b97c128eb441885d8 Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Tue, 4 Feb 2025 01:20:57 -0500 Subject: [PATCH 03/10] basic idris rendering --- blog | 1 - lib/DB.rakumod | 5 ++++- lib/DB/IdrisPost.rakumod | 47 ++++++++++++++++++++++++++++++++++------ 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/blog b/blog index 9b4dd8c..4198bdd 100755 --- a/blog +++ b/blog @@ -104,7 +104,6 @@ multi MAIN( #| Should the post be hidden from the archive? Bool :$hidden = False, ) { - say $default-ipkg; my $db = read-db $db-dir; my $id = $db.insert-post: diff --git a/lib/DB.rakumod b/lib/DB.rakumod index 55ab136..00b70fa 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -84,7 +84,10 @@ class PostDB { my $id-path = $by-id.add: "$id.html"; $id-path.spurt: $html; for $post.all-slugs -> $slug { - $by-slug.add($slug).symlink: $id-path; + # remove the symlink if it already exists + my $slug-path = $by-slug.add: $slug; + $slug-path.unlink if $slug-path.l; + $id-path.symlink: $slug-path; } } # Render the archive diff --git a/lib/DB/IdrisPost.rakumod b/lib/DB/IdrisPost.rakumod index e36da57..1c0bfbc 100644 --- a/lib/DB/IdrisPost.rakumod +++ b/lib/DB/IdrisPost.rakumod @@ -2,6 +2,7 @@ use v6.e.PREVIEW; use Pandoc; use JSON::Class:auth; +use File::Temp; use DB::Post; @@ -11,7 +12,7 @@ unit class IdrisPost does Post is json(:pretty); #| Marker for disambiguation between post types in json representation, the #| cheaty way -has Bool:D $.idris = True; +has Bool:D $.idris is json = True; #| Location of the ipkg for the package containing the post has IO::Path:D $.ipkg @@ -28,10 +29,42 @@ method title(--> Str:D) { # Use katla to highlight our file, mangle the resulting output, and then pass it # through to pandoc for html generation method render-html(--> Str:D) { - # Do a pack build to make sure we have the needed files - # TODO: Figure out how to only do this once - - # Run through katla - # Send output - die "Not implemented"; + my $project-dir = $!ipkg.parent; + indir $project-dir, { + # Do a pack build to make sure we have the needed files + # TODO: Figure out how to only do this once + pack ; + # First we have to find the location of the ttm file + my $relative-path = $!source.extension('ttm').relative: + $project-dir.add('src'); + my $ttc-dir = dir($project-dir.add('build/ttc/'))[0]; + my $ttm-path = $ttc-dir.add: $relative-path; + # Run through katla + my $output = katla 'markdown', $!source, $ttm-path; + # Send output to pandoc through a temporary file + my ($filename, $filehandle) = tempfile; + $filehandle.spurt: $output, :close; + markdown-to-html $filename.IO + }; +} + +# Run a pack command, erroring on failure +sub pack(*@args) { + my $pack = run 'pack', @args, :out, :err; + my $pack-out = $pack.out.slurp: :close; + my $pack-err = $pack.err.slurp: :close; + die "Pack exited with {$pack.exitcode}\nout: $pack-out\nerr: $pack-err" + unless $pack; +} + +# Run a katla command, erroring on failure, returning the standard out, and +# mangling the output accordingly +sub katla(*@args --> Str:D) { + my $katla = run 'katla', @args, :out, :err; + my $katla-out = $katla.out.slurp: :close; + my $katla-err = $katla.err.slurp: :close; + die "Katla exited with {$katla.exitcode}\nout: $katla-out\nerr: $katla-err" + unless $katla; + # TODO modify output + $katla-out } From 37e92392ca323556fb34954ef59eee17f0a810ac Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Tue, 4 Feb 2025 03:10:40 -0500 Subject: [PATCH 04/10] Basic syntax highlighting --- lib/Config.rakumod | 8 +++-- lib/DB/IdrisPost.rakumod | 15 ++++++++- projects/Idris/src/Posts/HelloWorld.md | 24 ++++++++++++++ resources/code.css | 43 ++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 resources/code.css diff --git a/lib/Config.rakumod b/lib/Config.rakumod index 73c4831..7418ec9 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -5,13 +5,17 @@ use DB::BlogMeta; unit class Config; + method generate-post(Str:D $title, Str:D $content, BlogMeta:D $meta) { - my $head = - head [ + my $head = head [ meta :charset; meta :name, :content; title "{$meta.title} — $title"; # TODO: Add style sheets + # Use Iosevka Alie as the monospace font + link :rel, + :href; + style %?RESOURCES.slurp; # TODO: Add description # TODO: Add header links ]; diff --git a/lib/DB/IdrisPost.rakumod b/lib/DB/IdrisPost.rakumod index 1c0bfbc..27a44a2 100644 --- a/lib/DB/IdrisPost.rakumod +++ b/lib/DB/IdrisPost.rakumod @@ -60,11 +60,24 @@ sub pack(*@args) { # Run a katla command, erroring on failure, returning the standard out, and # mangling the output accordingly sub katla(*@args --> Str:D) { + # Run the requested katla command my $katla = run 'katla', @args, :out, :err; my $katla-out = $katla.out.slurp: :close; my $katla-err = $katla.err.slurp: :close; die "Katla exited with {$katla.exitcode}\nout: $katla-out\nerr: $katla-err" unless $katla; - # TODO modify output + # Modify the output to use our styling instead of katla's + # Remove the style block and line breaks + $katla-out ~~ s:g/''//; + $katla-out ~~ s:g/'
'//; + # Use
 blocks for the code and inject our own class for idris code
+    $katla-out ~~ s:g/'' \s*/
/;
+    $katla-out ~~ s:g/\s* ''/<\/code><\/pre>/;
+    # Replace the highlighting classes with our own
+    $katla-out ~~ s:g/'class="Idris' (\w+) '"'/class="hl-{$/[0].lc}"/;
+    # Clean up inappropiate escaping of some characters
+    $katla-out ~~ s:g/'\\*'/*/;
+    $katla-out ~~ s:g/'\\_'/_/;
+    $katla-out ~~ s:g/'\\\\'/\\/;
     $katla-out
 }
diff --git a/projects/Idris/src/Posts/HelloWorld.md b/projects/Idris/src/Posts/HelloWorld.md
index 026080b..8e4f3f8 100644
--- a/projects/Idris/src/Posts/HelloWorld.md
+++ b/projects/Idris/src/Posts/HelloWorld.md
@@ -5,3 +5,27 @@ module Posts.HelloWorld
 
 %default total
 ```
+
+Here is an example function that prints to the standard out:
+
+```idris
+main : IO ()
+main = do
+  putStrLn "Hello World"
+  putStrLn "Hello \n with new lines \n world"
+  putStrLn "And some other potential problem characters: * _"
+```
+
+And a function with some more stuff:
+
+```idris
+-- A regular comment
+
+||| A doc comment
+thingomizer : (a : String) -> IO ()
+thingomizer a = 
+  let xs = map (+ 1) [1, 2, 3, 4]
+  in do
+    putStrLn a
+    printLn xs
+```
diff --git a/resources/code.css b/resources/code.css
new file mode 100644
index 0000000..5bb4f17
--- /dev/null
+++ b/resources/code.css
@@ -0,0 +1,43 @@
+/* TODO: Move this somewhere else */
+:root {
+  color-scheme: light dark;
+}
+
+code {
+    font-family: "Iosevka Aile Web", monospace;
+    background-color: light-dark(#fbf3db, #103c48);
+    color: light-dark(#53676d, #adbcbc);
+}
+
+pre > code {
+    display: block;
+    padding: 1rem;
+}
+
+.hl-type {
+    color: light-dark(#ad8900, #dbb32d);
+}
+
+.hl-module {
+    font-style: italic;
+}
+
+.hl-function {
+    color: light-dark(#428b00, #84c747);
+}
+
+.hl-bound {
+    color: light-dark(#ca4898, #f275be);
+}
+
+.hl-keyword {
+    color: light-dark(#0072d4, #4695f7);
+}
+
+.hl-comment {
+    color: light-dark(#909995, #72898f);
+}
+
+.hl-data {
+    color: light-dark(#d2212d, #ed4a46)
+}

From 7df154596bfe04765abf86d5d780e9fd655199dc Mon Sep 17 00:00:00 2001
From: Nathan McCarty 
Date: Tue, 4 Feb 2025 03:18:19 -0500
Subject: [PATCH 05/10] Center things a bit

---
 lib/Config.rakumod |  1 +
 resources/code.css |  6 +-----
 resources/main.css | 13 +++++++++++++
 3 files changed, 15 insertions(+), 5 deletions(-)
 create mode 100644 resources/main.css

diff --git a/lib/Config.rakumod b/lib/Config.rakumod
index 7418ec9..871abb8 100644
--- a/lib/Config.rakumod
+++ b/lib/Config.rakumod
@@ -15,6 +15,7 @@ method generate-post(Str:D $title, Str:D $content, BlogMeta:D $meta) {
         # Use Iosevka Alie as the monospace font
         link :rel,
             :href;
+        style %?RESOURCES.slurp;
         style %?RESOURCES.slurp;
         # TODO: Add description
         # TODO: Add header links
diff --git a/resources/code.css b/resources/code.css
index 5bb4f17..7abcc7f 100644
--- a/resources/code.css
+++ b/resources/code.css
@@ -1,12 +1,8 @@
-/* TODO: Move this somewhere else */
-:root {
-  color-scheme: light dark;
-}
-
 code {
     font-family: "Iosevka Aile Web", monospace;
     background-color: light-dark(#fbf3db, #103c48);
     color: light-dark(#53676d, #adbcbc);
+    min-width: 80ch;
 }
 
 pre > code {
diff --git a/resources/main.css b/resources/main.css
new file mode 100644
index 0000000..833b93c
--- /dev/null
+++ b/resources/main.css
@@ -0,0 +1,13 @@
+:root {
+  color-scheme: light dark;
+}
+
+body {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.post-body {
+    max-width: 80%;
+}

From 37238e864b43f3b98c6abe87c85233638a37b420 Mon Sep 17 00:00:00 2001
From: Nathan McCarty 
Date: Tue, 4 Feb 2025 03:32:23 -0500
Subject: [PATCH 06/10] Get the color scheme all fancy

---
 resources/main.css | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/resources/main.css b/resources/main.css
index 833b93c..f587fa8 100644
--- a/resources/main.css
+++ b/resources/main.css
@@ -1,5 +1,7 @@
 :root {
-  color-scheme: light dark;
+    color-scheme: light dark;
+    color: light-dark(#474747, #b9b9b9);
+    background-color: light-dark(#ebebeb, #252525);
 }
 
 body {
@@ -9,5 +11,16 @@ body {
 }
 
 .post-body {
-    max-width: 80%;
+    width: 66%;
+    display: block;
+    padding: 1rem;
+    background-color: light-dark(#ffffff, #181818);
+}
+
+.post-body > h1 {
+    text-align: center;
+}
+
+.post-body > p {
+    text-align: justify;
 }

From bf56790e3ac13b4a5df883f61d5a734e8cdcaa5c Mon Sep 17 00:00:00 2001
From: Nathan McCarty 
Date: Tue, 4 Feb 2025 04:17:34 -0500
Subject: [PATCH 07/10] set font and round corners

---
 lib/Config.rakumod             |  7 ++++++-
 projects/Markdown/MyNewBlog.md | 20 ++++++++++++++++++++
 resources/code.css             |  1 +
 resources/main.css             | 23 ++++++++++++++++++++---
 4 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/lib/Config.rakumod b/lib/Config.rakumod
index 871abb8..b836b95 100644
--- a/lib/Config.rakumod
+++ b/lib/Config.rakumod
@@ -5,7 +5,7 @@ use DB::BlogMeta;
 
 unit class Config;
 
-
+# TODO: Support GFM admonitions
 method generate-post(Str:D $title, Str:D $content, BlogMeta:D $meta) {
     my $head = head [
         meta :charset;
@@ -15,6 +15,11 @@ method generate-post(Str:D $title, Str:D $content, BlogMeta:D $meta) {
         # Use Iosevka Alie as the monospace font
         link :rel,
             :href;
+        # Use open sans as the content font
+        link :rel :href;
+        link :rel :href :crossorigin;
+        link :rel,
+            :href;
         style %?RESOURCES.slurp;
         style %?RESOURCES.slurp;
         # TODO: Add description
diff --git a/projects/Markdown/MyNewBlog.md b/projects/Markdown/MyNewBlog.md
index 965b3f2..59e094b 100644
--- a/projects/Markdown/MyNewBlog.md
+++ b/projects/Markdown/MyNewBlog.md
@@ -1,3 +1,23 @@
 # My New Blog
 
 This is a blog that uses a static site generator written in raku!
+
+## A sub headings
+
+With some text under it
+
+### A third layer
+
+Even more text
+
+## Back up
+
+function debug null byte compile syntax error protocol stack overflow cache memory leak parse integer void pointer exception handler runtime deploy code fragment database query optimize algorithm refactor git commit push merge conflict
+
+### Back down
+
+here we go
+
+#### even further
+
+weeeeee
diff --git a/resources/code.css b/resources/code.css
index 7abcc7f..7c14631 100644
--- a/resources/code.css
+++ b/resources/code.css
@@ -8,6 +8,7 @@ code {
 pre > code {
     display: block;
     padding: 1rem;
+    border-radius: 0.55rem / 0.5rem;
 }
 
 .hl-type {
diff --git a/resources/main.css b/resources/main.css
index f587fa8..dad7989 100644
--- a/resources/main.css
+++ b/resources/main.css
@@ -2,6 +2,7 @@
     color-scheme: light dark;
     color: light-dark(#474747, #b9b9b9);
     background-color: light-dark(#ebebeb, #252525);
+    font-family: "Open Sans", sans-serif, serif;
 }
 
 body {
@@ -13,14 +14,30 @@ body {
 .post-body {
     width: 66%;
     display: block;
-    padding: 1rem;
+    padding: 3rem;
+    border-radius: 1rem;
     background-color: light-dark(#ffffff, #181818);
 }
 
 .post-body > h1 {
     text-align: center;
+    color: light-dark(#dd0f9d, #eb6eb7);
 }
 
-.post-body > p {
-    text-align: justify;
+.post-body > h2 {
+    text-align: center;
+    color: light-dark(#282828, #dedede);
+}
+
+.post-body > h3 {
+    text-align: center;
+    color: light-dark(#282828, #dedede);
+}
+
+.post-body > h4 {
+    text-align: center;
+    color: light-dark(#282828, #dedede);
+}
+.post-body > p {
+    text-indent: 2ch;
 }

From 804b51aaa6d03aba1780a9798498dc44a74af262 Mon Sep 17 00:00:00 2001
From: Nathan McCarty 
Date: Tue, 4 Feb 2025 15:57:53 -0500
Subject: [PATCH 08/10] Style tweaks

---
 projects/Idris/src/Posts/HelloWorld.md |  6 ++++++
 resources/code.css                     | 10 +++++++---
 resources/main.css                     |  1 +
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/projects/Idris/src/Posts/HelloWorld.md b/projects/Idris/src/Posts/HelloWorld.md
index 8e4f3f8..afa7d4f 100644
--- a/projects/Idris/src/Posts/HelloWorld.md
+++ b/projects/Idris/src/Posts/HelloWorld.md
@@ -8,6 +8,12 @@ module Posts.HelloWorld
 
 Here is an example function that prints to the standard out:
 
+
+function debug null byte compile syntax error protocol stack overflow cache memory leak parse integer void pointer exception handler runtime deploy code fragment database query optimize algorithm refactor git commit push merge conflict
+function debug null byte compile syntax error protocol stack overflow cache memory leak parse integer void pointer exception handler runtime deploy code fragment database query optimize algorithm refactor git commit push merge conflict
+function debug null byte compile syntax error protocol stack overflow cache memory leak parse integer void pointer exception handler runtime deploy code fragment database query optimize algorithm refactor git commit push merge conflict
+
+
 ```idris
 main : IO ()
 main = do
diff --git a/resources/code.css b/resources/code.css
index 7c14631..4635ff2 100644
--- a/resources/code.css
+++ b/resources/code.css
@@ -3,14 +3,18 @@ code {
     background-color: light-dark(#fbf3db, #103c48);
     color: light-dark(#53676d, #adbcbc);
     min-width: 80ch;
-}
-
-pre > code {
+    width: 80%;
     display: block;
     padding: 1rem;
     border-radius: 0.55rem / 0.5rem;
 }
 
+pre {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
 .hl-type {
     color: light-dark(#ad8900, #dbb32d);
 }
diff --git a/resources/main.css b/resources/main.css
index dad7989..f6aeecd 100644
--- a/resources/main.css
+++ b/resources/main.css
@@ -17,6 +17,7 @@ body {
     padding: 3rem;
     border-radius: 1rem;
     background-color: light-dark(#ffffff, #181818);
+    /* text-align: justify; */
 }
 
 .post-body > h1 {

From ce364c01d809bb9b0134b1432d550e9966a3fd89 Mon Sep 17 00:00:00 2001
From: Nathan McCarty 
Date: Tue, 4 Feb 2025 16:51:37 -0500
Subject: [PATCH 09/10] Description generation

---
 lib/Config.rakumod          | 31 ++++++++++++-----
 lib/DB.rakumod              |  4 +--
 lib/DB/IdrisPost.rakumod    | 13 +++++++
 lib/DB/MarkdownPost.rakumod | 13 +++++++
 lib/DB/Post.rakumod         |  5 +++
 lib/Pandoc.rakumod          | 69 ++++++++++++++++++++++++++-----------
 6 files changed, 102 insertions(+), 33 deletions(-)

diff --git a/lib/Config.rakumod b/lib/Config.rakumod
index b836b95..af798d0 100644
--- a/lib/Config.rakumod
+++ b/lib/Config.rakumod
@@ -2,29 +2,42 @@ use v6.e.PREVIEW;
 
 use HTML::Functional;
 use DB::BlogMeta;
+use DB::Post;
 
 unit class Config;
 
-# TODO: Support GFM admonitions
-method generate-post(Str:D $title, Str:D $content, BlogMeta:D $meta) {
-    my $head = head [
+method generate-head(Str:D $title, BlogMeta:D $meta, $description?) {
+    head [
         meta :charset;
         meta :name, :content;
+        meta :author :content;
         title "{$meta.title} — $title";
-        # TODO: Add style sheets
-        # Use Iosevka Alie as the monospace font
-        link :rel,
-            :href;
-        # Use open sans as the content font
+        # Add description, if one exists
+        do if $description ~~ Str:D {
+            meta :description :content($description)
+        } else {
+            []
+        }
+        # Preconnect to all our resource sources
+        link :rel :href;
         link :rel :href;
         link :rel :href :crossorigin;
+        # Load fonts, Iosevka Alie for code, and Open Sans for content
+        link :rel,
+            :href;
         link :rel,
             :href;
+        # Inline our style sheets
         style %?RESOURCES.slurp;
         style %?RESOURCES.slurp;
-        # TODO: Add description
         # TODO: Add header links
     ];
+}
+
+# TODO: Support GFM admonitions
+method generate-post(Post:D $post, BlogMeta:D $meta) {
+    my $content = $post.render-html;
+    my $head = self.generate-head($post.title, $meta, $post.description);
     my $body =
     body [
        div :class, [
diff --git a/lib/DB.rakumod b/lib/DB.rakumod
index 00b70fa..fd95f6f 100644
--- a/lib/DB.rakumod
+++ b/lib/DB.rakumod
@@ -78,9 +78,7 @@ class PostDB {
        mkdir $by-slug unless $by-slug.e;
        # Render all the posts and make symlinks
        for %!posts.kv -> $id, $post {
-           my $html =
-                $config.generate-post:
-                    $post.title, $post.render-html, $!meta;
+           my $html = $config.generate-post: $post, $!meta;
            my $id-path = $by-id.add: "$id.html";
            $id-path.spurt: $html;
            for $post.all-slugs -> $slug {
diff --git a/lib/DB/IdrisPost.rakumod b/lib/DB/IdrisPost.rakumod
index 27a44a2..1e229ac 100644
--- a/lib/DB/IdrisPost.rakumod
+++ b/lib/DB/IdrisPost.rakumod
@@ -14,6 +14,9 @@ unit class IdrisPost does Post is json(:pretty);
 #| cheaty way
 has Bool:D $.idris is json = True;
 
+#| Override the generated description for this post
+has Str $.summary is json;
+
 #| Location of the ipkg for the package containing the post
 has IO::Path:D $.ipkg
     is required
@@ -48,6 +51,16 @@ method render-html(--> Str:D) {
     };
 }
 
+# Return our summary, if we have one, otherwise extract the first paragraph of
+# the markdown document
+method description(--> Str) {
+    if $!summary {
+        $!summary
+    } else {
+       markdown-first-paragraph $!source
+    }
+}
+
 # Run a pack command, erroring on failure
 sub pack(*@args) {
     my $pack = run 'pack', @args, :out, :err;
diff --git a/lib/DB/MarkdownPost.rakumod b/lib/DB/MarkdownPost.rakumod
index f901ce3..0f189d5 100644
--- a/lib/DB/MarkdownPost.rakumod
+++ b/lib/DB/MarkdownPost.rakumod
@@ -11,6 +11,9 @@ unit class MarkdownPost does Post is json(:pretty);
 #| cheaty way
 has Bool:D $.markdown = True;
 
+#| Override the generated description for this post
+has Str $.summary;
+
 method title(--> Str:D) {
     markdown-title $!source
 }
@@ -19,3 +22,13 @@ method title(--> Str:D) {
 method render-html(--> Str:D) {
     markdown-to-html $!source
 }
+
+# Return our summary, if we have one, otherwise extract the first paragraph of
+# the markdown document
+method description(--> Str) {
+    if $!summary {
+        $!summary
+    } else {
+       markdown-first-paragraph $!source
+    }
+}
diff --git a/lib/DB/Post.rakumod b/lib/DB/Post.rakumod
index f27c2b6..0c43fdb 100644
--- a/lib/DB/Post.rakumod
+++ b/lib/DB/Post.rakumod
@@ -53,3 +53,8 @@ method all-slugs(--> Array[Str:D]) {
 
 #| Render this post to an html body
 method render-html(--> Str:D) {...}
+
+#| Get the description for this post, returning nil if there is none
+method description(--> Str) {
+    Nil
+}
diff --git a/lib/Pandoc.rakumod b/lib/Pandoc.rakumod
index fb80e4e..74048f0 100644
--- a/lib/Pandoc.rakumod
+++ b/lib/Pandoc.rakumod
@@ -3,13 +3,10 @@ unit module Pandoc;
 
 use JSON::Fast;
 
-#| Extract the title from a markdown document
-#|
-#| The title is the only top level header, will throw an error if there are
-#| multiple top level headers or are none
-sub markdown-title(IO::Path:D $file --> Str:D) is export {
+#| Run pandoc with the given arguments, dieing on failure
+sub pandoc(*@args --> Str:D) {
     # Call into pandoc
-    my $pandoc = run , $file, :out, :err;
+    my $pandoc = run 'pandoc', @args, :out, :err;
 
     # Collect the output
     my $output = $pandoc.out.slurp: :close;
@@ -17,6 +14,17 @@ sub markdown-title(IO::Path:D $file --> Str:D) is export {
     die "Pandoc exited with {$pandoc.exitcode}\nout: $output\nerr: $stderr"
     unless $pandoc;
 
+    $output
+}
+
+#| Extract the title from a markdown document
+#|
+#| The title is the only top level header, will throw an error if there are
+#| multiple top level headers or are none
+sub markdown-title(IO::Path:D $file --> Str:D) is export {
+    # Collect the output
+    my $output = pandoc <-f gfm -t JSON>, $file;
+
     # Parse out output from pandoc, we are making an executive decision to trust
     # pandoc here, so we won't do any error handling for pandoc's output
     my %parsed = from-json $output;
@@ -27,12 +35,8 @@ sub markdown-title(IO::Path:D $file --> Str:D) is export {
         $v ~~ Associative && $v ~~ "Header"
     }
     my @headers = %parsed.grep(&is-header).grep(*[0] == 1);
-    if @headers.elems > 1 {
-        die "More than one top level header in $file";
-    };
-    if @headers.elems == 0 {
-        die "No top level headers in $file";
-    };
+    die "More than one top level header in $file" if @headers.elems > 1;
+    die "No top level headers in $file" if @headers.elems == 0;
 
     # Extract the header and process it into a string
     my @header = @headers[0][2].flat;
@@ -55,15 +59,38 @@ sub markdown-title(IO::Path:D $file --> Str:D) is export {
     return $title;
 }
 
+#| Use pandoc to extract the first paragraph of a markdown document
+sub markdown-first-paragraph(IO::Path:D $file --> Str:D) is export {
+    my $output = pandoc <-f gfm -t JSON>, $file;
+    my %parsed = from-json $output;
+    # Extract a list of paragraphs from the pandoc output
+    my sub is-para($v) {
+        $v ~~ Associative && $v ~~ 'Para'
+    }
+    my @paras = %parsed.grep(&is-para);
+    die "No paragraphs in markdown" if @paras.elems == 0;
+    my @para = @paras[0][0].flat;
+    # Proces it into a string
+    my $para = "";
+    for @para -> $component {
+        next unless $component ~~ Associative;
+        given $component {
+            when "Str" {
+                $para ~= $component;
+            }
+            when "Space" {
+                $para ~= " ";
+            }
+            default {
+                die "Invalid component type: $_";
+            }
+        }
+    }
+
+    $para
+}
+
 #| Use pandoc to render a markdown document to html
 sub markdown-to-html(IO::Path:D $file --> Str:D) is export {
-    # Call into pandoc
-    my $pandoc = run , $file, :out, :err;
-    # Collect the output
-    my $output = $pandoc.out.slurp: :close;
-    my $stderr = $pandoc.err.slurp: :close;
-    die "Pandoc exited with {$pandoc.exitcode}\nout: $output\nerr: $stderr"
-    unless $pandoc;
-
-    $output
+    pandoc <-f gfm>, $file
 }

From bc79817184e53a40c35f24736160bafde79c8ad6 Mon Sep 17 00:00:00 2001
From: Nathan McCarty 
Date: Tue, 4 Feb 2025 21:39:47 -0500
Subject: [PATCH 10/10] checkpoint

---
 lib/Config.rakumod | 17 ++++++++++++++++-
 resources/main.css | 23 ++++++++++++++++++++++-
 2 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/lib/Config.rakumod b/lib/Config.rakumod
index af798d0..5503bbb 100644
--- a/lib/Config.rakumod
+++ b/lib/Config.rakumod
@@ -30,16 +30,31 @@ method generate-head(Str:D $title, BlogMeta:D $meta, $description?) {
         # Inline our style sheets
         style %?RESOURCES.slurp;
         style %?RESOURCES.slurp;
-        # TODO: Add header links
     ];
 }
 
+method site-header(BlogMeta:D $meta) {
+    header :class, [
+        div :class, [
+           # TODO: Use a real image here
+           $meta.title
+        ];
+        div :class, [
+           $meta.tagline
+        ];
+        div :class, [
+
+        ]
+    ]
+}
+
 # TODO: Support GFM admonitions
 method generate-post(Post:D $post, BlogMeta:D $meta) {
     my $content = $post.render-html;
     my $head = self.generate-head($post.title, $meta, $post.description);
     my $body =
     body [
+       self.site-header: $meta;
        div :class, [
           $content
        ]
diff --git a/resources/main.css b/resources/main.css
index f6aeecd..602362d 100644
--- a/resources/main.css
+++ b/resources/main.css
@@ -9,12 +9,33 @@ body {
     display: flex;
     align-items: center;
     justify-content: center;
+    flex-direction: column;
+    gap: 1rem;
+}
+
+.site-header {
+    width: 60%;
+    display: block;
+    padding: 1rem;
+    border-radius: 1rem;
+    background-color: light-dark(#ffffff, #181818);
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+}
+
+.site-logo {
+    color: light-dark(#d6000c, #ed4a46);
+}
+
+.site-tagline {
 }
 
 .post-body {
     width: 66%;
     display: block;
-    padding: 3rem;
+    padding-left: 2rem;
+    padding-right: 2rem;
     border-radius: 1rem;
     background-color: light-dark(#ffffff, #181818);
     /* text-align: justify; */