fix: Fixed the image on the homepage.

This commit is contained in:
Spencer Brower
2026-07-10 18:41:31 -04:00
parent c3574c6737
commit 1de6f58024
5 changed files with 165 additions and 63 deletions
+5
View File
@@ -3,3 +3,8 @@
- 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
should be Hugo / go.
- [ ] Deal with the favicon
- [ ] Icon support.
- [ ] RSS
- [ ] sitemap
- [ ] robots.txt?
+77 -6
View File
@@ -7,6 +7,7 @@ import "core:os"
import "core:strings"
Page_Type :: enum {
Home,
Post,
Standalone,
}
@@ -16,6 +17,7 @@ Page :: struct {
slug: string,
permalink: string,
title: string,
description: string,
date: string,
draft: bool,
is_starred: bool,
@@ -30,6 +32,7 @@ Page :: struct {
walk_content :: proc(content_path: string, include_drafts: bool) -> []Page {
pages: [dynamic]Page
collect_home(&pages, content_path)
collect_standalone(&pages, content_path)
posts_path := fmt.tprintf("%s/posts", content_path)
@@ -51,6 +54,27 @@ walk_content :: proc(content_path: string, include_drafts: bool) -> []Page {
return pages[:]
}
collect_home :: proc(pages: ^[dynamic]Page, content_path: string) {
html_path := fmt.tprintf("%s/index.html", content_path)
if os.exists(html_path) {
page, ok := load_page(html_path, .Home, "")
if ok {
page.permalink = "/"
append(pages, page)
}
return
}
md_path := fmt.tprintf("%s/index.md", content_path)
if os.exists(md_path) {
page, ok := load_page(md_path, .Home, "")
if ok {
page.permalink = "/"
append(pages, page)
}
}
}
collect_standalone :: proc(pages: ^[dynamic]Page, content_path: string) {
entries, err := os.read_all_directory_by_path(content_path, context.allocator)
if err != nil {
@@ -60,14 +84,17 @@ collect_standalone :: proc(pages: ^[dynamic]Page, content_path: string) {
defer os.file_info_slice_delete(entries, context.allocator)
for entry in entries {
if !strings.has_suffix(entry.name, ".md") {
if !is_content_file(entry.name) {
continue
}
if entry.name == "index.md" || entry.name == "index.html" {
continue
}
if entry.type != .Regular {
continue
}
slug := entry.name[:len(entry.name) - 3]
slug := strip_extension(entry.name)
page, ok := load_page(entry.fullpath, .Standalone, slug)
if ok {
append(pages, page)
@@ -86,16 +113,19 @@ collect_posts :: proc(pages: ^[dynamic]Page, posts_path: string) {
for entry in entries {
switch entry.type {
case .Regular:
if !strings.has_suffix(entry.name, ".md") {
if !is_content_file(entry.name) {
continue
}
slug := entry.name[:len(entry.name) - 3]
slug := strip_extension(entry.name)
page, ok := load_page(entry.fullpath, .Post, slug)
if ok {
append(pages, page)
}
case .Directory:
index_path := fmt.tprintf("%s/index.md", entry.fullpath)
index_path := fmt.tprintf("%s/index.html", entry.fullpath)
if !os.exists(index_path) {
index_path = fmt.tprintf("%s/index.md", entry.fullpath)
}
if !os.exists(index_path) {
continue
}
@@ -105,7 +135,6 @@ collect_posts :: proc(pages: ^[dynamic]Page, posts_path: string) {
append(pages, page)
}
case .Undetermined, .Symlink, .Named_Pipe, .Socket, .Block_Device, .Character_Device:
// Do nothing
}
}
}
@@ -134,14 +163,22 @@ load_page :: proc(
page.type = page_type
page.slug = slug
page.title = fm.title
page.description = fm.description
page.date = fm.date
page.draft = fm.draft
page.is_starred = fm.isStarred
page.menu = fm.menu
page.body = strings.clone(body)
if strings.has_suffix(file_path, ".html") {
page.body_html = strings.clone(body)
} else {
page.body_html = cm.markdown_to_html_from_string(body, {.Unsafe})
}
switch page_type {
case .Home:
page.permalink = "/"
case .Post:
page.permalink = fmt.aprintf("/posts/%s/", slug)
case .Standalone:
@@ -152,3 +189,37 @@ load_page :: proc(
return
}
is_content_file :: proc(name: string) -> bool {
return strings.has_suffix(name, ".md") || strings.has_suffix(name, ".html")
}
strip_extension :: proc(name: string) -> string {
dot := strings.last_index(name, ".")
if dot < 0 {
return name
}
return name[:dot]
}
// copy_static_assets copies non-content files from the content root to the output directory.
copy_static_assets :: proc(content_path: string, output_dir: string) {
entries, err := os.read_all_directory_by_path(content_path, context.allocator)
if err != nil {
return
}
defer os.file_info_slice_delete(entries, context.allocator)
for entry in entries {
if entry.type != .Regular {
continue
}
if is_content_file(entry.name) {
continue
}
dest := fmt.tprintf("%s/%s", output_dir, entry.name)
if err := os.copy_file(dest, entry.fullpath); err != nil {
fmt.eprintfln("thor: cannot copy %s: %v", entry.name, err)
}
}
}
+2
View File
@@ -6,6 +6,7 @@ import "core:strings"
Frontmatter :: struct {
title: string,
description: string,
date: string,
publishDate: string,
draft: bool,
@@ -45,6 +46,7 @@ parse_frontmatter :: proc(content: string) -> (fm: Frontmatter, body: string, ok
}
fm.title = json_get_string(obj, "title")
fm.description = json_get_string(obj, "description")
fm.date = json_get_string(obj, "date")
fm.publishDate = json_get_string(obj, "publishDate")
fm.draft = json_get_bool(obj, "draft")
+1 -1
View File
@@ -20,7 +20,7 @@ main :: proc() {
pages := walk_content(opt.content_dir, opt.drafts)
render_site(pages, opt.output_dir)
render_site(pages, opt.content_dir, opt.output_dir)
}
load_default_options :: proc(opt: ^Options) {
+57 -33
View File
@@ -4,45 +4,58 @@ import "core:fmt"
import "core:os"
import "core:strings"
SITE_TITLE :: "One Idiot Developer"
SITE_DESC :: "This is fine...right?"
@(rodata)
MONTHS: [12]string = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
}
HEADER :: `
<header>
<nav>
<ul>
<li class="mr-auto"><a href="/">Home</a></li>
<li><a href="/ideas/">Ideas</a></li>
<li><a href="/posts/">Posts</a></li>
</ul>
</nav>
</header>
`
render_site :: proc(pages: []Page, output_dir: string) {
render_site :: proc(pages: []Page, content_path: string, output_dir: string) {
sort_pages_by_date(pages)
// Find home page and extract site title
home: Page
has_home := false
site_title := "Site"
for page in pages {
html := render_page_html(page)
if page.type == .Home {
home = page
has_home = true
site_title = page.title
break
}
}
// Copy static assets (avatar, favicon, etc.)
copy_static_assets(content_path, output_dir)
// Render individual content pages (skip home)
for page in pages {
if page.type == .Home {
continue
}
html := render_page_html(page, site_title)
write_page(output_dir, page.permalink, html)
}
home_html := render_home_html(pages)
// Render home page
if has_home {
home_html := render_home_html(home, pages, site_title)
write_file(fmt.tprintf("%s/index.html", output_dir), home_html)
}
posts_html := render_posts_html(pages)
// Render posts list page
posts_html := render_posts_html(pages, site_title)
write_page(output_dir, "/posts/", posts_html)
fmt.printfln("Rendered %d pages to %s", len(pages) + 2, output_dir)
total := len(pages) + 1
if !has_home {
total += 1
}
fmt.printfln("Rendered %d pages to %s", total, output_dir)
}
render_page_html :: proc(page: Page) -> string {
render_page_html :: proc(page: Page, site_title: string) -> string {
body := fmt.aprintf(
`<main>
<article class="prose">
@@ -55,15 +68,18 @@ render_page_html :: proc(page: Page) -> string {
render_date(page),
page.body_html,
)
title := fmt.tprintf("%s | %s", page.title, SITE_TITLE)
title := fmt.tprintf("%s | %s", page.title, site_title)
return render_chrome(title, body)
}
render_home_html :: proc(pages: []Page) -> string {
render_home_html :: proc(home: Page, pages: []Page, site_title: string) -> string {
items: [dynamic]string
defer delete(items)
for page in pages {
if page.type == .Home {
continue
}
append(&items, render_post_item(page))
}
@@ -72,24 +88,20 @@ render_home_html :: proc(pages: []Page) -> string {
body := fmt.aprintf(
`<main>
<header class="text-center mb-28">
<img class="mx-auto w-18 h-18 rounded-full" src="/avatar.jpg">
<h1 class="text-2xl/12 mt-2.5 mb-0 font-bold">%s</h1>
<p class="text-slate-400">%s</p>
</header>
%s </header>
<ul>
%s
</ul>
</main>
`,
SITE_TITLE,
SITE_DESC,
home.body_html,
post_list,
)
return render_chrome(SITE_TITLE, body)
return render_chrome(site_title, body)
}
render_posts_html :: proc(pages: []Page) -> string {
render_posts_html :: proc(pages: []Page, site_title: string) -> string {
parts: [dynamic]string
defer delete(parts)
@@ -123,7 +135,7 @@ render_posts_html :: proc(pages: []Page) -> string {
append(&parts, "</main>\n")
body := strings.join(parts[:], "")
title := fmt.tprintf("Posts | %s", SITE_TITLE)
title := fmt.tprintf("Posts | %s", site_title)
return render_chrome(title, body)
}
@@ -153,6 +165,18 @@ render_chrome :: proc(page_title: string, body: string) -> string {
)
}
HEADER :: `
<header>
<nav>
<ul>
<li class="mr-auto"><a href="/">Home</a></li>
<li><a href="/ideas/">Ideas</a></li>
<li><a href="/posts/">Posts</a></li>
</ul>
</nav>
</header>
`
render_post_item :: proc(page: Page) -> string {
date_html := ""
if page.date != "" {