author | Alan Dipert
<alan@dipert.org> 2025-10-08 05:06:26 UTC |
committer | Alan Dipert
<alan@dipert.org> 2025-10-08 05:06:26 UTC |
parent | 18a2920fd84ba3de6ac3f116d4d113cec4a43c5e |
AGENTS.md | +2 | -1 |
Makefile | +15 | -11 |
README.md | +2 | -2 |
md/Home.md | +0 | -1 |
md/HomepageDesign.md | +1 | -1 |
tools/gen_index.sh | +37 | -0 |
tools/index_list.awk | +91 | -0 |
tpl/style.css | +4 | -0 |
diff --git a/AGENTS.md b/AGENTS.md index afbacdc..b880fda 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,11 +15,12 @@ Welcome! This document describes how automated helpers should operate when worki - Always run `make assets && make` after touching Markdown, templates, or build scripts unless instructed otherwise. - `make deploy` is reserved for publishing—never call it unless explicitly asked and the git worktree is clean. - Ensure `tools/mdlink2html.awk` and build helpers continue to support wiki-style intra-site links. +- Keep the generated `out/Index.html` in sync; the `tools/gen_index.sh` helper runs automatically from `make`. - Keep updates idempotent: don’t delete generated output or vendor directories unless instructed. ## Blog Post Style Guidelines -When drafting or editing Markdown articles under `md_export/`: +When drafting or editing Markdown articles under `md/`: - Begin with a level-one heading that matches the filename (e.g., `# HomepageDesign`). - Use short, conversational paragraphs aimed at technical readers; prefer present tense and active voice. diff --git a/Makefile b/Makefile index 385b69b..e3aebba 100644 --- a/Makefile +++ b/Makefile @@ -8,24 +8,25 @@ CSS := tpl/style.css MD2HTML ?= /usr/bin/cmark-gfm CMARK_FLAGS := --to html --extension table --validate-utf8 BUILDINFO := tools/buildinfo.sh +INDEX_HTML := $(OUT)/Index.html DEPLOY_HOST ?= arsien23i2@dreamhost:tailrecursion.com/~alan MD_FILES := $(shell find $(SRC) -type f -name '*.md' | LC_ALL=C sort) HTML := $(patsubst $(SRC)/%.md,$(OUT)/%.html,$(MD_FILES)) -all: $(OUT)/style.css $(HTML) +all: $(OUT)/style.css $(HTML) $(INDEX_HTML) $(OUT)/style.css: $(CSS) - mkdir -p $(OUT) - cp $(CSS) $@ + @mkdir -p $(OUT) + @cp $(CSS) $@ $(OUT): - mkdir -p $(OUT) + @mkdir -p $(OUT) $(OUT)/%.html: $(SRC)/%.md $(HEAD) $(FOOT) tools/mdlink2html.awk $(BUILDINFO) | $(OUT) - mkdir -p $(@D) - awk -f tools/mdlink2html.awk $< > $@.rewritten.md - $(MD2HTML) $(CMARK_FLAGS) $@.rewritten.md > $@.body.html + @mkdir -p $(@D) + @awk -f tools/mdlink2html.awk $< > $@.rewritten.md + @$(MD2HTML) $(CMARK_FLAGS) $@.rewritten.md > $@.body.html @css_prefix="$$(dirname "$@" | sed -e 's#^$(OUT)##' -e 's#^/##' -e 's#[^/][^/]*#../#g')"; \ page_title=$$(basename "$<" .md); \ build_info=$$($(BUILDINFO)); \ @@ -35,11 +36,14 @@ $(OUT)/%.html: $(SRC)/%.md $(HEAD) $(FOOT) tools/mdlink2html.awk $(BUILDINFO) | sed -e "s|@CSS@|$${css_prefix}|g" -e "s|@BUILDINFO@|$${build_info_esc}|g" -e "s|@TITLE@|$${page_title_esc}|g" $(FOOT) > $@.foot.html; \ cat $@.head.html $@.body.html $@.foot.html > $@; \ rm -f $@.head.html $@.foot.html - rm -f $@.rewritten.md $@.body.html + @rm -f $@.rewritten.md $@.body.html assets: - mkdir -p $(OUT) - rsync -a --include='*/' --exclude='*.md' --exclude='*.MD' --prune-empty-dirs $(SRC)/ $(OUT)/ + @mkdir -p $(OUT) + @rsync -a --include='*/' --exclude='*.md' --exclude='*.MD' --prune-empty-dirs $(SRC)/ $(OUT)/ + +$(INDEX_HTML): $(HTML) tools/gen_index.sh tools/index_list.awk $(HEAD) $(FOOT) $(BUILDINFO) | $(OUT) + @tools/gen_index.sh $@ $(SRC) deploy: check-git-clean assets all @if [ -z "$(DEPLOY_HOST)" ]; then \ @@ -53,7 +57,7 @@ tree: printf 'HTML outputs: ' && if [ -d $(OUT) ]; then find $(OUT) -type f -name '*.html' | wc -l; else echo 0; fi clean: - rm -rf $(OUT) + @rm -rf $(OUT) help: @echo 'Static site generator targets:' diff --git a/README.md b/README.md index 7eebf65..04bf84e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Homepage static site generator -This converts Markdown from `md_export/` to HTML in `out/` and can deploy the result to your shared host. +This converts Markdown from `md/` to HTML in `out/` and can deploy the result to your shared host. > AI helpers: read `AGENTS.md` before making changes. ## Usage make assets && make -xdg-open out/index.html # or open on macOS +xdg-open out/Index.html # or open on macOS > Note: `make deploy` requires a clean git worktree. Commit or stash changes first if you plan to publish. diff --git a/md/Home.md b/md/Home.md index 3de48c9..20cb45c 100644 --- a/md/Home.md +++ b/md/Home.md @@ -36,4 +36,3 @@ Updates | 2020-11-10 | Added three [Cognicast](https://www.cognitect.com/cognicast/) appearances to [TechWorks](./TechWorks.md). | | 2020-11-09 | Added [FairDivision](./FairDivision.md). | | 2020-11-02 | Deployed new [Zim](https://zim-wiki.org/)-based site and established redirects from previous URLs. | - diff --git a/md/HomepageDesign.md b/md/HomepageDesign.md index 0ab7939..505d765 100644 --- a/md/HomepageDesign.md +++ b/md/HomepageDesign.md @@ -8,7 +8,7 @@ For years I maintained this site directly out of [Zim](https://zim-wiki.org/). Z ## Static site generator today -Today the site is rebuilt with a tiny static site generator that lives right alongside the content. Everything under `md_export/` is authored in Markdown, and a `make` pipeline rewrites wiki links, runs [`cmark-gfm`](https://github.com/github/cmark-gfm) to render HTML, wraps the result in a shared template, and copies any non-Markdown assets into the `out/` directory. Deploys are a simple `make deploy`, which rsyncs the generated files to my DreamHost account. The build footer shows who published the site, when, and from which git revision. +Today the site is rebuilt with a tiny static site generator that lives right alongside the content. Everything under `md/` is authored in Markdown, and a `make` pipeline rewrites wiki links, runs [`cmark-gfm`](https://github.com/github/cmark-gfm) to render HTML, wraps the result in a shared template, and copies any non-Markdown assets into the `out/` directory. Deploys are a simple `make deploy`, which rsyncs the generated files to my DreamHost account. The build footer shows who published the site, when, and from which git revision. ## Why change? diff --git a/tools/gen_index.sh b/tools/gen_index.sh new file mode 100755 index 0000000..cf934c2 --- /dev/null +++ b/tools/gen_index.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -euo pipefail + +output=${1:?"usage: gen_index.sh out/Index.html [src-dir]"} +src_dir=${2:-md} +script_dir=$(cd "$(dirname "$0")" && pwd) +root_dir=$(cd "$script_dir/.." && pwd) +cd "$root_dir" + +build_info=$(tools/buildinfo.sh) +build_info_esc=$(printf '%s\n' "$build_info" | sed 's/[\/&]/\\&/g') +css_prefix="" +page_title="Index" + +head_tmp=$(mktemp) +foot_tmp=$(mktemp) +body_tmp=$(mktemp) + +sed -e "s|@CSS@|$css_prefix|g" \ + -e "s|@BUILDINFO@|$build_info_esc|g" \ + -e "s|@TITLE@|$page_title|g" \ + tpl/head.html > "$head_tmp" + +sed -e "s|@CSS@|$css_prefix|g" \ + -e "s|@BUILDINFO@|$build_info_esc|g" \ + -e "s|@TITLE@|$page_title|g" \ + tpl/foot.html > "$foot_tmp" + +{ + echo "<h1>Index of Alan's Homepage</h1>" + echo "<p>Browse every page in this wiki-style site:</p>" + find "$src_dir" -type f -name '*.md' | LC_ALL=C sort | awk -v prefix="$src_dir" -f tools/index_list.awk +} > "$body_tmp" + +cat "$head_tmp" "$body_tmp" "$foot_tmp" > "$output" + +rm -f "$head_tmp" "$body_tmp" "$foot_tmp" diff --git a/tools/index_list.awk b/tools/index_list.awk new file mode 100755 index 0000000..bda3856 --- /dev/null +++ b/tools/index_list.awk @@ -0,0 +1,91 @@ +#!/usr/bin/env awk -f +# Generate a nested unordered list of site pages from a list of Markdown files. + +function escape_regex(s, i, c, out, specials) { + specials = ".^$[]()|*+?{}\\" + out = "" + for (i = 1; i <= length(s); i++) { + c = substr(s, i, 1) + if (index(specials, c)) { + out = out "\\" c + } else { + out = out c + } + } + return out +} + +function indent(depth, i, s) { + s = "" + for (i = 1; i < depth; i++) { + s = s " " + } + return s +} + +function render(node, depth, i, child, display, link, pad, link_tmp) { + for (i = 1; i <= child_count[node]; i++) { + child = children[node, i] + display = name[child] + link = href[child] + pad = indent(depth) + + if (link != "") { + link_tmp = link + sub(/\.md$/, ".html", link_tmp) + link_tmp = "./" link_tmp + printf("%s<li><a href=\"%s\">%s</a>", pad, link_tmp, display) + } else { + printf("%s<li>%s", pad, display) + } + + if (child_count[child] > 0) { + printf("\n%s <ul>\n", pad) + render(child, depth + 1) + printf("%s </ul>\n", pad) + printf("%s</li>\n", pad) + } else { + printf("</li>\n") + } + } +} + +BEGIN { + FS = "/" + pref = prefix + if (pref != "" && substr(pref, length(pref), 1) != "/") { + pref = pref "/" + } + pref_pattern = escape_regex(pref) +} +{ + rel = $0 + if (pref != "") { + sub("^" pref_pattern, "", rel) + } + base = rel + gsub(/\.md$/, "", base) + + n = split(base, parts, "/") + full = "" + for (i = 1; i <= n; i++) { + parent = full + full = (full == "" ? parts[i] : full "/" parts[i]) + + if (!(full in seen)) { + seen[full] = 1 + child_count[parent]++ + children[parent, child_count[parent]] = full + name[full] = parts[i] + } + + if (i == n) { + href[full] = rel + } + } +} +END { + print "<ul class=\"site-index\">" + render("", 1) + print "</ul>" +} diff --git a/tpl/style.css b/tpl/style.css index f506c5a..b7ad6f6 100644 --- a/tpl/style.css +++ b/tpl/style.css @@ -67,6 +67,10 @@ img[src*="200px"]{width:200px;} .content th[align="right"]{text-align:right;} .content td[align="center"], .content th[align="center"]{text-align:center;} +.site-index{list-style:none;margin:1.5rem 0;padding:0;} +.site-index>li{margin:.35rem 0;} +.site-index ul{list-style:none;margin:.35rem 0;padding-left:1.25rem;} +.site-index a{font-weight:600;} .site-foot{ margin-top:2rem; border-top:1px solid rgba(0,0,0,.08);