mirror of
https://github.com/sbrow/thor.git
synced 2026-08-26 11:23:32 -04:00
fix: Fixed the image on the homepage.
This commit is contained in:
@@ -3,3 +3,8 @@
|
|||||||
- 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.
|
||||||
|
- [ ] RSS
|
||||||
|
- [ ] sitemap
|
||||||
|
- [ ] robots.txt?
|
||||||
|
|||||||
+97
-26
@@ -7,22 +7,24 @@ import "core:os"
|
|||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
Page_Type :: enum {
|
Page_Type :: enum {
|
||||||
|
Home,
|
||||||
Post,
|
Post,
|
||||||
Standalone,
|
Standalone,
|
||||||
}
|
}
|
||||||
|
|
||||||
Page :: struct {
|
Page :: struct {
|
||||||
type: Page_Type,
|
type: Page_Type,
|
||||||
slug: string,
|
slug: string,
|
||||||
permalink: string,
|
permalink: string,
|
||||||
title: string,
|
title: string,
|
||||||
date: string,
|
description: string,
|
||||||
draft: bool,
|
date: string,
|
||||||
is_starred: bool,
|
draft: bool,
|
||||||
menu: string,
|
is_starred: bool,
|
||||||
body: string,
|
menu: string,
|
||||||
body_html: string,
|
body: string,
|
||||||
bundle_dir: string,
|
body_html: string,
|
||||||
|
bundle_dir: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk_content reads the content directory and returns all non-draft pages
|
// walk_content reads the content directory and returns all non-draft pages
|
||||||
@@ -30,6 +32,7 @@ Page :: struct {
|
|||||||
walk_content :: proc(content_path: string, include_drafts: bool) -> []Page {
|
walk_content :: proc(content_path: string, include_drafts: bool) -> []Page {
|
||||||
pages: [dynamic]Page
|
pages: [dynamic]Page
|
||||||
|
|
||||||
|
collect_home(&pages, content_path)
|
||||||
collect_standalone(&pages, content_path)
|
collect_standalone(&pages, content_path)
|
||||||
|
|
||||||
posts_path := fmt.tprintf("%s/posts", 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[:]
|
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) {
|
collect_standalone :: proc(pages: ^[dynamic]Page, content_path: string) {
|
||||||
entries, err := os.read_all_directory_by_path(content_path, context.allocator)
|
entries, err := os.read_all_directory_by_path(content_path, context.allocator)
|
||||||
if err != nil {
|
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)
|
defer os.file_info_slice_delete(entries, context.allocator)
|
||||||
|
|
||||||
for entry in entries {
|
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
|
continue
|
||||||
}
|
}
|
||||||
if entry.type != .Regular {
|
if entry.type != .Regular {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
slug := entry.name[:len(entry.name) - 3]
|
slug := strip_extension(entry.name)
|
||||||
page, ok := load_page(entry.fullpath, .Standalone, slug)
|
page, ok := load_page(entry.fullpath, .Standalone, slug)
|
||||||
if ok {
|
if ok {
|
||||||
append(pages, page)
|
append(pages, page)
|
||||||
@@ -86,16 +113,19 @@ collect_posts :: proc(pages: ^[dynamic]Page, posts_path: string) {
|
|||||||
for entry in entries {
|
for entry in entries {
|
||||||
switch entry.type {
|
switch entry.type {
|
||||||
case .Regular:
|
case .Regular:
|
||||||
if !strings.has_suffix(entry.name, ".md") {
|
if !is_content_file(entry.name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
slug := entry.name[:len(entry.name) - 3]
|
slug := strip_extension(entry.name)
|
||||||
page, ok := load_page(entry.fullpath, .Post, slug)
|
page, ok := load_page(entry.fullpath, .Post, slug)
|
||||||
if ok {
|
if ok {
|
||||||
append(pages, page)
|
append(pages, page)
|
||||||
}
|
}
|
||||||
case .Directory:
|
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) {
|
if !os.exists(index_path) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -105,7 +135,6 @@ collect_posts :: proc(pages: ^[dynamic]Page, posts_path: string) {
|
|||||||
append(pages, page)
|
append(pages, page)
|
||||||
}
|
}
|
||||||
case .Undetermined, .Symlink, .Named_Pipe, .Socket, .Block_Device, .Character_Device:
|
case .Undetermined, .Symlink, .Named_Pipe, .Socket, .Block_Device, .Character_Device:
|
||||||
// Do nothing
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -131,17 +160,25 @@ load_page :: proc(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
page.type = page_type
|
page.type = page_type
|
||||||
page.slug = slug
|
page.slug = slug
|
||||||
page.title = fm.title
|
page.title = fm.title
|
||||||
page.date = fm.date
|
page.description = fm.description
|
||||||
page.draft = fm.draft
|
page.date = fm.date
|
||||||
page.is_starred = fm.isStarred
|
page.draft = fm.draft
|
||||||
page.menu = fm.menu
|
page.is_starred = fm.isStarred
|
||||||
page.body = strings.clone(body)
|
page.menu = fm.menu
|
||||||
page.body_html = cm.markdown_to_html_from_string(body, {.Unsafe})
|
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 {
|
switch page_type {
|
||||||
|
case .Home:
|
||||||
|
page.permalink = "/"
|
||||||
case .Post:
|
case .Post:
|
||||||
page.permalink = fmt.aprintf("/posts/%s/", slug)
|
page.permalink = fmt.aprintf("/posts/%s/", slug)
|
||||||
case .Standalone:
|
case .Standalone:
|
||||||
@@ -152,3 +189,37 @@ load_page :: proc(
|
|||||||
return
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+4
-2
@@ -6,6 +6,7 @@ import "core:strings"
|
|||||||
|
|
||||||
Frontmatter :: struct {
|
Frontmatter :: struct {
|
||||||
title: string,
|
title: string,
|
||||||
|
description: string,
|
||||||
date: string,
|
date: string,
|
||||||
publishDate: string,
|
publishDate: string,
|
||||||
draft: bool,
|
draft: bool,
|
||||||
@@ -44,8 +45,9 @@ parse_frontmatter :: proc(content: string) -> (fm: Frontmatter, body: string, ok
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fm.title = json_get_string(obj, "title")
|
fm.title = json_get_string(obj, "title")
|
||||||
fm.date = json_get_string(obj, "date")
|
fm.description = json_get_string(obj, "description")
|
||||||
|
fm.date = json_get_string(obj, "date")
|
||||||
fm.publishDate = json_get_string(obj, "publishDate")
|
fm.publishDate = json_get_string(obj, "publishDate")
|
||||||
fm.draft = json_get_bool(obj, "draft")
|
fm.draft = json_get_bool(obj, "draft")
|
||||||
fm.isStarred = json_get_bool(obj, "isStarred")
|
fm.isStarred = json_get_bool(obj, "isStarred")
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ main :: proc() {
|
|||||||
|
|
||||||
pages := walk_content(opt.content_dir, opt.drafts)
|
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) {
|
load_default_options :: proc(opt: ^Options) {
|
||||||
|
|||||||
+58
-34
@@ -4,45 +4,58 @@ import "core:fmt"
|
|||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
|
||||||
SITE_TITLE :: "One Idiot Developer"
|
|
||||||
SITE_DESC :: "This is fine...right?"
|
|
||||||
|
|
||||||
@(rodata)
|
|
||||||
MONTHS: [12]string = {
|
MONTHS: [12]string = {
|
||||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||||
}
|
}
|
||||||
|
|
||||||
HEADER :: `
|
render_site :: proc(pages: []Page, content_path: string, output_dir: string) {
|
||||||
<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) {
|
|
||||||
sort_pages_by_date(pages)
|
sort_pages_by_date(pages)
|
||||||
|
|
||||||
|
// Find home page and extract site title
|
||||||
|
home: Page
|
||||||
|
has_home := false
|
||||||
|
site_title := "Site"
|
||||||
|
|
||||||
for page in pages {
|
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)
|
write_page(output_dir, page.permalink, html)
|
||||||
}
|
}
|
||||||
|
|
||||||
home_html := render_home_html(pages)
|
// Render home page
|
||||||
write_file(fmt.tprintf("%s/index.html", output_dir), home_html)
|
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)
|
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(
|
body := fmt.aprintf(
|
||||||
`<main>
|
`<main>
|
||||||
<article class="prose">
|
<article class="prose">
|
||||||
@@ -55,15 +68,18 @@ render_page_html :: proc(page: Page) -> string {
|
|||||||
render_date(page),
|
render_date(page),
|
||||||
page.body_html,
|
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)
|
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
|
items: [dynamic]string
|
||||||
defer delete(items)
|
defer delete(items)
|
||||||
|
|
||||||
for page in pages {
|
for page in pages {
|
||||||
|
if page.type == .Home {
|
||||||
|
continue
|
||||||
|
}
|
||||||
append(&items, render_post_item(page))
|
append(&items, render_post_item(page))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,24 +88,20 @@ render_home_html :: proc(pages: []Page) -> string {
|
|||||||
body := fmt.aprintf(
|
body := fmt.aprintf(
|
||||||
`<main>
|
`<main>
|
||||||
<header class="text-center mb-28">
|
<header class="text-center mb-28">
|
||||||
<img class="mx-auto w-18 h-18 rounded-full" src="/avatar.jpg">
|
%s </header>
|
||||||
<h1 class="text-2xl/12 mt-2.5 mb-0 font-bold">%s</h1>
|
|
||||||
<p class="text-slate-400">%s</p>
|
|
||||||
</header>
|
|
||||||
<ul>
|
<ul>
|
||||||
%s
|
%s
|
||||||
</ul>
|
</ul>
|
||||||
</main>
|
</main>
|
||||||
`,
|
`,
|
||||||
SITE_TITLE,
|
home.body_html,
|
||||||
SITE_DESC,
|
|
||||||
post_list,
|
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
|
parts: [dynamic]string
|
||||||
defer delete(parts)
|
defer delete(parts)
|
||||||
|
|
||||||
@@ -123,7 +135,7 @@ render_posts_html :: proc(pages: []Page) -> string {
|
|||||||
append(&parts, "</main>\n")
|
append(&parts, "</main>\n")
|
||||||
|
|
||||||
body := strings.join(parts[:], "")
|
body := strings.join(parts[:], "")
|
||||||
title := fmt.tprintf("Posts | %s", SITE_TITLE)
|
title := fmt.tprintf("Posts | %s", site_title)
|
||||||
return render_chrome(title, body)
|
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 {
|
render_post_item :: proc(page: Page) -> string {
|
||||||
date_html := ""
|
date_html := ""
|
||||||
if page.date != "" {
|
if page.date != "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user