Compare commits

..

No commits in common. "bc79817184e53a40c35f24736160bafde79c8ad6" and "815ff4bf4ef5ca1a94ed24e0800031763c907dbf" have entirely different histories.

11 changed files with 38 additions and 364 deletions

6
blog
View file

@ -25,9 +25,6 @@ my IO::Path:D $default-db-dir =
#| The default output directory #| The default output directory
my IO::Path:D $default-site-dir = $default-blog-dir.add('site/'); 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 #| Initalize the database
multi MAIN( multi MAIN(
"db", "db",
@ -95,8 +92,6 @@ multi MAIN(
"idris", "idris",
#| The path to the idris file #| The path to the idris file
IO::Path(Str) $source, IO::Path(Str) $source,
#| The path to the ipkg file
IO::Path(Str) :$ipkg = $default-ipkg,
#| The path of the database file #| The path of the database file
IO::Path(Str) :$db-dir = $default-db-dir, IO::Path(Str) :$db-dir = $default-db-dir,
#| The date/time the post should be recorded as posted at #| The date/time the post should be recorded as posted at
@ -111,7 +106,6 @@ multi MAIN(
source => $source.absolute.IO, source => $source.absolute.IO,
posted-at => $posted-at, posted-at => $posted-at,
hidden => $hidden, hidden => $hidden,
ipkg => $ipkg.absolute.IO,
); );
$db.write: $db-dir; $db.write: $db-dir;
say 'Post inserted with id ', $id; say 'Post inserted with id ', $id;

View file

@ -2,66 +2,22 @@ use v6.e.PREVIEW;
use HTML::Functional; use HTML::Functional;
use DB::BlogMeta; use DB::BlogMeta;
use DB::Post;
unit class Config; unit class Config;
method generate-head(Str:D $title, BlogMeta:D $meta, $description?) { method generate-post(Str:D $title, Str:D $content, BlogMeta:D $meta) {
my $head =
head [ head [
meta :charset<utf-8>; meta :charset<utf-8>;
meta :name<viewport>, :content<width=device-width, initial-scale=1>; meta :name<viewport>, :content<width=device-width, initial-scale=1>;
meta :author :content<Nathan McCarty>; # meta :name<description>, :content<raku does htmx>;
title "{$meta.title} $title"; title "";
# 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;
]; ];
} my $body;
method site-header(BlogMeta:D $meta) {
header :class<site-header>, [
div :class<site-logo>, [
# TODO: Use a real image here
$meta.title
];
div :class<site-tagline>, [
$meta.tagline
];
div :class<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 [
self.site-header: $meta;
div :class<post-body>, [
$content
]
];
# TODO: Setup footer
# my $footer; # my $footer;
die "Not done yet";
my $html = html :lang<en>, [ my $html = html :lang<en>, [
$head, $head,
$body $body

View file

@ -78,14 +78,13 @@ class PostDB {
mkdir $by-slug unless $by-slug.e; mkdir $by-slug unless $by-slug.e;
# Render all the posts and make symlinks # Render all the posts and make symlinks
for %!posts.kv -> $id, $post { for %!posts.kv -> $id, $post {
my $html = $config.generate-post: $post, $!meta; my $html =
$config.generate-post:
$post.title, $post.render-html, $!meta;
my $id-path = $by-id.add: "$id.html"; my $id-path = $by-id.add: "$id.html";
$id-path.spurt: $html; $id-path.spurt: $html;
for $post.all-slugs -> $slug { for $post.all-slugs -> $slug {
# remove the symlink if it already exists $by-slug.add($slug).symlink: $id-path;
my $slug-path = $by-slug.add: $slug;
$slug-path.unlink if $slug-path.l;
$id-path.symlink: $slug-path;
} }
} }
# Render the archive # Render the archive

View file

@ -2,7 +2,6 @@ use v6.e.PREVIEW;
use Pandoc; use Pandoc;
use JSON::Class:auth<zef:vrurg>; use JSON::Class:auth<zef:vrurg>;
use File::Temp;
use DB::Post; use DB::Post;
@ -12,18 +11,7 @@ unit class IdrisPost does Post is json(:pretty);
#| Marker for disambiguation between post types in json representation, the #| Marker for disambiguation between post types in json representation, the
#| cheaty way #| cheaty way
has Bool:D $.idris is json = True; has Bool:D $.idris = 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
is json(
:to-json(*.Str),
:from-json(*.IO)
);
method title(--> Str:D) { method title(--> Str:D) {
markdown-title($!source) markdown-title($!source)
@ -32,65 +20,5 @@ method title(--> Str:D) {
# Use katla to highlight our file, mangle the resulting output, and then pass it # Use katla to highlight our file, mangle the resulting output, and then pass it
# through to pandoc for html generation # through to pandoc for html generation
method render-html(--> Str:D) { method render-html(--> Str:D) {
my $project-dir = $!ipkg.parent; die "Not implemented";
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 <build>;
# 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
};
}
# 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;
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) {
# 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;
# Modify the output to use our styling instead of katla's
# Remove the style block and line breaks
$katla-out ~~ s:g/'<style>' .* '</style>'//;
$katla-out ~~ s:g/'<br />'//;
# Use <pre> blocks for the code and inject our own class for idris code
$katla-out ~~ s:g/'<code class="' \w+ '">' \s*/<pre><code class="idris-code">/;
$katla-out ~~ s:g/\s* '</code>'/<\/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
} }

View file

@ -2,6 +2,7 @@ use v6.e.PREVIEW;
use Pandoc; use Pandoc;
use JSON::Class:auth<zef:vrurg>; use JSON::Class:auth<zef:vrurg>;
use DB::Post; use DB::Post;
#| A plain markdown post #| A plain markdown post
@ -11,24 +12,11 @@ unit class MarkdownPost does Post is json(:pretty);
#| cheaty way #| cheaty way
has Bool:D $.markdown = True; has Bool:D $.markdown = True;
#| Override the generated description for this post
has Str $.summary;
method title(--> Str:D) { method title(--> Str:D) {
markdown-title $!source markdown-title($!source)
} }
# Simply provide our source file to pandoc # Simply provide our source file to pandoc
method render-html(--> Str:D) { method render-html(--> Str:D) {
markdown-to-html $!source die "Not implemented";
}
# 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,8 +53,3 @@ method all-slugs(--> Array[Str:D]) {
#| Render this post to an html body #| Render this post to an html body
method render-html(--> Str:D) {...} method render-html(--> Str:D) {...}
#| Get the description for this post, returning nil if there is none
method description(--> Str) {
Nil
}

View file

@ -3,27 +3,19 @@ unit module Pandoc;
use JSON::Fast; use JSON::Fast;
#| Run pandoc with the given arguments, dieing on failure
sub pandoc(*@args --> Str:D) {
# Call into pandoc
my $pandoc = run 'pandoc', @args, :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
}
#| Extract the title from a markdown document #| Extract the title from a markdown document
#| #|
#| The title is the only top level header, will throw an error if there are #| The title is the only top level header, will throw an error if there are
#| multiple top level headers or are none #| multiple top level headers or are none
sub markdown-title(IO::Path:D $file --> Str:D) is export { sub markdown-title(IO::Path:D $file --> Str:D) is export {
# Call into pandoc
my $pandoc = run <pandoc -f gfm -t JSON>, $file, :out, :err;
# Collect the output # Collect the output
my $output = pandoc <-f gfm -t JSON>, $file; my $output = $pandoc.out.slurp: :close;
my $stderr = $pandoc.err.slurp: :close;
die "Pandoc exited with {$pandoc.exitcode}\nout: $output\nerr: $stderr"
unless $pandoc;
# Parse out output from pandoc, we are making an executive decision to trust # 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 # pandoc here, so we won't do any error handling for pandoc's output
@ -35,8 +27,12 @@ sub markdown-title(IO::Path:D $file --> Str:D) is export {
$v ~~ Associative && $v<t> ~~ "Header" $v ~~ Associative && $v<t> ~~ "Header"
} }
my @headers = %parsed<blocks>.grep(&is-header).grep(*<c>[0] == 1); my @headers = %parsed<blocks>.grep(&is-header).grep(*<c>[0] == 1);
die "More than one top level header in $file" if @headers.elems > 1; if @headers.elems > 1 {
die "No top level headers in $file" if @headers.elems == 0; die "More than one top level header in $file";
};
if @headers.elems == 0 {
die "No top level headers in $file";
};
# Extract the header and process it into a string # Extract the header and process it into a string
my @header = @headers[0]<c>[2].flat; my @header = @headers[0]<c>[2].flat;
@ -59,38 +55,15 @@ sub markdown-title(IO::Path:D $file --> Str:D) is export {
return $title; 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 #| Use pandoc to render a markdown document to html
sub markdown-to-html(IO::Path:D $file --> Str:D) is export { sub markdown-to-html(IO::Path:D $file --> Str:D) is export {
pandoc <-f gfm>, $file # 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
} }

View file

@ -5,33 +5,3 @@ module Posts.HelloWorld
%default total %default total
``` ```
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
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
```

View file

@ -1,23 +1,3 @@
# My New Blog # My New Blog
This is a blog that uses a static site generator written in raku! 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

View file

@ -1,44 +0,0 @@
code {
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;
}
pre {
display: flex;
align-items: center;
justify-content: center;
}
.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)
}

View file

@ -1,65 +0,0 @@
:root {
color-scheme: light dark;
color: light-dark(#474747, #b9b9b9);
background-color: light-dark(#ebebeb, #252525);
font-family: "Open Sans", sans-serif, serif;
}
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-left: 2rem;
padding-right: 2rem;
border-radius: 1rem;
background-color: light-dark(#ffffff, #181818);
/* text-align: justify; */
}
.post-body > h1 {
text-align: center;
color: light-dark(#dd0f9d, #eb6eb7);
}
.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;
}