Compare commits
10 commits
815ff4bf4e
...
bc79817184
Author | SHA1 | Date | |
---|---|---|---|
bc79817184 | |||
ce364c01d8 | |||
804b51aaa6 | |||
bf56790e3a | |||
37238e864b | |||
7df154596b | |||
37e92392ca | |||
2c38910ccd | |||
56833369ce | |||
addce4f87c |
11 changed files with 363 additions and 37 deletions
6
blog
6
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
|
||||
|
@ -106,6 +111,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;
|
||||
|
|
|
@ -2,21 +2,65 @@ use v6.e.PREVIEW;
|
|||
|
||||
use HTML::Functional;
|
||||
use DB::BlogMeta;
|
||||
use DB::Post;
|
||||
|
||||
unit class Config;
|
||||
|
||||
method generate-post(Str:D $title, Str:D $content, BlogMeta:D $meta) {
|
||||
my $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 :name<description>, :content<raku does htmx>;
|
||||
title "";
|
||||
meta :author :content<Nathan McCarty>;
|
||||
title "{$meta.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;
|
||||
# my $footer;
|
||||
}
|
||||
|
||||
die "Not done yet";
|
||||
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 $html = html :lang<en>, [
|
||||
$head,
|
||||
|
|
|
@ -78,13 +78,14 @@ 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 {
|
||||
$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
|
||||
|
|
|
@ -2,6 +2,7 @@ use v6.e.PREVIEW;
|
|||
|
||||
use Pandoc;
|
||||
use JSON::Class:auth<zef:vrurg>;
|
||||
use File::Temp;
|
||||
|
||||
use DB::Post;
|
||||
|
||||
|
@ -11,7 +12,18 @@ 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;
|
||||
|
||||
#| 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) {
|
||||
markdown-title($!source)
|
||||
|
@ -20,5 +32,65 @@ 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) {
|
||||
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 <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
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use v6.e.PREVIEW;
|
|||
|
||||
use Pandoc;
|
||||
use JSON::Class:auth<zef:vrurg>;
|
||||
|
||||
use DB::Post;
|
||||
|
||||
#| A plain markdown post
|
||||
|
@ -12,11 +11,24 @@ 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)
|
||||
markdown-title $!source
|
||||
}
|
||||
|
||||
# Simply provide our source file to pandoc
|
||||
method render-html(--> Str:D) {
|
||||
die "Not implemented";
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -5,3 +5,33 @@ module Posts.HelloWorld
|
|||
|
||||
%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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
44
resources/code.css
Normal file
44
resources/code.css
Normal file
|
@ -0,0 +1,44 @@
|
|||
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)
|
||||
}
|
65
resources/main.css
Normal file
65
resources/main.css
Normal file
|
@ -0,0 +1,65 @@
|
|||
: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;
|
||||
}
|
Loading…
Add table
Reference in a new issue