Description generation

This commit is contained in:
Nathan McCarty 2025-02-04 16:51:37 -05:00
parent 804b51aaa6
commit ce364c01d8
6 changed files with 102 additions and 33 deletions

View file

@ -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<utf-8>;
meta :name<viewport>, :content<width=device-width, initial-scale=1>;
meta :author :content<Nathan McCarty>;
title "{$meta.title} $title";
# TODO: Add style sheets
# Use Iosevka Alie as the monospace font
link :rel<stylesheet>,
:href<https://static.stranger.systems/fonts/IosevkaAlie/IosevkaAile.css>;
# 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<preconnect> :href<https://static.stranger.systems>;
link :rel<preconnect> :href<https://fonts.googleapis.com>;
link :rel<preconnect> :href<https://fonts.gstatic.com> :crossorigin;
# Load fonts, Iosevka Alie for code, and Open Sans for content
link :rel<stylesheet>,
: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>;
# Inline our style sheets
style %?RESOURCES<main.css>.slurp;
style %?RESOURCES<code.css>.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<post-body>, [

View file

@ -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 {

View file

@ -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;

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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 <pandoc -f gfm -t JSON>, $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<t> ~~ "Header"
}
my @headers = %parsed<blocks>.grep(&is-header).grep(*<c>[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]<c>[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<t> ~~ 'Para'
}
my @paras = %parsed<blocks>.grep(&is-para);
die "No paragraphs in markdown" if @paras.elems == 0;
my @para = @paras[0][0]<c>.flat;
# Proces it into a string
my $para = "";
for @para -> $component {
next unless $component ~~ Associative;
given $component<t> {
when "Str" {
$para ~= $component<c>;
}
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 <pandoc -f gfm>, $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
}