This repository contains the source code of https://troyfigiel.com. It is generated from Scheme code using Haunt.
To build this website locally, ensure you have Git and Guix installed, and run the following commands:
git clone https://git.sr.ht/~troyfigiel/troyfigiel.com
cd troyfigiel.com
git checkout 1f79657
guix time-machine -C channels.scm -- shell -m manifest.scm --container -- make
The generated website can be found in the site
directory.
The Guix repository is our only dependency and its commit is pinned in
channels.scm
. This ensures the required older versions of the packages
specified in the manifest.scm
are used automatically.
This website is hosted on SourceHut pages.
Unless otherwise specified, the code in this repository is licensed under the GNU GPLv3. See COPYING for more details.
(specifications->manifest '("emacs" "emacs-htmlize" "emacs-ox-haunt" "git" "guile" "haunt" "make"))
(list (channel (name 'guix) (url "https://git.savannah.gnu.org/git/guix.git") (branch "master") (commit "966b6b0157c3a66238c63d100f50a8a1e2de0bb5") (introduction (make-channel-introduction "9edb3f66fd807b096b48283debdcddccfea34bad" (openpgp-fingerprint "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))))
emacs --batch --eval "(progn (require 'org) (org-babel-tangle-file \"README.org\"))"
git diff --exit-code
Tangling README.org.
Exporting Org files to HTML.
emacs --batch --load org/export.el --eval "(export-all)"
Building website using Haunt.
haunt build
site: tangle channels.scm manifest.scm <<site-build-steps>> .PHONY: tangle tangle: README.org channels.scm manifest.scm <<tangle-readme>> .PHONY: clean clean: rm -rf pages/programs/website posts site
image: guix oauth: pages.sr.ht/PAGES:RW packages: - gzip - hut - tar environment: shell: guix time-machine -C channels.scm -- shell -m manifest.scm --container tasks: - install: | cd troyfigiel.com $shell -- git --no-pager log --oneline -1 - build: | cd troyfigiel.com $shell -- make site - upload: | cd troyfigiel.com tar -cvzC site . -f site.tar.gz hut pages publish -d troyfigiel.com site.tar.gz
(require 'org) (require 'ox-haunt) (defun export--single-org-post (org-post-path) "Export a single Org post specified by its ORG-POST-PATH." (with-temp-buffer (insert-file-contents org-post-path) (org-mode) (let ((buffer-file-name org-post-path)) (ox-haunt-export-to-html)))) (defun export--all-org-posts () "Export all org posts in the `org/' directory." (let ((ox-haunt-base-dir ".") (all-org-posts (directory-files (file-name-concat "org" "posts") t "\\.org"))) (make-directory (file-name-concat ox-haunt-base-dir "posts") t) (mapc #'export--single-org-post all-org-posts))) (defun export--literate-website () "Export all org posts in the `org/' directory." (with-temp-buffer (insert-file-contents "README.org") (org-mode) (let* ((backup-inhibited t) (html-buffer (ox-haunt-export-as-html)) (website-dir (file-name-concat "pages" "programs" "website"))) (make-directory website-dir t) (with-current-buffer html-buffer (write-file (file-name-concat website-dir "index.html")) (kill-buffer (current-buffer)))))) (defun export-all () (let ((org-confirm-babel-evaluate nil) (org-export-with-section-numbers nil) (org-export-with-toc nil) (org-html-head-include-default-style nil) (org-html-htmlize-output-type 'css)) (org-babel-do-load-languages 'org-babel-load-languages '((shell . t))) (export--all-org-posts) (export--literate-website)))
(use-modules (haunt builder assets) (haunt builder atom) (haunt builder blog) (haunt builder flat-pages) (haunt post) (haunt reader) (haunt site) (src theme)) (define blog-collections `(("Recent Posts" "blog/index.html" ,posts/reverse-chronological))) (define (dirname-slug post) (string-append (post-slug-v2 post) "/index")) (site #:title "troyfigiel" #:domain "troyfigiel.com" #:posts-directory "posts" #:build-directory "site" #:default-metadata '((author . "Troy Figiel")) #:readers (list html-reader sxml-reader) #:builders (list (blog #:theme troyfigiel-theme #:post-prefix "blog" #:collections blog-collections) (atom-feed) (flat-pages "pages" #:template (theme-layout troyfigiel-theme)) (static-directory "assets/openpgpkey" ".well-known/openpgpkey") (static-directory "assets/fonts" "fonts") (static-directory "assets/css" "css")) #:make-slug dirname-slug)
(define-module (src theme) #:use-module (haunt builder blog) #:use-module (haunt post) #:use-module (haunt site) #:use-module (ice-9 popen) #:use-module (ice-9 rdelim) #:use-module (srfi srfi-19) #:export (troyfigiel-theme)) (define (stylesheet name) `(link (@ (rel "stylesheet") (href ,(string-append "/css/" name ".css"))))) (define (link name uri) `(a (@ (href ,uri)) ,name)) (define troyfigiel-theme (theme #:name "troyfigiel" #:layout (lambda (site title body) `((doctype "html") (head (meta (@ (charset "utf-8"))) (title ,(string-append title " - " (site-title site))) (link (@ (rel "alternate") (type "application/atom+xml") (title "Atom feed") (href "/feed.xml"))) ,(stylesheet "fonts") ,(stylesheet "site") ,(stylesheet "code")) (body (div (@ (class "container")) (nav (ul (li ,(link "Troy Figiel" "/")) (li ,(link "Blog" "/blog")) (li ,(link "Literate programs" "/programs")) (li ,(link "Contact" "/contact")))) ,body)))) #:post-template (lambda (post) `((h1 (@ (class "title")) ,(post-ref post 'title)) (div (@ (class "post")) ,(post-sxml post)))) #:collection-template (lambda (site title posts prefix) `((h1 ,title) ,(map (lambda (post) (let ((url (string-append prefix "/" (post-slug-v2 post)))) `(div (@ (class "summary")) (h2 (a (@ (href ,url)) ,(post-ref post 'title))) (div (@ (class "date")) ,(date->string (post-date post) "~d.~m.~Y")) (div (@ (class "post")) ,(car (post-sxml post)))))) posts)))))
@font-face { font-family: "Open Sans"; src: url("fonts/OpenSans-Regular.woff2") format("woff2"); font-weight: 400; font-style: normal; } @font-face { font-family: "Open Sans"; src: url("fonts/OpenSans-Italic.woff2") format("woff2"); font-weight: 400; font-style: italic; } @font-face { font-family: "Open Sans"; src: url("fonts/OpenSans-SemiBold.woff2") format("woff2"); font-weight: 600; font-style: normal; } @font-face { font-family: "Open Sans"; src: url("fonts/OpenSans-SemiBoldItalic.woff2") format("woff2"); font-weight: 600; font-style: italic; } @font-face { font-family: "Open Sans"; src: url("fonts/OpenSans-Bold.woff2") format("woff2"); font-weight: 700; font-style: normal; } @font-face { font-family: "Open Sans"; src: url("fonts/OpenSans-BoldItalic.woff2") format("woff2"); font-weight: 700; font-style: italic; } @font-face { font-family: "Droid Sans Mono"; src: url("fonts/DroidSansMono.woff2") format("woff2"); }
code, pre { font-family: var(--mono-font); color: var(--text); } .src { background-color: #dfdfdf; border-radius: 0.3rem; } code { padding-inline: 0.3rem; } pre { padding: 1rem 1.4rem; max-width: 100%; overflow: auto; color: var(--text); } .org-builtin { color: #775228; font-weight: bold; } .org-comment { color: #6a5937; font-style: italic; } .org-comment-delimiter { color: #6a5937; font-style: italic; } .org-constant { color: #006e50; } .org-doc { color: #42573f; font-style: italic; } .org-function-name { color: #882000; } .org-keyword { color: #702f1f; font-weight: bold; } .org-makefile-targets { color: #882000; } .org-string { color: #3a7800; } .org-type { color: #226022; } .org-variable-name { color: #125a7f; }
:root, ::backdrop { --sans-font: "Open Sans", sans-serif; --mono-font: "Droid Sans Mono", monospace; --standard-border-radius: 5px; --bg: #f0efea; --text: #23211c; } /* Reset box-sizing */ *, *::before, *::after { box-sizing: border-box; } html { font-family: var(--sans-font); scroll-behavior: smooth; background-color: var(--bg); height: 100%; } /* Make the body a nice central block */ body { color: var(--text); font-size: 1.15rem; line-height: 1.5; display: grid; grid-template-columns: 1fr min(55rem, 90%) 1fr; margin: 0; } body > * { grid-column: 2; } /* Make the header bg full width, but the content inline with body */ /* TODO: I will need to find a way to get the header out of the nested divs. */ body > div > div > header { grid-column: 1 / -1; } body > div > div > header > *:only-child { margin-block-start: 2rem; } body > div > div > header h1 { max-width: 1200px; margin: 1rem auto; } body > div > div > header p { font-style: italic; } /* Add a little padding to ensure spacing is correct between content and header > nav */ main { padding-top: 1.5rem; } /* TODO: Why is the footer in a div? */ body > div > footer { margin-top: 4rem; padding: 2rem 1rem 1.5rem 1rem; font-size: 0.9rem; border-top: 1px solid var(--text); } /* Format headers */ h1 { font-size: 3rem; } h2 { font-size: 1.8rem; margin-top: 3rem; } p { margin: 1.5rem 0; } /* Prevent long strings from overflowing container */ p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; } /* Fix line height when title wraps */ h1, h2, h3 { line-height: 1.1; } /* Reduce header size on mobile */ @media only screen and (max-width: 720px) { h1 { font-size: 2.5rem; } h2 { font-size: 2.1rem; } h3 { font-size: 1.75rem; } h4 { font-size: 1.25rem; } } /* Format links & buttons */ a, a:visited { /* Make links semibold */ font-weight: 600; color: var(--text); } a:hover { text-decoration: none; } /* Format navigation */ nav { line-height: 2; padding: 1rem 0 0 0; border-bottom: 1px solid var(--text); font-weight: 600; } /* Use flexbox to allow items to wrap, as needed */ nav ul { align-content: space-around; align-items: center; justify-content: left; list-style-type: none; padding: 0; } /* List items are inline elements, make them behave more like blocks */ nav ul li { margin: 0 4rem 0 0; display: inline-block; font-weight: 600; } nav a, nav a:visited { margin: 0 2rem 2rem 0.5rem; color: var(--text); display: inline-block; text-decoration: underline; } nav a:hover, nav a.current, nav a[aria-current="page"] { border-color: var(--text); color: var(--text); cursor: pointer; text-decoration: none; } section { border-top: 1px solid var(--text); border-bottom: 1px solid var(--text); padding: 2rem 1rem; margin: 3rem 0; } /* Don't double separators when chaining sections */ section + section, section:first-child { border-top: 0; padding-top: 0; } section:last-child { border-bottom: 0; padding-bottom: 0; } table { border-collapse: collapse; margin: 1.5rem 0; } figure > table { width: max-content; margin: 0; } td, th { border: 1px solid var(--text); text-align: start; padding: 0.5rem; } th { font-weight: 600; } table caption { font-weight: 600; margin-bottom: 0.5rem; } /* Misc body elements */ hr { border: none; height: 1px; margin: 1rem auto; } figure { margin: 0; display: block; overflow-x: auto; } sup, sub { vertical-align: baseline; position: relative; } sup { top: -0.4em; } sub { top: 0.3em; }
/pages/programs/website/ /posts/ /site/