From bbf7b4f6c8c0cad6bf6a1eee19fb2b0c766fd9e0 Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Fri, 7 Feb 2025 06:54:36 -0500 Subject: [PATCH 01/18] oops --- projects/Markdown/2025/01-Jan/AdventOfBugs.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/projects/Markdown/2025/01-Jan/AdventOfBugs.md b/projects/Markdown/2025/01-Jan/AdventOfBugs.md index 3242738..121f50e 100644 --- a/projects/Markdown/2025/01-Jan/AdventOfBugs.md +++ b/projects/Markdown/2025/01-Jan/AdventOfBugs.md @@ -65,6 +65,10 @@ calls to make interaction between lazy and strict code Just Work™: had initially thought I had discovered just _one_ compiler bug, but it turns out I also stumbled into an unrelated issue in the termination checker![^8] +I also found a convoluted hole in the case generator that I had to reach out to +the community for help minimizing (thank you +dunhamsteve!):[idris-lang/Idris2#3466](https://github.com/idris-lang/Idris2/issues/3466)[^9] + I broke the [pack](https://github.com/stefan-hoeck/idris2-pack) package manager by, apparently, being the first person to try and upload a library to `pack-db` with library code in literate files, in my @@ -96,3 +100,5 @@ ecosystem. subset of the language for which termination is a 'trivial' property, which includes most code I've written in the language so far, but not nearly all of it. + +[^9]: It's me, I'm the user on the discord From b55a4f2b41f1e2e11857df72c4c4862c1b797acc Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sat, 8 Feb 2025 19:19:17 -0500 Subject: [PATCH 02/18] post list subcommand --- blog | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/blog b/blog index d06ea67..159ab7f 100755 --- a/blog +++ b/blog @@ -6,6 +6,8 @@ use DB::BlogMeta; use DB::MarkdownPost; use DB::IdrisPost; +use Pretty::Table; + my %*SUB-MAIN-OPTS = :named-anywhere, :bundling, @@ -150,3 +152,46 @@ multi MAIN( my $db = read-db $db-dir; $db.render: $site-dir; } + +#| Provide a table of posts, in newest to oldest order +multi MAIN( + "post", + "list", + #| The path of the database directory + IO::Path(Str) :$db-dir = $default-db-dir, + #| The number of posts to show on a single page + Int :$per-page = 10; + #| The page number to show + Int :$page = 1; +) { + my $db = read-db $db-dir; + my @pages = + $db.sorted-posts.rotor($per-page, :partial); + my @page = @pages[$page - 1].flat; + my $table = Pretty::Table.new: + title => "Posts (page $page/{@pages.elems})", + field-names => ["ID", "Title", "Type"]; + for @page -> $pair { + my $id = $pair.key; + my $post = $pair.value; + # TODO: Terminal aware truncation + my $title = do if $post.title.chars > 50 { + "{$post.title.substr(0, 50)}..." + } else { + $post.title + }; + my $type = do given $post { + when MarkdownPost { + "md" + } + when IdrisPost { + "idr" + } + default { + "" + } + } + $table.add-row: [$id, $title, $type]; + } + say $table; +} From e05269b9a30a43957c4cb45cb878ef28b8df53dd Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sat, 8 Feb 2025 23:38:27 -0500 Subject: [PATCH 03/18] More blog commands --- blog | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/blog b/blog index 159ab7f..9918182 100755 --- a/blog +++ b/blog @@ -72,6 +72,7 @@ multi MAIN( #| Create a new markdown post multi MAIN( + "post", "new", "markdown", #| The path to the markdown file @@ -98,6 +99,7 @@ multi MAIN( #| Create a new idris post multi MAIN( + "post", "new", "idris", #| The path to the idris file @@ -127,7 +129,8 @@ multi MAIN( #| Update the last editied time on a post multi MAIN( - "touch", + "post", + "edit", #| The post id to touch Int:D $id, #| The path of the database file @@ -195,3 +198,59 @@ multi MAIN( } say $table; } + +#| Display information about a post +multi MAIN( + "post", + "info", + #| The id of the post to display information for + Int $id, + #| The path of the database directory + IO::Path(Str) :$db-dir = $default-db-dir, + #| Display all of the information and not just the primaries + Bool :$full, +) { + my $db = read-db $db-dir; + my $post = $db.posts{$id.Int}; + given $post { + say 'Title: ', .title; + say 'Type: ', .WHAT.^name; + say 'Source: ', .source.relative; + if .hidden { + say "Hidden"; + } + if .all-slugs { + if $full { + say 'Slugs: '; + for .all-slugs -> $slug { + say ' * ', $slug; + } + } else { + say 'Primary Slug: ', .all-slugs[*-1]; + } + } else { + say 'Slugs: []'; + } + if .tags { + say 'Tags:'; + for .tags -> $tag { + say ' * ', $tag; + } + } + given .posted-at { + say 'Posted At: ', .Date.Str, ' ', .hh-mm-ss; + } + if .edited-at { + if $full { + say 'Edits: '; + for .edited-at.sort.reverse { + say ' * ', .Date.Str, ' ', .hh-mm-ss; + } + } else { + given .edited-at.sort[*-1] { + say 'Last Edited At: ', .Date.Str, ' ', .hh-mm-ss; + } + } + } + } +} From 7a5ef790f3f90695e82abc58849e59c85fd52592 Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sat, 8 Feb 2025 23:58:10 -0500 Subject: [PATCH 04/18] post tag command --- blog | 28 ++++++++++++++++++++++++++++ lib/DB/Post.rakumod | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/blog b/blog index 9918182..759f669 100755 --- a/blog +++ b/blog @@ -254,3 +254,31 @@ multi MAIN( } } } + +#| Add or remove a tag to a post +multi MAIN( + "post", + "tag", + #| The id of the post to display information for + Int $id, + #| The tag to add/remove + Str $tag, + #| remove the tag instead of adding it + Bool :$remove, + #| The path of the database directory + IO::Path(Str) :$db-dir = $default-db-dir, +) { + my $db = read-db $db-dir; + my $post = $db.posts{$id.Int}; + if $remove { + die "Post did not have requested tag" + unless $tag ~~ any($post.tags); + my $index = $post.tags.first: $tag; + $post.tags.=grep(* ne $tag); + } else { + die "Post already had requested tag" + if $tag ~~ any($post.tags); + $post.tags.push: $tag; + } + $db.write: $db-dir; +} diff --git a/lib/DB/Post.rakumod b/lib/DB/Post.rakumod index 246753e..c1e98c7 100644 --- a/lib/DB/Post.rakumod +++ b/lib/DB/Post.rakumod @@ -37,7 +37,7 @@ DateTime:D @.edited-at #| An optional list of extra slugs to use for this post has Str:D @.slugs is json = []; #| An optional list of tags for this post -has Str:D @.tags is json = []; +has Str:D @.tags is rw is json = []; #| Should the post be hidden from the main list has Bool:D $.hidden is json is rw = False; From 73de17fc2c498c1ced6d0f645a29590eaa406c8e Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 00:52:53 -0500 Subject: [PATCH 05/18] Add content src to atom feed --- lib/Atom.rakumod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Atom.rakumod b/lib/Atom.rakumod index 0efd238..f058731 100644 --- a/lib/Atom.rakumod +++ b/lib/Atom.rakumod @@ -39,6 +39,8 @@ sub post-to-item(BlogMeta:D $meta, Int:D $id, Post:D $post --> XML::Element) { $xml.append: $author; $xml.append: XML::Element.new(:name, :attribs({:href($link), :rel})); + $xml.append: + XML::Element.new(:name, :attribs({:src($link), :type})); $xml.append: XML::Element.new(:name, :nodes([$desc])) if $desc; if $post.tags { From 49502df4165b24a4efb29f7e6b8a7c3df0c610b2 Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 01:25:33 -0500 Subject: [PATCH 06/18] Tweaks --- lib/Atom.rakumod | 1 + lib/DB/BlogMeta.rakumod | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Atom.rakumod b/lib/Atom.rakumod index f058731..caee5d8 100644 --- a/lib/Atom.rakumod +++ b/lib/Atom.rakumod @@ -39,6 +39,7 @@ sub post-to-item(BlogMeta:D $meta, Int:D $id, Post:D $post --> XML::Element) { $xml.append: $author; $xml.append: XML::Element.new(:name, :attribs({:href($link), :rel})); + # TODO: Actually embed the content $xml.append: XML::Element.new(:name, :attribs({:src($link), :type})); $xml.append: diff --git a/lib/DB/BlogMeta.rakumod b/lib/DB/BlogMeta.rakumod index e6ccf5d..a5a1fd2 100644 --- a/lib/DB/BlogMeta.rakumod +++ b/lib/DB/BlogMeta.rakumod @@ -23,8 +23,8 @@ has Str:D $.base-url is required; #| Return the base url, but substitute it out if the test environment variable #| is set method get-base-url(--> Str:D) { - if %*ENV { - "http://localhost:8080" + if %*ENV { + "http://localhost:8000" } else { $!base-url } From 0571a16dabd71c7ea68b71d449748b6c7557c6e7 Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 01:50:37 -0500 Subject: [PATCH 07/18] Add series data structure --- lib/Config.rakumod | 1 + lib/DB.rakumod | 23 ++++++++++++++++++++++- lib/DB/Series.rakumod | 15 +++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 lib/DB/Series.rakumod diff --git a/lib/Config.rakumod b/lib/Config.rakumod index 68c8172..a16d9f4 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -24,6 +24,7 @@ method generate-post(Post:D $post, BlogMeta:D $meta) { ] ] ]; + # TODO: Setup Comments # TODO: Setup footer # my $footer; diff --git a/lib/DB.rakumod b/lib/DB.rakumod index dac655d..d977061 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -9,6 +9,7 @@ use XML; use XQ; use DB::Post; +use DB::Series; use DB::BlogMeta; use DB::MarkdownPost; use DB::IdrisPost; @@ -25,6 +26,8 @@ class PostDB { #| A mapping from post ids to posts # has %.posts is Posts; has %.posts{Int} of PostTypes = %(); + #| A mapping from series ids to series + has %.series{Int} of Series = %(); #| The post id to use for placeholder posts has Int $.placeholder-id = 0; @@ -57,9 +60,11 @@ class PostDB { #| Write a database to a directory method write(IO::Path:D $dir) { my $posts-dir = $dir.add('posts/'); + my $series-dir = $dir.add('series/'); # Make sure directory structrue exists mkdir $dir unless $dir.e; mkdir $posts-dir unless $posts-dir.e; + mkdir $series-dir unless $series-dir.e; # Write out metadata # TODO: Track changes and only write changed files $dir.add('meta.json').spurt: $!meta.to-json(:sorted-keys); @@ -67,6 +72,10 @@ class PostDB { for %!posts.kv -> $key, $value { $posts-dir.add("$key.json").spurt: $value.to-json(:sorted-keys); } + # Write out the series + for %!series.kv -> $key, $value { + $series-dir.add("$key.json").spurt: $value.to-json(:sorted-keys); + } } #| Render the site to the provided output directory @@ -114,6 +123,7 @@ class PostDB { $tags-dir.add("$tag.html").spurt: $config.generate-tag-page(self, $tag); } + # TODO: Generate the series pages # Render the rss/atom feed my $atom-path = $out-dir.add('atom.xml'); my $atom = posts-to-atom self; @@ -135,6 +145,7 @@ class PostDB { #| Read the database out of a directory sub read-db(IO::Path:D $dir --> PostDB:D) is export { my $posts-dir = $dir.add('posts/'); + my $series-dir = $dir.add('series/'); die "DB directory does not exist" unless $dir.e; die "posts directory does not exist" unless $posts-dir.e; # Read metadata @@ -160,6 +171,16 @@ sub read-db(IO::Path:D $dir --> PostDB:D) is export { } } } + # Read series + my %series{Int} of PostTypes = %(); + # For backwards compatability, the series directory is optional. It will be + # created on the next db operation, but we don't need it to read the site + if $series-dir.e { + for dir $series-dir -> $series { + my $id = $series.extension("").basename.Int; + %series{$id} = Series.from-json: $series.slurp; + } + } # Build db structure - PostDB.new: meta => $meta, posts => %posts + PostDB.new: meta => $meta, posts => %posts, series => %series } diff --git a/lib/DB/Series.rakumod b/lib/DB/Series.rakumod new file mode 100644 index 0000000..2241215 --- /dev/null +++ b/lib/DB/Series.rakumod @@ -0,0 +1,15 @@ +use v6.e.PREVIEW; + +use JSON::Class:auth; + +#| A plain markdown post +unit class Series is json(:pretty); + +#| The title of a series +has Str:D $.title is required; + +#| The description of a series +has Str:D $.desc is required; + +#| The ids of the posts in the series, in series order +has Int:D @.post-ids = []; From d170a2fccfa2a5e0e326515d83b720b122821fec Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 02:12:14 -0500 Subject: [PATCH 08/18] Series commands --- blog | 100 ++++++++++++++++++++++++++++++++++++++++++ lib/DB.rakumod | 18 +++++++- lib/DB/Series.rakumod | 2 +- 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/blog b/blog index 759f669..6784af9 100755 --- a/blog +++ b/blog @@ -3,6 +3,7 @@ use v6.e.PREVIEW; use DB; use DB::BlogMeta; +use DB::Series; use DB::MarkdownPost; use DB::IdrisPost; @@ -282,3 +283,102 @@ multi MAIN( } $db.write: $db-dir; } + +#| Create a new series +multi MAIN( + "series", + "new", + #| The path of the database file + IO::Path(Str) :$db-dir = $default-db-dir, +) { + my $db = read-db $db-dir; + say 'Series Title: '; + my $title = get; + say 'Series Description: '; + my $desc = get; + + my $series = Series.new: + title => $title, desc => $desc; + my $id = $db.insert-series: $series; + say 'Series inserted with id ', $id; + + $db.write: $db-dir; +} + + +#| Provide a table of series +multi MAIN( + "series", + "list", + #| The path of the database directory + IO::Path(Str) :$db-dir = $default-db-dir, + #| The number of items to show on a single page + Int :$per-page = 10; + #| The page number to show + Int :$page = 1; +) { + my $db = read-db $db-dir; + my @pages = + $db.series.sort(*.key).rotor($per-page, :partial); + my @page = @pages[$page - 1].flat; + my $table = Pretty::Table.new: + title => "Series (page $page/{@pages.elems})", + field-names => ["ID", "Title", "Desc"]; + for @page -> $pair { + my $id = $pair.key; + my $series = $pair.value; + # TODO: Terminal aware truncation + my $title = do if $series.title.chars > 40 { + "{$series.title.substr(0, 50)}..." + } else { + $series.title + }; + my $desc = do if $series.desc.chars > 40 { + "{$series.desc.substr(0, 50)}..." + } else { + $series.desc + }; + $table.add-row: [$id, $title, $desc]; + } + say $table; +} + +#| Display the contents of a series +multi MAIN( + "series", + "info", + #| The id of the series to display + Int $id, + #| The path of the database directory + IO::Path(Str) :$db-dir = $default-db-dir, +) { + my $db = read-db $db-dir; + my $series = $db.series{$id.Int}; + say 'Title: ', $series.title; + say 'Description:'; + for $series.desc.lines -> $line { + say ' ', $line; + } + say 'Posts:'; + for $series.post-ids -> $post-id { + my $post = $db.posts{$post-id}; + say ' * ', $post-id, ': ', $post.title; + } +} + +#| Add a post to a series +multi MAIN( + "series", + "add", + #| The id of the series to add to + Int $series-id, + #| The id of the post to add + Int $post-id, + #| The path of the database directory + IO::Path(Str) :$db-dir = $default-db-dir, +) { + my $db = read-db $db-dir; + my $series = $db.series{$series-id.Int}; + $series.post-ids.push: $post-id.Int; + $db.write: $db-dir; +} diff --git a/lib/DB.rakumod b/lib/DB.rakumod index d977061..042f1a9 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -40,6 +40,15 @@ class PostDB { } } + #| Get the next unused series ID + method next-series-id(--> Int) { + if %!series.elems > 0 { + %!series.keys.max + 1 + } else { + 0 + } + } + #| Insert a new post to the DB, returning its id method insert-post(PostTypes $post --> Int) { my $id = self.next-post-id; @@ -47,6 +56,13 @@ class PostDB { $id } + #| Insert a new series to the DB, returning its id + method insert-series(Series:D $series --> Int) { + my $id = self.next-series-id; + %!series{$id} = $series; + $id + } + #| Initialize a new database method init(BlogMeta:D $meta --> PostDB:D) { my %posts{Int} of PostTypes = %(); @@ -172,7 +188,7 @@ sub read-db(IO::Path:D $dir --> PostDB:D) is export { } } # Read series - my %series{Int} of PostTypes = %(); + my %series{Int} of Series:D = %(); # For backwards compatability, the series directory is optional. It will be # created on the next db operation, but we don't need it to read the site if $series-dir.e { diff --git a/lib/DB/Series.rakumod b/lib/DB/Series.rakumod index 2241215..4379cae 100644 --- a/lib/DB/Series.rakumod +++ b/lib/DB/Series.rakumod @@ -12,4 +12,4 @@ has Str:D $.title is required; has Str:D $.desc is required; #| The ids of the posts in the series, in series order -has Int:D @.post-ids = []; +has Int:D @.post-ids is rw = []; From 4793d660e268c3178443d0784390c14df7bad59c Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 02:43:22 -0500 Subject: [PATCH 09/18] Add series links --- lib/Config.rakumod | 9 +++++---- lib/DB.rakumod | 2 +- lib/DB/Series.rakumod | 9 +++++++++ lib/Render/Head.rakumod | 1 + lib/Render/Post.rakumod | 39 ++++++++++++++++++++++++++++++++++++--- resources/main.css | 4 ++-- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/lib/Config.rakumod b/lib/Config.rakumod index a16d9f4..dafed14 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -11,14 +11,15 @@ use DB::Post; unit class Config; # TODO: Support GFM admonitions -method generate-post(Post:D $post, BlogMeta:D $meta) { +method generate-post(Int:D $id, Post:D $post, $db) { + my $meta = $db.meta; my $content = $post.render-html; my $head = generate-head($meta, $post.title, $post.description); my $body = body [ site-header $meta; article :class, [ - post-header $post; + post-header $id, $post, $db; div :class, [ $content; ] @@ -46,7 +47,7 @@ method generate-blurb(Int:D $id, $db) { h2 $post.title; ]; ]; - post-info $post; + post-info $id, $post, $db; if $desc ~~ Str:D { div :class, [ p $post.description; @@ -117,7 +118,7 @@ method generate-tag-blurb($db, $tag, $limit?) { a :href($link), span [ h3 $post.title; ]; - post-info $post; + post-info $id, $post, $db; if $desc ~~ Str:D { div :class, [ p $post.description; diff --git a/lib/DB.rakumod b/lib/DB.rakumod index 042f1a9..f092775 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -112,7 +112,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, $!meta; + my $html = $config.generate-post: $id, $post, self; my $id-path = $by-id.add: "$id.html"; $id-path.spurt: $html; for $post.all-slugs -> $slug { diff --git a/lib/DB/Series.rakumod b/lib/DB/Series.rakumod index 4379cae..6510a7b 100644 --- a/lib/DB/Series.rakumod +++ b/lib/DB/Series.rakumod @@ -13,3 +13,12 @@ has Str:D $.desc is required; #| The ids of the posts in the series, in series order has Int:D @.post-ids is rw = []; + +#| Returns true if this series contains the given post id +method contains-post(Int:D $post-id --> Bool:D) { + if $post-id ~~ any @!post-ids { + True + } else { + False + } +} diff --git a/lib/Render/Head.rakumod b/lib/Render/Head.rakumod index e7eb435..5eaf19c 100644 --- a/lib/Render/Head.rakumod +++ b/lib/Render/Head.rakumod @@ -61,6 +61,7 @@ sub site-header(BlogMeta:D $meta) is export { header-link 'Index', '/index.html', 'home'; header-link 'Archive', '/archive.html', 'archive'; header-link 'Tags', '/tags.html', 'purchase-tag-alt'; + header-link 'Series', '/series.html', 'book'; header-link 'About', '/about.html', 'info-circle'; header-link 'Feed', '/atom.xml', 'rss'; ]; diff --git a/lib/Render/Post.rakumod b/lib/Render/Post.rakumod index 826582e..637217f 100644 --- a/lib/Render/Post.rakumod +++ b/lib/Render/Post.rakumod @@ -3,6 +3,7 @@ unit module Render::Post; use Render::Util; use DB::Post; +use DB::Series; use HTML::Functional; @@ -81,20 +82,52 @@ sub post-tags(Post:D $post) is export { } } -sub post-info(Post:D $post) is export { +sub series-tag(Int:D $post-id, Int:D $series-id, Series:D $series) is export { + span :class, [ + a :href("/series/$series-id.html"), [ + icon 'book'; + ' '; + span :class, [ + $series.title; + ' '; + '('; + ($series.post-ids.first($post-id, :k) + 1).Str; + '/'; + $series.post-ids.elems.Str; + ')'; + ] + ] + ] +} + +sub post-series-tags(Int:D $post-id, Post:D $post, $db) is export { + # Find all the series this post is in + my @series = $db.series.grep(*.value.contains-post: $post-id); + if @series { + div :class, + @series.map(-> $pair { + series-tag $post-id, $pair.key, $pair.value + }); + } else { + [] + } +} + +sub post-info(Int:D $id, Post:D $post, $db) is export { div :class, [ post-date $post; post-edit $post; post-read-time $post; post-tags $post; + post-series-tags $id, $post, $db; ]; } -sub post-header(Post:D $post) is export { +sub post-header(Int:D $id, Post:D $post, $db) is export { header :class, [ div :class, [ h1 $post.title; ]; - post-info $post; + post-info $id, $post, $db; ] } diff --git a/resources/main.css b/resources/main.css index 4b54d5e..87dc6ac 100644 --- a/resources/main.css +++ b/resources/main.css @@ -65,10 +65,10 @@ body, .post { flex-wrap: wrap; margin-top: var(--box-margin-vert); } -.header-links > a > span { +.header-links > a > span, .post-series-tag > a > span { text-decoration: underline; } -.header-links > a { +.header-links > a, .post-series-tag > a { text-decoration: none; } From 5c569780dce3ec15ea2907582308a58a0b97707b Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 03:10:21 -0500 Subject: [PATCH 10/18] Fix tag formatting issues --- lib/Config.rakumod | 18 +++++++++++++----- lib/Render/Post.rakumod | 2 +- resources/main.css | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/Config.rakumod b/lib/Config.rakumod index dafed14..1c63d24 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -10,6 +10,14 @@ use DB::Post; unit class Config; +sub show-html($html) { + my $out = "$html"; + # Work around HTML::Functional automatically putting newlines between tags + $out ~~ s:g/'' \v+ ''/<\/i>/; + $out ~~ s:g/\v+ ''/<\/a>/; + $out +} + # TODO: Support GFM admonitions method generate-post(Int:D $id, Post:D $post, $db) { my $meta = $db.meta; @@ -34,7 +42,7 @@ method generate-post(Int:D $id, Post:D $post, $db) { $body ]; - "$html" + show-html $html } method generate-blurb(Int:D $id, $db) { @@ -81,7 +89,7 @@ method generate-index($db) { $body ]; - "$html" + show-html $html } method generate-archive($db) { @@ -106,7 +114,7 @@ method generate-archive($db) { $body ]; - "$html" + show-html $html } method generate-tag-blurb($db, $tag, $limit?) { @@ -164,7 +172,7 @@ method generate-tags-page($db, @tags) { $body ]; - "$html" + show-html $html } method generate-tag-page($db, $tag) { @@ -180,5 +188,5 @@ method generate-tag-page($db, $tag) { $body ]; - "$html" + show-html $html } diff --git a/lib/Render/Post.rakumod b/lib/Render/Post.rakumod index 637217f..1724c74 100644 --- a/lib/Render/Post.rakumod +++ b/lib/Render/Post.rakumod @@ -63,7 +63,7 @@ sub post-tag(Str:D $tag) is export { span :class, [ a :href("/tags/$tag.html"), [ icon 'hash'; - $tag; + span $tag; ] ] } diff --git a/resources/main.css b/resources/main.css index 87dc6ac..36a1245 100644 --- a/resources/main.css +++ b/resources/main.css @@ -65,10 +65,10 @@ body, .post { flex-wrap: wrap; margin-top: var(--box-margin-vert); } -.header-links > a > span, .post-series-tag > a > span { +.header-links > a > span, .post-series-tag > a > span, .post-tag > a > span { text-decoration: underline; } -.header-links > a, .post-series-tag > a { +.header-links > a, .post-series-tag > a, .post-tag > a { text-decoration: none; } From 8ce16daa58785be16bf588dc948886e1559e075d Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 03:13:28 -0500 Subject: [PATCH 11/18] Only display post date in blurb Still dispay the datetime in the hover --- lib/Render/Post.rakumod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Render/Post.rakumod b/lib/Render/Post.rakumod index 1724c74..faed911 100644 --- a/lib/Render/Post.rakumod +++ b/lib/Render/Post.rakumod @@ -18,9 +18,9 @@ sub post-date(Post:D $post) is export { ); div :class, :title("Posted At $timestamp"), [ - icon 'time'; + icon 'calendar'; ' '; - $timestamp + $datetime.Date.Str ] } From 35bdb1c86c0d0bf7c15b17a8d70d717cf92c7e1b Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 03:20:09 -0500 Subject: [PATCH 12/18] Style up the post info bubbles a bit more --- resources/colors.css | 3 +++ resources/main.css | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/resources/colors.css b/resources/colors.css index 5974f21..77a1a86 100644 --- a/resources/colors.css +++ b/resources/colors.css @@ -71,6 +71,9 @@ a:visited { .post-body h2, .post-body h3, .post-body h4 { color: var(--fg-1); } +.post-info > * { + background-color: var(--bg-2); +} blockquote { background-color: var(--bg-1); } diff --git a/resources/main.css b/resources/main.css index 36a1245..5e890fc 100644 --- a/resources/main.css +++ b/resources/main.css @@ -128,6 +128,10 @@ body, .post { flex-direction: column; box-sizing: border-box; } +.post-info > * { + padding: 0.25em; + border-radius: 0.25em; +} /* TODO: Nice fancy blockquotes */ blockquote { From 471603ae021c4af30f2863ce0d3febfafc0f21dd Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 03:22:30 -0500 Subject: [PATCH 13/18] Change Archive link to All Posts --- lib/Render/Head.rakumod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Render/Head.rakumod b/lib/Render/Head.rakumod index 5eaf19c..c43c72b 100644 --- a/lib/Render/Head.rakumod +++ b/lib/Render/Head.rakumod @@ -59,7 +59,7 @@ sub site-header(BlogMeta:D $meta) is export { ]; div :class, [ header-link 'Index', '/index.html', 'home'; - header-link 'Archive', '/archive.html', 'archive'; + header-link 'All Posts', '/archive.html', 'archive'; header-link 'Tags', '/tags.html', 'purchase-tag-alt'; header-link 'Series', '/series.html', 'book'; header-link 'About', '/about.html', 'info-circle'; From 7cf4827d0c5c7cdf33e97fd9d09fe43b89a9cafb Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 03:25:38 -0500 Subject: [PATCH 14/18] Change lasted edited at to date only in bubble --- lib/Render/Post.rakumod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Render/Post.rakumod b/lib/Render/Post.rakumod index faed911..a196edf 100644 --- a/lib/Render/Post.rakumod +++ b/lib/Render/Post.rakumod @@ -38,7 +38,7 @@ sub post-edit(Post:D $post) is export { div :class, :title("Last Edited At $timestamp"), [ icon 'edit'; ' '; - $timestamp + $datetime.Date.Str ] } From baf8d6556bb1b9ad92cf5e01bbb0162a59056d1c Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 05:28:59 -0500 Subject: [PATCH 15/18] Generate series pages --- lib/Config.rakumod | 33 +----------- lib/DB.rakumod | 8 +++ lib/DB/Series.rakumod | 11 ++++ lib/Render/Post.rakumod | 21 ++++++++ lib/Render/Series.rakumod | 105 ++++++++++++++++++++++++++++++++++++++ lib/Render/Util.rakumod | 9 ++++ resources/colors.css | 7 +-- resources/main.css | 15 +++--- 8 files changed, 168 insertions(+), 41 deletions(-) create mode 100644 lib/Render/Series.rakumod diff --git a/lib/Config.rakumod b/lib/Config.rakumod index 1c63d24..767bd1e 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -10,14 +10,6 @@ use DB::Post; unit class Config; -sub show-html($html) { - my $out = "$html"; - # Work around HTML::Functional automatically putting newlines between tags - $out ~~ s:g/'' \v+ ''/<\/i>/; - $out ~~ s:g/\v+ ''/<\/a>/; - $out -} - # TODO: Support GFM admonitions method generate-post(Int:D $id, Post:D $post, $db) { my $meta = $db.meta; @@ -45,34 +37,13 @@ method generate-post(Int:D $id, Post:D $post, $db) { show-html $html } -method generate-blurb(Int:D $id, $db) { - my $post = $db.posts{$id}; - my $desc = $post.description; - my $link = post-link $id, $post; - div :class, [ - div :class, [ - a :href($link), span [ - h2 $post.title; - ]; - ]; - post-info $id, $post, $db; - if $desc ~~ Str:D { - div :class, [ - p $post.description; - ]; - } else { - [] - } - ] -} - method generate-index($db) { my @most-recent = $db.sorted-posts .head(10) .grep(!*.value.hidden) .map(-> $pair { - self.generate-blurb: $pair.key, $db + generate-blurb $pair.key, $db }); my $head = generate-head($db.meta); @@ -97,7 +68,7 @@ method generate-archive($db) { $db.sorted-posts .grep(!*.value.hidden) .map(-> $pair { - self.generate-blurb: $pair.key, $db + generate-blurb $pair.key, $db }); my $head = generate-head($db.meta); diff --git a/lib/DB.rakumod b/lib/DB.rakumod index f092775..4fc9288 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -14,6 +14,7 @@ use DB::BlogMeta; use DB::MarkdownPost; use DB::IdrisPost; use DB::PlaceholderPost; +use Render::Series; use Atom; use Config; @@ -140,6 +141,13 @@ class PostDB { $config.generate-tag-page(self, $tag); } # TODO: Generate the series pages + my $series-dir = $out-dir.add('series/'); + mkdir $series-dir unless $series-dir.e; + for %!series.kv -> $key, $value { + $series-dir.add("$key.html").spurt: + series-page($key, self); + } + # TODO: Generate the main series page # Render the rss/atom feed my $atom-path = $out-dir.add('atom.xml'); my $atom = posts-to-atom self; diff --git a/lib/DB/Series.rakumod b/lib/DB/Series.rakumod index 6510a7b..59fd964 100644 --- a/lib/DB/Series.rakumod +++ b/lib/DB/Series.rakumod @@ -22,3 +22,14 @@ method contains-post(Int:D $post-id --> Bool:D) { False } } + +#| Returns the date of the lastest post +method latest-post($db) { + my @posts = @!post-ids.map(-> $i {$db.posts{$i}}); + if @posts { + my $most-recent-post = @posts.max(*.posted-at); + $most-recent-post.posted-at + } else { + Nil + } +} diff --git a/lib/Render/Post.rakumod b/lib/Render/Post.rakumod index a196edf..8bb6f97 100644 --- a/lib/Render/Post.rakumod +++ b/lib/Render/Post.rakumod @@ -131,3 +131,24 @@ sub post-header(Int:D $id, Post:D $post, $db) is export { post-info $id, $post, $db; ] } + +sub generate-blurb(Int:D $id, $db) is export { + my $post = $db.posts{$id}; + my $desc = $post.description; + my $link = post-link $id, $post; + div :class, [ + div :class, [ + a :href($link), span [ + h2 $post.title; + ]; + ]; + post-info $id, $post, $db; + if $desc ~~ Str:D { + div :class, [ + p $post.description; + ]; + } else { + [] + } + ] +} diff --git a/lib/Render/Series.rakumod b/lib/Render/Series.rakumod new file mode 100644 index 0000000..0db9a55 --- /dev/null +++ b/lib/Render/Series.rakumod @@ -0,0 +1,105 @@ +use v6.e.PREVIEW; +unit module Render::Series; + +use Render::Util; +use Render::Head; +use Render::Post; +use DB::Post; +use DB::Series; + +use HTML::Functional; + +sub series-date(Series:D $series, $db) is export { + my $datetime = $series.latest-post: $db; + if $datetime { + my $timestamp = sprintf( + "%s %02d:%02d%s", + $datetime.yyyy-mm-dd, + ($datetime.hour % 12) || 12, + $datetime.minute, + $datetime.hour < 12 ?? 'am' !! 'pm' + ); + + div :class, :title("Latest post made at $timestamp"), [ + icon 'calendar'; + ' '; + $datetime.Date.Str + ] + } else { + [] + } +} + +sub series-read-time(Series:D $series, $db) is export { + my @readtimes = $series.post-ids.map(-> $i {$db.posts{$i}.readtimes}); + my ($slow, $average, $fast) = 0, 0, 0; + for @readtimes -> ($s, $a, $f) { + $slow += $s; + $average += $a; + $fast += $f; + } + div :class, :title, [ + icon 'timer'; + ' '; + mins-to-string $slow; + ' '; + '/'; + ' '; + mins-to-string $average; + ' '; + '/'; + ' '; + mins-to-string $fast; + ] +} + +sub series-count(Series:D $series, $db) is export { + my $count = $series.post-ids.elems; + div :class, :title("Series has $count articles"), [ + icon 'add-to-queue'; + ' '; + "$count articles"; + ] +} + +sub series-info(Series:D $series, $db) is export { + div :class, [ + series-date $series, $db; + series-read-time $series, $db; + series-count $series, $db; + ] +} + +sub series-header(Series:D $series, $db) is export { + header :class, [ + div :class, [ + h1 $series.title; + ]; + div :class, [ + p $series.desc; + ]; + series-info $series, $db; + ] +} + +sub series-page(Int:D $series-id, $db) is export { + my $meta = $db.meta; + my $series = $db.series{$series-id}; + my $head = generate-head($meta, $series.title, $series.desc); + my $body = + body [ + site-header $meta; + article :class, [ + series-header $series, $db; + div :class, + $series.post-ids.map(*.&generate-blurb($db)); + ] + ]; + + my $html = html :lang, [ + $head; + $body + ]; + + show-html $html; +} diff --git a/lib/Render/Util.rakumod b/lib/Render/Util.rakumod index 05630a7..79ece58 100644 --- a/lib/Render/Util.rakumod +++ b/lib/Render/Util.rakumod @@ -5,6 +5,15 @@ use DB::Post; use HTML::Functional; +sub show-html($html) is export { + my $out = "$html"; + # Work around HTML::Functional automatically putting newlines between tags + $out ~~ s:g/'' \v+ ''/<\/i>/; + $out ~~ s:g/\v+ ''/<\/a>/; + $out ~~ s:g/',' \v+ ' * { +.post-info > *, .series-info > * { background-color: var(--bg-2); } blockquote { diff --git a/resources/main.css b/resources/main.css index 5e890fc..7a78e3f 100644 --- a/resources/main.css +++ b/resources/main.css @@ -27,13 +27,13 @@ } /* Main Body and Post Flexboxs */ -body, .post { +body, .post, .series { display: flex; flex-direction: column; align-items: center; gap: var(--box-gap); } -.post { +.post, .series { width: 100%; } @@ -75,7 +75,7 @@ body, .post { /* Style the post header, body, and blurbs */ /* TODO: Style footnotes and get footnote hover working */ -.post-header, .post-body { +.post-header, .post-body, .series-header { display: flex; flex-direction: column; align-items: center; @@ -90,11 +90,11 @@ body, .post { margin: auto var(--box-margin-horz); align-self: stretch; } -.post-title h1 { +.post-title h1, .series-title h1 { margin-top: 0px; margin-bottom: 0px; } -.post-info { +.post-info, .series-info { display: flex; flex-direction: row; align-items: center; @@ -108,7 +108,7 @@ body, .post { .post-body h2, .post-body h3, .post-body h4 { text-align: center; } -.post-blurbs { +.post-blurbs, .series-blurbs { display: flex; flex-direction: column; align-items: center; @@ -116,6 +116,7 @@ body, .post { max-width: var(--content-width); padding: var(--box-padding-vert) var(--box-padding-horz); border-radius: var(--box-radius); + box-sizing: border-box; } .post-blurb { width: 100%; @@ -128,7 +129,7 @@ body, .post { flex-direction: column; box-sizing: border-box; } -.post-info > * { +.post-info > *, .series-info > * { padding: 0.25em; border-radius: 0.25em; } From 109165b80e0a1dbcb2436ebe6f699cbd9f015e5a Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 05:51:52 -0500 Subject: [PATCH 16/18] Generate series list page --- lib/DB.rakumod | 6 ++++-- lib/Render/Series.rakumod | 39 +++++++++++++++++++++++++++++++++++++++ resources/colors.css | 6 +++--- resources/main.css | 6 +++--- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/lib/DB.rakumod b/lib/DB.rakumod index 4fc9288..190b4a3 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -140,14 +140,16 @@ class PostDB { $tags-dir.add("$tag.html").spurt: $config.generate-tag-page(self, $tag); } - # TODO: Generate the series pages + # Generate the series pages my $series-dir = $out-dir.add('series/'); mkdir $series-dir unless $series-dir.e; for %!series.kv -> $key, $value { $series-dir.add("$key.html").spurt: series-page($key, self); } - # TODO: Generate the main series page + # Generate the main series page + $out-dir.add('series.html').spurt: + series-list-page self; # Render the rss/atom feed my $atom-path = $out-dir.add('atom.xml'); my $atom = posts-to-atom self; diff --git a/lib/Render/Series.rakumod b/lib/Render/Series.rakumod index 0db9a55..add11be 100644 --- a/lib/Render/Series.rakumod +++ b/lib/Render/Series.rakumod @@ -103,3 +103,42 @@ sub series-page(Int:D $series-id, $db) is export { show-html $html; } + +sub series-blurb(Int:D $id, Series:D $series, $db) { + my $link = "/series/$id.html"; + div :class, [ + div :class, [ + a :href($link), span [ + h2 $series.title; + ]; + p $series.desc; + ]; + series-info $series, $db; + ] +} + +sub series-list-page($db) is export { + my @series = $db.series.sort(*.value.latest-post: $db); + my @series-blurbs = (); + for @series -> $pair { + my $id = $pair.key; + my $series = $pair.value; + @series-blurbs.push: + series-blurb $id, $series, $db; + } + + my $head = generate-head($db.meta); + my $body = body [ + site-header $db.meta; + div :class, [ + h1 "All Series" + ], @series-blurbs; + ]; + + my $html = html :lang, [ + $head; + $body; + ]; + + show-html $html; +} diff --git a/resources/colors.css b/resources/colors.css index 2880c7c..42db252 100644 --- a/resources/colors.css +++ b/resources/colors.css @@ -54,10 +54,10 @@ a:visited { color: var(--dim-0); } .post-body, .post-header, .post-blurbs, .tags, .tags .tag-blurb-post, -.series-header, .series-blurbs { +.series-header, .series-blurbs, .series-list { background-color: var(--bg-0); } -.post-blurb, .tags .tag-blurb { +.post-blurb, .tags .tag-blurb, .series-list-blurb { background-color: var(--bg-1); } :not(.tags) .tag-blurb { @@ -66,7 +66,7 @@ a:visited { :not(.tags) .tag-blurb-post { background-color: var(--bg-1); } -.post-title, .post-blurbs h1, .series-header h1 { +.post-title, .post-blurbs h1, .series-header h1, .series-list h1 { color: var(--green); } .post-body h2, .post-body h3, .post-body h4 { diff --git a/resources/main.css b/resources/main.css index 7a78e3f..3617870 100644 --- a/resources/main.css +++ b/resources/main.css @@ -33,7 +33,7 @@ body, .post, .series { align-items: center; gap: var(--box-gap); } -.post, .series { +.post, .series, .series-list { width: 100%; } @@ -108,7 +108,7 @@ body, .post, .series { .post-body h2, .post-body h3, .post-body h4 { text-align: center; } -.post-blurbs, .series-blurbs { +.post-blurbs, .series-blurbs, .series-list { display: flex; flex-direction: column; align-items: center; @@ -118,7 +118,7 @@ body, .post, .series { border-radius: var(--box-radius); box-sizing: border-box; } -.post-blurb { +.post-blurb, .series-list-blurb { width: 100%; display: block; border-radius: var(--box-radius); From bdceab76526f90787971f4932db04dc7c71e8040 Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 06:37:26 -0500 Subject: [PATCH 17/18] Start of variadic printf post --- db/posts/5.json | 14 ++++++ db/series/0.json | 7 +++ projects/Idris/Idris.ipkg | 1 + .../Idris/src/LessMacrosMoreTypes/Printf.md | 45 +++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 db/posts/5.json create mode 100644 db/series/0.json create mode 100644 projects/Idris/src/LessMacrosMoreTypes/Printf.md diff --git a/db/posts/5.json b/db/posts/5.json new file mode 100644 index 0000000..a689e5e --- /dev/null +++ b/db/posts/5.json @@ -0,0 +1,14 @@ +{ + "edited-at": [ + ], + "hidden": false, + "idris": true, + "ipkg": "/home/nathan/Projects/Blog/projects/Idris/Idris.ipkg", + "posted-at": "2025-02-09T06:23:37.499533-05:00", + "slugs": [ + ], + "source": "/home/nathan/Projects/Blog/projects/Idris/src/LessMacrosMoreTypes/Printf.md", + "tags": [ + "idris" + ] +} \ No newline at end of file diff --git a/db/series/0.json b/db/series/0.json new file mode 100644 index 0000000..8661aef --- /dev/null +++ b/db/series/0.json @@ -0,0 +1,7 @@ +{ + "desc": "Macros are annoying, but an unfortunate fact of life in many programming languages. Especially in languages with nominally \"strong\" type systems, like Rust, macros are quite frequently needed to work around the type system to avoid needless repetition when consuming an API, generate formulaic boilerplate that only exists to please the type system, or work around the lack of variadic functions for things like printf. Lets explore the ways we can use dependently typed constructs to eliminate the need for such macros.", + "post-ids": [ + 5 + ], + "title": "Less Macros, More Types" +} \ No newline at end of file diff --git a/projects/Idris/Idris.ipkg b/projects/Idris/Idris.ipkg index 152ad1b..8331301 100644 --- a/projects/Idris/Idris.ipkg +++ b/projects/Idris/Idris.ipkg @@ -18,6 +18,7 @@ authors = "Nathan McCarty" -- modules to install modules = Idris , Posts.HelloWorld + , LessMacrosMoreTypes.Printf -- main file (i.e. file to load at REPL) -- main = diff --git a/projects/Idris/src/LessMacrosMoreTypes/Printf.md b/projects/Idris/src/LessMacrosMoreTypes/Printf.md new file mode 100644 index 0000000..ae9b2e8 --- /dev/null +++ b/projects/Idris/src/LessMacrosMoreTypes/Printf.md @@ -0,0 +1,45 @@ +# Type Safe Variadic printf + +```idris hide +module LessMacrosMoreTypes.Printf + +%default total +``` + +While C can provide convenient string formatting by having hideously memory +unsafe variadics, and dynamic languages, like python, can do the same while +being memory safe by not being type safe, many type safe languages, such as +Rust, are forced to provide such functionality through the use of a macro. +Dependently typed languages, like Idris, can provide a printf like formatting +interface, while maintaining both memory and type saftey, without the need for +the macro. We will explore this by implementing a simplified version of `printf` +in Idris from scratch. + +This article is inspired by an exercise from chapter 6 of [Type Driven +Development with +Idris](https://www.manning.com/books/type-driven-development-with-idris), and is +written as a literate Idris file. + +## Gameplan + +Our goal is to provide a printf function that can be called, much like it's C equivlant, like so: + +> [!NOTE] +> As this is a literate Idris document, and we haven't defined our `printf` +> function yet, we have to use a `failing` block to ask the compiler to check +> that this code parses, and syntax highlight it for us, but not attempt to +> actually compile it. + +```idris +failing + example_usage : String + example_usage = printf "%s %d %02d" "hello" 1 2 +``` + + + +## Parsing a Format String + +## Calculating a Type From a Format String + +## printf From 1d91c5108489aea7b641aa35ad9557911625ff7e Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Sun, 9 Feb 2025 07:31:26 -0500 Subject: [PATCH 18/18] Support admonitions --- lib/Config.rakumod | 1 - lib/DB.rakumod | 1 + lib/Render/Head.rakumod | 2 + resources/admonitions.css | 84 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 resources/admonitions.css diff --git a/lib/Config.rakumod b/lib/Config.rakumod index 767bd1e..9a7ec07 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -10,7 +10,6 @@ use DB::Post; unit class Config; -# TODO: Support GFM admonitions method generate-post(Int:D $id, Post:D $post, $db) { my $meta = $db.meta; my $content = $post.render-html; diff --git a/lib/DB.rakumod b/lib/DB.rakumod index 190b4a3..ec89780 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -160,6 +160,7 @@ class PostDB { $res-dir.add('colors.css').spurt: %?RESOURCES.slurp; $res-dir.add('main.css').spurt: %?RESOURCES.slurp; $res-dir.add('code.css').spurt: %?RESOURCES.slurp; + $res-dir.add('admonitions.css').spurt: %?RESOURCES.slurp; } #| Get a list of posts sorted by date diff --git a/lib/Render/Head.rakumod b/lib/Render/Head.rakumod index c43c72b..c93c3ff 100644 --- a/lib/Render/Head.rakumod +++ b/lib/Render/Head.rakumod @@ -38,6 +38,8 @@ sub generate-head(BlogMeta:D $meta, $title?, $description?) is export { :href; link :rel, :href; + link :rel, + :href; ] } diff --git a/resources/admonitions.css b/resources/admonitions.css new file mode 100644 index 0000000..801cf4d --- /dev/null +++ b/resources/admonitions.css @@ -0,0 +1,84 @@ +/* Universal configuration */ +.note, +.tip, +.important, +.warning, +.caution { + display: flex; + flex-direction: row; + width: 66%; + box-sizing: border-box; + background-color: var(--bg-1); + color: var(--fg-1); + padding: 0.5rem; + border-radius: 1rem; + border: solid 0.5rem; + margin-top: var(--box-margin-vert); + margin-bottom: var(--box-margin-vert); +} +.note .title, +.tip .title, +.important .title, +.warning .title, +.caution .title { + align-self: center; +} +.note .title p, +.tip .title p, +.important .title p, +.warning .title p, +.caution .title p { + font-size: 0; + display: inline-block; + position: relative; +} +.note .title p::before, +.tip .title p::before, +.important .title p::before, +.warning .title p::before, +.caution .title p::before { + font-family: 'boxicons' !important; + font-size: 3rem; + display: inline-block; +} + +/* Notes */ +.note { + border-color: var(--blue); +} +.note .title p::before { + content: "\eb21"; + color: var(--blue); +} +/* Tips */ +.tip { + border-color: var(--green); +} +.tip .title p::before { + content: "\eb0d"; + color: var(--green); +} +/* Importants */ +.important { + border-color: var(--violet); +} +.important .title p::before { + content: "\eb0d"; + color: var(--violet); +} +/* Warnings */ +.warning { + border-color: var(--orange); +} +.warning .title p::before { + content: "\e9a3"; + color: var(--orange); +} +/* Cautions */ +.caution { + border-color: var(--red); +} +.caution .title p::before { + content: "\ee87"; + color: var(--red); +}