Compare commits

...

4 Commits

Author SHA1 Message Date
Spencer Brower 0dc6cc117f feat: Added robots.txt 2026-07-11 10:45:22 -04:00
Spencer Brower d10b832f3f feat: Added comment support. 2026-07-11 10:44:08 -04:00
Spencer Brower c3efbf80f0 build: Switched site build from Hugo to Thor. 2026-07-11 10:37:23 -04:00
Spencer Brower 177117e471 feat: Added icons. 2026-07-11 10:17:42 -04:00
3 changed files with 42 additions and 12 deletions
-3
View File
@@ -3,8 +3,6 @@
- Look at the source for 3 template languages that support mustache syntax, - Look at the source for 3 template languages that support mustache syntax,
and see how they implement the tempaltes, then pick the best one. One of them and see how they implement the tempaltes, then pick the best one. One of them
should be Hugo / go. should be Hugo / go.
- [ ] Deal with the favicon
- [ ] Icon support.
- [ ] robots.txt? - [ ] robots.txt?
- [ ] Evaluate Tufte CSS — borrow sidenote CSS or replace TailwindCSS entirely - [ ] Evaluate Tufte CSS — borrow sidenote CSS or replace TailwindCSS entirely
- Option B: Steal Tufte's sidenote/margin-note CSS (adapt for dark theme), keep TailwindCSS - Option B: Steal Tufte's sidenote/margin-note CSS (adapt for dark theme), keep TailwindCSS
@@ -17,4 +15,3 @@
- [ ] Nix build integration — main flake runs thor + tailwindcss instead of Hugo - [ ] Nix build integration — main flake runs thor + tailwindcss instead of Hugo
- [ ] Copy-to-clipboard button on code blocks (was in Hugo's custom-head.html) - [ ] Copy-to-clipboard button on code blocks (was in Hugo's custom-head.html)
- [ ] OpenGraph meta tags - [ ] OpenGraph meta tags
- [ ] utteranc.es comments on posts
+11
View File
@@ -0,0 +1,11 @@
package main
ICON_HOME :: `<svg aria-hidden="true" class="hi-svg-inline h-6 w-6" fill="none" height="1em" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="1em"><path d="m3 9 9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>`
ICON_GITHUB :: `<svg aria-hidden="true" class="hi-svg-inline h-7 w-7" fill="none" height="1em" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="1em"><path d="M15 22v-4a4.8 4.8.0 00-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35.0-3.5.0.0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35.0 3.5A5.403 5.403.0 004 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>`
ICON_RSS :: `<svg aria-hidden="true" class="hi-svg-inline h-7 w-7" fill="none" height="1em" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="1em"><path d="M4 11a9 9 0 019 9"/><path d="M4 4a16 16 0 0116 16"/><circle cx="5" cy="19" r="1"/></svg>`
ICON_CHEVRON_UP :: `<svg aria-hidden="true" class="hi-svg-inline h-10 w-10" fill="none" height="1em" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" width="1em"><path d="m18 15-6-6-6 6"/></svg>`
ICON_STAR :: `<svg aria-hidden="true" class="hi-svg-inline h-3.5 w-3.5 text-yellow-500 align-baseline mr-2" fill="currentColor" height="1em" viewBox="0 0 16 16" width="1em"><path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/></svg>`
+31 -9
View File
@@ -26,9 +26,6 @@ render_site :: proc(pages: []Page, content_path: string, output_dir: string, bas
} }
} }
// Copy static assets (avatar, favicon, etc.)
copy_static_assets(content_path, output_dir)
// Render individual content pages (skip home) // Render individual content pages (skip home)
for page in pages { for page in pages {
if page.type == .Home { if page.type == .Home {
@@ -58,6 +55,13 @@ render_site :: proc(pages: []Page, content_path: string, output_dir: string, bas
sitemap := generate_sitemap(pages, base_url) sitemap := generate_sitemap(pages, base_url)
write_file(fmt.tprintf("%s/sitemap.xml", output_dir), sitemap) write_file(fmt.tprintf("%s/sitemap.xml", output_dir), sitemap)
// Copy static assets (avatar, favicon, etc.)
copy_static_assets(content_path, output_dir)
// Generate robots.txt
robots := fmt.aprintf("User-agent: *\nAllow: /\nSitemap: %s/sitemap.xml\n", base_url)
write_file(fmt.tprintf("%s/robots.txt", output_dir), robots)
total := len(pages) + 1 total := len(pages) + 1
if !has_home { if !has_home {
total += 1 total += 1
@@ -66,17 +70,30 @@ render_site :: proc(pages: []Page, content_path: string, output_dir: string, bas
} }
render_page_html :: proc(page: Page, site_title: string) -> string { render_page_html :: proc(page: Page, site_title: string) -> string {
comments := ""
if page.type == .Post {
comments = `
<script src="https://utteranc.es/client.js"
repo="sbrow/sbrow.github.io"
issue-term="pathname"
label="Comment"
theme="github-dark"
crossorigin="anonymous"
async></script>`
}
body := fmt.aprintf( body := fmt.aprintf(
`<main> `<main>
<article class="prose"> <article class="prose">
<h1>%s</h1> <h1>%s</h1>
%s %s %s %s
</article> </article>%s
</main> </main>
`, `,
page.title, page.title,
render_date(page), render_date(page),
page.body_html, page.body_html,
comments,
) )
title := fmt.tprintf("%s | %s", page.title, site_title) title := fmt.tprintf("%s | %s", page.title, site_title)
return render_chrome(title, body) return render_chrome(title, body)
@@ -150,6 +167,7 @@ render_posts_html :: proc(pages: []Page, site_title: string) -> string {
} }
render_chrome :: proc(page_title: string, body: string) -> string { render_chrome :: proc(page_title: string, body: string) -> string {
header := fmt.aprintf(HEADER, ICON_HOME)
return fmt.aprintf( return fmt.aprintf(
`<!DOCTYPE html> `<!DOCTYPE html>
<html lang="en"> <html lang="en">
@@ -163,8 +181,9 @@ render_chrome :: proc(page_title: string, body: string) -> string {
</head> </head>
<body> <body>
%s%s<footer> %s%s<footer>
<a href="https://github.com/sbrow" target="_blank" rel="noopener noreferrer me" title="Github">GitHub</a> <a class="goto-top opacity-0" href="#">%s</a>
<a href="/index.xml" target="_blank" rel="noopener noreferrer me" title="Rss">RSS</a> <a href="https://github.com/sbrow" target="_blank" rel="noopener noreferrer me" title="Github">%s</a>
<a href="/index.xml" target="_blank" rel="noopener noreferrer me" title="Rss">%s</a>
<p class="pt-5 prose">Proudly built with <a href="https://odin-lang.org/">Odin</a> and <a href="https://tailwindcss.com/">Tailwindcss</a></p> <p class="pt-5 prose">Proudly built with <a href="https://odin-lang.org/">Odin</a> and <a href="https://tailwindcss.com/">Tailwindcss</a></p>
<p><small>&copy;</small> 2026</p> <p><small>&copy;</small> 2026</p>
</footer> </footer>
@@ -174,8 +193,11 @@ render_chrome :: proc(page_title: string, body: string) -> string {
</html> </html>
`, `,
page_title, page_title,
HEADER, header,
body, body,
ICON_CHEVRON_UP,
ICON_GITHUB,
ICON_RSS,
) )
} }
@@ -183,7 +205,7 @@ HEADER :: `
<header> <header>
<nav> <nav>
<ul> <ul>
<li class="mr-auto"><a href="/">Home</a></li> <li class="mr-auto"><a href="/">%s</a></li>
<li><a href="/ideas/">Ideas</a></li> <li><a href="/ideas/">Ideas</a></li>
<li><a href="/posts/">Posts</a></li> <li><a href="/posts/">Posts</a></li>
</ul> </ul>
@@ -202,7 +224,7 @@ render_post_item :: proc(page: Page) -> string {
} }
star := "" star := ""
if page.is_starred { if page.is_starred {
star = `<span class="text-yellow-500 mr-2">★</span>` star = ICON_STAR
} }
return fmt.aprintf( return fmt.aprintf(
` <li class="flex justify-between"><a href="%s">%s</a><span>%s%s</span></li>`, ` <li class="flex justify-between"><a href="%s">%s</a><span>%s%s</span></li>`,