diff --git a/blog b/blog index 5145556..4198bdd 100755 --- a/blog +++ b/blog @@ -44,12 +44,8 @@ multi MAIN( my $title = get; print "Tagline: "; my $tagline = get; - print "Base URL: "; - my $base-url = get; - my $meta = - BlogMeta.new: - title => $title, tagline => $tagline, base-url => $base-url; + my $meta = BlogMeta.new: title => $title, tagline => $tagline; my $db = DB::PostDB.init: $meta; @@ -122,22 +118,6 @@ multi MAIN( say 'Post has slugs: ', $db.posts{$id}.all-slugs; } -#| Update the last editied time on a post -multi MAIN( - "touch", - #| The post id to touch - Int:D $id, - #| The path of the database file - IO::Path(Str) :$db-dir = $default-db-dir, - #| The date/time the post should be recorded as laste edited at - DateTime(Str) :$edited-at = DateTime.now, -) { - my $db = read-db $db-dir; - my $post = $db.posts{$id.Int}; - $post.edited-at.push: $edited-at; - $db.write: $db-dir; -} - #| Render the blog to html multi MAIN( "render", diff --git a/lib/Atom.rakumod b/lib/Atom.rakumod deleted file mode 100644 index 0c0ad7b..0000000 --- a/lib/Atom.rakumod +++ /dev/null @@ -1,64 +0,0 @@ -use v6.e.PREVIEW; - -use DB::Post; -use DB::BlogMeta; - -use XML; - -unit module Atom; - -# get the link for a post -sub post-link(BlogMeta:D $meta, Int:D $id, Post:D $post --> Str:D) { - my @slugs = $post.all-slugs; - my $base = $meta.get-base-url; - if @slugs.elems { - "$base/posts/by-slug/{@slugs[*-1]}.html" - } else { - "$base/posts/by-id/$id.html" - } -} - -#| Convert a single post to an atom item -sub post-to-item(BlogMeta:D $meta, Int:D $id, Post:D $post --> XML::Element) { - my $link = post-link $meta, $id, $post; - my $desc = $post.description; - my $xml = XML::Element.new(:name); - $xml.append: - XML::Element.new(:name, :nodes([$link])); - $xml.append: - XML::Element.new(:name, :nodes([$post.title])); - $xml.append: - XML::Element.new(:name<updated>, :nodes([$post.updated])); - $xml.append: - XML::Element.new(:name<published>, :nodes([$post.posted-at])); - my $author = XML::Element.new(:name<author>); - $author.append: - XML::Element.new(:name<email>, :nodes(["thatonelutenist@stranger.systems"])); - $author.append: - XML::Element.new(:name<name>, :nodes(["Nathan McCarty"])); - $xml.append: $author; - $xml.append: - XML::Element.new(:name<link>, :attribs({:href($link), :rel<alternate>})); - $xml.append: - XML::Element.new(:name<summary>, :nodes([$desc])) if $desc; - - $xml -} - -#| Produce an atom feed from the database -sub posts-to-atom($db --> XML::Element) is export { - my $updated = $db.posts.values.map(*.updated).max; - my $xml = - XML::Element.new( - :name<feed>, - :attribs({:xmlns('http://www.w3.org/2005/Atom')})); - $xml.append: XML::Element.new(:name<id>, :nodes([$db.meta.get-base-url])); - $xml.append: XML::Element.new(:name<title>, :nodes([$db.meta.title])); - $xml.append: XML::Element.new(:name<updated>, :nodes([$updated])); - for $db.sorted-posts -> $pair { - unless $pair.value.hidden { - $xml.append: post-to-item $db.meta, $pair.key, $pair.value; - } - } - $xml -} diff --git a/lib/Config.rakumod b/lib/Config.rakumod index 595d2ce..5503bbb 100644 --- a/lib/Config.rakumod +++ b/lib/Config.rakumod @@ -6,16 +6,12 @@ use DB::Post; unit class Config; -method generate-head($title, BlogMeta:D $meta, $description?) { +method generate-head(Str:D $title, BlogMeta:D $meta, $description?) { head [ meta :charset<utf-8>; meta :name<viewport>, :content<width=device-width, initial-scale=1>; meta :author :content<Nathan McCarty>; - do if $title ~~ Str:D { - title "$title — {$meta.title}"; - } else { - title $meta.title; - } + title "{$meta.title} — $title"; # Add description, if one exists do if $description ~~ Str:D { meta :description :content($description) @@ -26,15 +22,11 @@ method generate-head($title, BlogMeta:D $meta, $description?) { link :rel<preconnect> :href<https://static.stranger.systems>; link :rel<preconnect> :href<https://fonts.googleapis.com>; link :rel<preconnect> :href<https://fonts.gstatic.com> :crossorigin; - link :rel<preconnect> :href<https://unpkg.com>; - # Load fonts, Iosevka for code, Open Sans for content, and boxicons for - # icons + # Load fonts, Iosevka Alie for code, and Open Sans for content link :rel<stylesheet>, - :href<https://static.stranger.systems/fonts/Iosevka/Iosevka.css>; + :href<https://static.stranger.systems/fonts/IosevkaAlie/IosevkaAile.css>; link :rel<stylesheet>, :href<https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap>; - link :rel<stylesheet>, - :href<https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css>; # Inline our style sheets style %?RESOURCES<main.css>.slurp; style %?RESOURCES<code.css>.slurp; @@ -51,115 +43,8 @@ method site-header(BlogMeta:D $meta) { $meta.tagline ]; div :class<header-links>, [ - a :href</index.html>, [ - icon 'home'; - ' '; - span [ - 'Home'; - ]; - ]; - a :href</archive.html>, [ - icon 'archive'; - ' '; - span [ - 'Archive'; - ]; - ]; - a :href</about.html>, [ - icon 'info-circle'; - ' '; - span [ - 'About'; - ]; - ]; - a :href</atom.xml>, [ - icon 'rss'; - ' '; - span [ - 'Feed'; - ]; - ]; - ]; - ] -} -method post-date(Post:D $post) { - my $datetime = $post.posted-at; - my $timestamp = sprintf( - "%s %02d:%02d%s", - $datetime.yyyy-mm-dd, - ($datetime.hour % 12) || 12, - $datetime.minute, - $datetime.hour < 12 ?? 'am' !! 'pm' - ); - - div :class<post-time>, :title("Posted At $timestamp"), [ - icon 'time'; - ' '; - $timestamp - ] -} - -method post-edit(Post:D $post) { - return [] unless $post.edited-at.elems; - my $datetime = $post.edited-at.max; - my $timestamp = sprintf( - "%s %02d:%02d%s", - $datetime.yyyy-mm-dd, - ($datetime.hour % 12) || 12, - $datetime.minute, - $datetime.hour < 12 ?? 'am' !! 'pm' - ); - - div :class<post-edit>, :title("Laste Edited At $timestamp"), [ - icon 'edit'; - ' '; - $timestamp - ] -} - -sub mins-to-string($mins) { - if $mins < 60 { - $mins.Str ~ "m" - } else { - my $h = $mins div 60; - my $m = $mins mod 60; - $h.Str ~ "h" ~ $m.Str ~ "m" - } -} - -method post-read-time(Post:D $post) { - my ($slow, $average, $fast) = $post.readtimes; - div :class<post-read-time>, :title<Estimated read time at 140/180/220 WPM>, [ - icon 'timer'; - ' '; - mins-to-string $slow; - ' '; - '/'; - ' '; - mins-to-string $average; - ' '; - '/'; - ' '; - mins-to-string $fast; - ] -} - -method post-info(Post:D $post) { - div :class<post-info>, [ - self.post-date: $post; - self.post-edit: $post; - self.post-read-time: $post; - # TODO: Add tags once we have support for that - ]; -} - -method post-header(Post:D $post) { - header :class<post-header>, [ - div :class<post-title>, [ - h1 $post.title; - ]; - self.post-info: $post; + ] ] } @@ -170,11 +55,8 @@ method generate-post(Post:D $post, BlogMeta:D $meta) { my $body = body [ self.site-header: $meta; - article :class<post>, [ - self.post-header: $post; - div :class<post-body>, [ - $content; - ] + div :class<post-body>, [ + $content ] ]; # TODO: Setup footer @@ -187,85 +69,3 @@ method generate-post(Post:D $post, BlogMeta:D $meta) { "<!doctype html>$html" } - -method generate-blurb(Int:D $id, $db) { - my $post = $db.posts{$id}; - my $desc = $post.description; - my @slugs = $post.all-slugs; - # Use the primary slug if there is one, the id if there isn't - my $link = do if @slugs.elems { - "/posts/by-slug/{@slugs[*-1]}.html" - } else { - "/posts/by-id/$id.html" - } - div :class<post-blurb>, [ - div :class<post-blurb-title>, [ - a :href($link), span [ - h2 $post.title; - ]; - ]; - self.post-info: $post; - if $desc ~~ Str:D { - div :class<post-blurb-description>, [ - 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 - }); - - my $head = self.generate-head(Nil, $db.meta); - my $body = body [ - self.site-header: $db.meta; - div :class<post-blurbs>, [ - h1 "Recent Posts" - ], @most-recent; - ]; - - my $html = - html :lang<en>, [ - $head, - $body - ]; - - "<!doctype html>$html" -} - -method generate-archive($db) { - my @most-recent = - $db.sorted-posts - .grep(!*.value.hidden) - .map(-> $pair { - self.generate-blurb: $pair.key, $db - }); - - my $head = self.generate-head(Nil, $db.meta); - my $body = body [ - self.site-header: $db.meta; - div :class<post-blurbs>, [ - h1 "All Posts" - ], @most-recent; - ]; - - my $html = - html :lang<en>, [ - $head, - $body - ]; - - "<!doctype html>$html" -} - -sub icon($icon) { - i(:class("bx bx-$icon")) -} diff --git a/lib/DB.rakumod b/lib/DB.rakumod index fe30ec0..fd95f6f 100644 --- a/lib/DB.rakumod +++ b/lib/DB.rakumod @@ -5,14 +5,12 @@ unit module DB; use Pandoc; use JSON::Class:auth<zef:vrurg>; -use XML; use DB::Post; use DB::BlogMeta; use DB::MarkdownPost; use DB::IdrisPost; use DB::PlaceholderPost; -use Atom; use Config; subset PostTypes where MarkdownPost:D | IdrisPost:D | PlaceholderPost:D; @@ -85,28 +83,15 @@ class PostDB { $id-path.spurt: $html; for $post.all-slugs -> $slug { # remove the symlink if it already exists - my $slug-path = $by-slug.add: "$slug.html"; + my $slug-path = $by-slug.add: $slug; $slug-path.unlink if $slug-path.l; $id-path.symlink: $slug-path; } } - # Render the index - $out-dir.add('index.html').spurt: $config.generate-index(self); # Render the archive - $out-dir.add('archive.html').spurt: $config.generate-archive(self); - # Symlink the about article - my $about-path = $out-dir.add('about.html'); - $about-path.unlink if $about-path.l; - $by-id.add("{$!meta.about-id}.html").symlink: $about-path; # Render the rss/atom feed - my $atom-path = $out-dir.add('atom.xml'); - my $atom = posts-to-atom self; - $atom-path.spurt: ~$atom; - } - - #| Get a list of posts sorted by date - method sorted-posts() { - %!posts.sort(*.value.posted-at).reverse + # Render the index + die "Not Implemented" } } diff --git a/lib/DB/BlogMeta.rakumod b/lib/DB/BlogMeta.rakumod index e6ccf5d..f2a0382 100644 --- a/lib/DB/BlogMeta.rakumod +++ b/lib/DB/BlogMeta.rakumod @@ -13,19 +13,3 @@ has Str:D $.tagline is required is rw; #| The id of the placeholder post has Int:D $.placeholder-id is rw = 0; - -#| The id of the about post -has Int:D $.about-id is rw = 0; - -#| The base url of this post -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<BLOG_TEST> { - "http://localhost:8080" - } else { - $!base-url - } -} diff --git a/lib/DB/Post.rakumod b/lib/DB/Post.rakumod index ed0819d..0c43fdb 100644 --- a/lib/DB/Post.rakumod +++ b/lib/DB/Post.rakumod @@ -24,7 +24,6 @@ DateTime:D $.posted-at #| An optional list of edit times for the post has DateTime:D @.edited-at - is rw is json( :to-json( value => { $^value.Str } @@ -43,15 +42,6 @@ has Bool:D $.hidden is json is rw = False; #| document produced it method title(--> Str:D) {...} -#| The time the post was last updated at -method updated(--> DateTime:D) { - if @!edited-at { - @!edited-at.max - } else { - $!posted-at - } -} - #| Get the list of slugs for this post, including ones auto generated from #| the title, as well as any additional slugs method all-slugs(--> Array[Str:D]) { @@ -68,9 +58,3 @@ method render-html(--> Str:D) {...} method description(--> Str) { Nil } - -#| Estimated readtimes at 140/180/220 wpm -method readtimes() { - my $word-count = $!source.slurp.words.elems; - ($word-count / 140, $word-count / 180, $word-count / 220).map(*.ceiling) -} diff --git a/lib/Pandoc.rakumod b/lib/Pandoc.rakumod index 75887e3..74048f0 100644 --- a/lib/Pandoc.rakumod +++ b/lib/Pandoc.rakumod @@ -92,8 +92,5 @@ sub markdown-first-paragraph(IO::Path:D $file --> Str:D) is export { #| Use pandoc to render a markdown document to html sub markdown-to-html(IO::Path:D $file --> Str:D) is export { - # Remove the header, we'll regenerate it later - my $output = pandoc <-f gfm>, $file; - $output ~~ s:g/'<h1' .* '</h1>'//; - $output + pandoc <-f gfm>, $file } diff --git a/resources/code.css b/resources/code.css index da050c2..4635ff2 100644 --- a/resources/code.css +++ b/resources/code.css @@ -1,13 +1,12 @@ code { - font-family: "Iosevka Web", monospace; + font-family: "Iosevka Aile Web", monospace; background-color: light-dark(#fbf3db, #103c48); color: light-dark(#53676d, #adbcbc); + min-width: 80ch; width: 80%; display: block; padding: 1rem; border-radius: 0.55rem / 0.5rem; - white-space: pre-wrap; - overflow-x: auto; } pre { diff --git a/resources/main.css b/resources/main.css index 6f12ebf..602362d 100644 --- a/resources/main.css +++ b/resources/main.css @@ -5,7 +5,7 @@ font-family: "Open Sans", sans-serif, serif; } -body, .post { +body { display: flex; align-items: center; justify-content: center; @@ -13,18 +13,8 @@ body, .post { gap: 1rem; } -.header-links { - display: flex; - flex-direction: row; - align-items: center; - gap: 1rem; - font-size: 1.1rem; - flex-wrap: wrap; - margin-top: 0.5rem; -} - .site-header { - width: 50%; + width: 60%; display: block; padding: 1rem; border-radius: 1rem; @@ -36,93 +26,26 @@ body, .post { .site-logo { color: light-dark(#d6000c, #ed4a46); - font-size: 2.5rem; - font-weight: bold; } .site-tagline { - color: light-dark(#909995, #777777); - font-size: 0.9rem; - font-style: italic; } -.post-body, .post-header, .post-blurbs { +.post-body { width: 66%; display: block; - padding-left: 1.5rem; - padding-right: 1.5rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; + padding-left: 2rem; + padding-right: 2rem; border-radius: 1rem; background-color: light-dark(#ffffff, #181818); /* text-align: justify; */ } -.post-blurbs { - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - gap: 1rem; -} - -.post-blurb { - width: 100%; - display: block; - border-radius: 1rem; - padding: 0.5rem; - background-color: light-dark(#ebebeb, #252525); - /* background-color: light-dark(#cdcdcd, #3b3b3b); */ - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - /* gap: 0.25rem; */ -} - -.post-header { - padding: 1.5rem; - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; -} - -.post-title { +.post-body > h1 { text-align: center; color: light-dark(#dd0f9d, #eb6eb7); } -.post-blurbs > h1 { - color: light-dark(#1d9700, #70b433); -} - -a:link { - color: light-dark(#009c8f, #41c7b9); -} - -a:visited { - color: light-dark(#dd0f9d, #eb6eb7); -} -.post-title > h1, .post-blurb-title > h2 { - margin-top: 0px; - margin-bottom: 0px; -} - -.post-info { - display: flex; - flex-direction: row; - align-items: center; - gap: 1rem; - color: light-dark(#909995, #777777); - font-size: 0.9rem; - flex-wrap: wrap; -} - -.post-read-time { - text-decoration: underline dotted; -} - .post-body > h2 { text-align: center; color: light-dark(#282828, #dedede); @@ -137,14 +60,6 @@ a:visited { text-align: center; color: light-dark(#282828, #dedede); } -/* .post-body > p { */ -/* text-indent: 2ch; */ -/* } */ - -a > span { - text-decoration: underline; -} - -a { - text-decoration: none; +.post-body > p { + text-indent: 2ch; }