commit c3491fa94db4ccf37ee0398a9f35790221e6b1f5
parent f01f01ed70594bc62c32c035d3f0f583bda7043c
Author: Luke Willis <lukejw@loquat.dev>
Date: Fri, 4 Jul 2025 01:16:01 -0400
Working blog demo
Diffstat:
6 files changed, 136 insertions(+), 95 deletions(-)
diff --git a/Makefile.am b/Makefile.am
@@ -25,7 +25,8 @@ bin_SCRIPTS = \
SOURCES = \
grug/ui.scm \
- grug/utils.scm
+ grug/utils.scm \
+ grug/builders.scm
EXTRA_DIST += \
README.md \
diff --git a/example/pages/index.md b/example/pages/index.md
@@ -1,22 +0,0 @@
-# Hello, world!
-This is a test markdown file.
-
-I can use both **bold** and *italic* because `code` is super cool.
-What do you think?
-
-## Title 2
-
-### Title 3
-
-#### Title 4
-
-##### Title 5 (List edition)
-
-1. Numero uno
-2. Numero dos
- - "That's rascist"
- - No it's not
- - I can count in spanish, too (two)!
-3. Numero tres
-
-The test is complete.
diff --git a/example/posts/hello-world.md b/example/posts/hello-world.md
@@ -0,0 +1,2 @@
+# Hello, post!
+This is a test post.
diff --git a/example/posts/update_situation.md b/example/posts/update_situation.md
@@ -0,0 +1,2 @@
+# The Update Situation
+Things are changing, and it might be good or bad.
diff --git a/grug/builders.scm b/grug/builders.scm
@@ -0,0 +1,126 @@
+(define-module (grug builders)
+ #:use-module (ice-9 popen)
+ #:use-module (ice-9 textual-ports)
+ #:use-module (ice-9 pretty-print)
+ #:use-module (htmlprag)
+ #:use-module (grug utils)
+ #:export (copy-directory
+ simple-pages
+ blog))
+
+;; Reads the markdown file at path and converts it into sxml representing html ('shtml')
+;; TODO: Cleanup and move to its own module
+(define (markdown->shtml path)
+ (let* ((cmd (string-append "cmark --to html --nobreaks < " path))
+ (port (open-input-pipe cmd))
+ (html (get-string-all port)))
+ (close-port port)
+ (delete '*TOP* (html->shtml (string-delete #\newline html)))))
+
+;; Copy the given directory to the site.
+;; This is good for things like CSS folders.
+(define* (copy-directory directory
+ #:key
+ (prefix "site"))
+ (display "copy-directory\n")
+
+ (for-each
+ (lambda (path)
+ (let* ((output-path (string-append prefix "/" path))
+ (output-dir (dirname output-path)))
+ (format #t "\t~A -> ~A\n" path output-path)
+ (unless (file-exists? output-dir) (mkdir output-dir))
+ (copy-file path output-path)))
+ (reverse (ls-recursive directory))))
+
+;; TODO: Cleanup and move to its own module
+(define (basic-template body)
+ `(*TOP* (*DECL* DOCTYPE html)
+ (html
+ (head
+ (meta (@ (charset "utf-8")))
+ (title "Hello, world!"))
+ (body
+ (h1 "Test site")
+ ,@body))))
+
+;; Build the pages in the given directory and copy them to the site.
+;; This is good for things like 'About' or 'Contact' pages.
+;; TODO: Don't assume a markdown reader
+(define* (simple-pages directory
+ #:key
+ (prefix "site")
+ (template basic-template))
+ (display "simple-pages\n")
+
+ ;; Iterate through files in directory
+ (for-each
+ (lambda (path)
+ (let* ((base-shtml (markdown->shtml path))
+ (built-shtml (template base-shtml))
+ (output (shtml->html built-shtml))
+ (output-dir
+ (string-append prefix
+ (substring (dirname path)
+ (string-length directory))))
+ (output-path
+ (string-append output-dir "/"
+ (basename path ".md")
+ ".html")))
+ (unless (file-exists? output-dir) (mkdir output-dir))
+ (format #t "\t~A -> ~A\n" path output-path)
+ (call-with-output-file output-path
+ (lambda (port)
+ (display output port)))))
+ ;; The list is reversed so that the shortest paths are listed first.
+ (reverse (ls-recursive directory))))
+
+;; TODO: Cleanup and move to its own module
+(define (basic-collection-template posts)
+ `((h1 "Posts")
+ ,@(map (lambda (post)
+ `(article (h2 ,post)
+ (p "Description...")))
+ posts)))
+
+;; TODO: Figure out how to get title, date and description information from posts
+;; This would likely require some kind of parsing system for the first line of the file.
+;; Perhaps it could be written in scheme? `((title . "My Update") (date . "07/10/2007 18:00"))
+;; If I were to modify the text by removing the first line, I would have to change the parsing
+;; process. I'd have to do it the hard way using ports after cutting out the string.
+;; TODO: Cleanup and move to its own module
+(define* (blog directory
+ #:key
+ (prefix "site")
+ (post-prefix "posts")
+ (template basic-template)
+ (collection-template basic-collection-template))
+ (display "blog\n")
+
+ (let ((posts (map
+ (lambda (path)
+ (let* ((base-shtml (markdown->shtml path))
+ (built-shtml (template base-shtml))
+ (output (shtml->html built-shtml))
+ (output-dir
+ (string-append prefix "/" post-prefix
+ (substring (dirname path)
+ (string-length directory))))
+ (output-path
+ (string-append output-dir "/"
+ (basename path ".md")
+ ".html")))
+ (unless (file-exists? output-dir) (mkdir output-dir))
+ (format #t "\t~A -> ~A\n" path output-path)
+ (call-with-output-file output-path
+ (lambda (port)
+ (display output port)))
+ output-path))
+ (reverse (ls-recursive directory)))))
+ ;; Build index.html
+ (let ((output (shtml->html (template (collection-template posts))))
+ (output-path (string-append prefix "/index.html")))
+ (format #t "\t~A\n" output-path)
+ (call-with-output-file output-path
+ (lambda (port)
+ (display output port))))))
diff --git a/grug/ui.scm b/grug/ui.scm
@@ -1,80 +1,12 @@
;;; Grug user interface.
(define-module (grug ui)
- #:use-module (grug utils)
- #:use-module (htmlprag)
- #:use-module (ice-9 popen)
- #:use-module (ice-9 textual-ports)
#:use-module (ice-9 pretty-print)
+ #:use-module (grug builders)
#:export (main))
-;; Pipe the contents of a markdown file into cmark
-;; TODO: Change to make compact HTML without messing up things like code blocks
-(define (markdown->html path)
- (let* ((cmd (string-append "cmark --to html --nobreaks < " path))
- (port (open-input-pipe cmd))
- (html (get-string-all port)))
- (close-port port)
- (string-delete #\newline html)))
-
-(define (basic-template body)
- `(*TOP* (*DECL* DOCTYPE html)
- (html
- (head
- (meta (@ (charset "utf-8")))
- (title "Hello, world! m"))
- (body
- (h1 "Test site")
- ,@body))))
-
-;; Copy the given directory to the output directory. Very simple.
-;; I would use this for copying CSS files and images, for example.
-(define* (copy-directory directory
- #:key
- (prefix "site"))
- (display "copy-directory\n")
-
- (for-each
- (lambda (path)
- (let* ((output-path (string-append prefix "/" path))
- (output-dir (dirname output-path)))
- (format #t "\t~A -> ~A\n" path output-path)
- (unless (file-exists? output-dir) (mkdir output-dir))
- (copy-file path output-path)))
- (reverse (ls-recursive directory))))
-
-;; Build pages from the given directory into the output directory.
-;; 'input-dir/index.md' will become 'output-dir/index.html'
-;; 'input-dir/subdir/index.md' will become 'output-dir/subdir/index.html'
-;; ...and so on.
-(define* (simple-pages directory
- #:key
- (prefix "site")
- (template basic-template))
- (display "simple-pages\n")
-
- ;; Iterate through files in directory
- (for-each
- (lambda (path)
- (let* ((base-sxml (delete '*TOP* (html->sxml (markdown->html path))))
- (built-sxml (template base-sxml))
- (output (sxml->html built-sxml))
- (output-dir
- (string-append prefix
- (substring (dirname path)
- (string-length directory))))
- (output-path
- (string-append output-dir "/"
- (basename path ".md")
- ".html")))
- (unless (file-exists? output-dir) (mkdir output-dir))
- (format #t "\t~A -> ~A\n" path output-path)
- (call-with-output-file output-path
- (lambda (port)
- (display output port)))))
- ;; Reverse the list so that base directories are listed first
- (reverse (ls-recursive directory))))
-
(define* (main prog . args)
+ (pretty-print args)
(simple-pages "pages")
- (copy-directory "css"))
+ (copy-directory "css")
+ (blog "posts"))