builder v1
sageclove
Wed, 01 Jan 2025 20:27:27 -0700
26 files changed,
591 insertions(+),
251 deletions(-)
jump to
A
.vscode/launch.json
@@ -0,0 +1,17 @@
+{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${fileDirname}", + "console": "integratedTerminal", + "args": ["../"] + } + ] +}
M
build/build.go
→
build/build.go
@@ -1,5 +1,191 @@
+// https://gobyexample.com/ + package main +import ( + "bufio" + "io" + "os" + "path/filepath" + "strconv" + "strings" + "text/template" + "time" +) + +type PageProperties struct { + Title string + StylePath string + HeaderLinks [][]*HeaderLink +} + +type HeaderLink struct { + Name string + Active bool +} + +type Page struct { + Name string + Parent *Page + Children []*Page +} + +var rootPath = "." +var now = time.Now() + func main() { + args := os.Args[1:] + if len(args) != 0 { + rootPath = args[0] + } + os.RemoveAll(filepath.Join("out")) + os.Mkdir(filepath.Join("out"), 0777) + buildPage(buildPageLinkedList()) + + cssFS := os.DirFS(filepath.Join(rootPath, "css")) + os.CopyFS(filepath.Join("out", "css"), cssFS) + os.Rename( + filepath.Join("out", "css", "style.css"), + filepath.Join("out", "css", "style."+strconv.FormatInt(now.Unix(), 10)+".css"), + ) + + assetsFS := os.DirFS(filepath.Join(rootPath, "assets")) + os.CopyFS(filepath.Join("out", "assets"), assetsFS) } + +func buildPageLinkedList() *Page { + rootNode := &Page{ + Name: "index", + Parent: nil, + } + parentNode := rootNode + tailNode := rootNode + currentDepth := 0 + tabFile, _ := os.Open(filepath.Join(rootPath, "site.tab")) + scanner := bufio.NewScanner(tabFile) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + depth := strings.Count(scanner.Text(), " ") + pageName := strings.TrimLeft(scanner.Text(), " ") + if len(pageName) > 0 { + if depth == currentDepth { + newNode := &Page{ + Name: pageName, + Parent: parentNode, + } + parentNode.Children = append(parentNode.Children, newNode) + tailNode = newNode + } else if depth > currentDepth { + currentDepth = depth + parentNode = tailNode + newNode := &Page{ + Name: pageName, + Parent: parentNode, + } + parentNode.Children = append(parentNode.Children, newNode) + tailNode = newNode + } else if depth < currentDepth { + unwind := (currentDepth - depth) + 1 + currentDepth = depth + for range unwind { + tailNode = tailNode.Parent + } + parentNode = tailNode + newNode := &Page{ + Name: pageName, + Parent: parentNode, + } + parentNode.Children = append(parentNode.Children, newNode) + tailNode = newNode + } + } + } + return rootNode +} + +func parentLinks(page *Page) []*HeaderLink { + if page.Parent == nil { + return nil + } + if page.Parent.Parent == nil { + return nil + } + parentPages := page.Parent.Parent.Children + parentLinks := make([]*HeaderLink, len(parentPages)) + for i, parentPage := range parentPages { + parentLinks[i] = &HeaderLink{ + Name: parentPage.Name, + Active: page.Parent == parentPage, + } + } + return parentLinks +} + +func siblingLinks(page *Page) []*HeaderLink { + if page.Parent == nil { + return nil + } + siblingPages := page.Parent.Children + siblingLinks := make([]*HeaderLink, len(siblingPages)) + for i, siblingPage := range siblingPages { + siblingLinks[i] = &HeaderLink{ + Name: siblingPage.Name, + Active: page == siblingPage, + } + } + return siblingLinks +} + +func childLinks(page *Page) []*HeaderLink { + childPages := page.Children + childLinks := make([]*HeaderLink, len(childPages)) + for i, childPage := range childPages { + childLinks[i] = &HeaderLink{ + Name: childPage.Name, + Active: false, + } + } + return childLinks +} + +func buildPage(page *Page) { + parents := parentLinks(page) + siblings := siblingLinks(page) + children := childLinks(page) + headerLinks := [][]*HeaderLink{parents, siblings, children} + // https://abhinavg.net/2019/07/11/zero-alloc-slice-filter/ + filteredHeaderLinks := headerLinks[:0] + for _, headerLink := range headerLinks { + if headerLink != nil { + filteredHeaderLinks = append(filteredHeaderLinks, headerLink) + } + } + pageFilePath := filepath.Join(rootPath, "site", page.Name+".html") + pageFileInfo, _ := os.Stat(pageFilePath) + if pageFileInfo == nil { + dst, _ := os.Create(pageFilePath) + src, _ := os.Open(filepath.Join(rootPath, "templates", "page.html")) + io.Copy(dst, src) + dst.Sync() + } + outfile, _ := os.Create(filepath.Join("out", page.Name+".html")) + template, _ := template.ParseFiles( + filepath.Join(rootPath, "templates", "document.html"), + filepath.Join(rootPath, "templates", "header.html"), + filepath.Join(rootPath, "site", page.Name+".html"), + ) + template.Execute( + outfile, + &PageProperties{ + Title: page.Name, + StylePath: "/css/style." + strconv.FormatInt(now.Unix(), 10) + ".css", + HeaderLinks: filteredHeaderLinks, + }, + ) + for _, child := range page.Children { + buildPage(child) + } +} + +func __(foo any) {}
A
build/readme.md
@@ -0,0 +1,15 @@
+## Requirements + +Go 1.23.4 + +## Usage + +`./build <ROOT>` + +`<ROOT>` should be a path to the site's source directory. The source directory must contain the following subdirectories: +- `css`: for CSS stylesheets +- `assets`: for other assets (e.g. images, files...) +- `site`: for site pages. The structure of this subdirectory determines the structure of the site itself. +- `templates`: for reused markup. + +If `<ROOT>` is omitted, the current directory will be used as the source directory.
M
css/style.css
→
css/style.css
@@ -16,5 +16,117 @@
--col-blue-t: #abd0ba; --col-purple-t: #e7c4a4; --col-green-t: #dcd9b0; - --col-orange-t: #efe2a9; -}+ --col-orange-t: #efe2a9; +} + +html { + background: var(--col-white); + color: var(--col-black); +} + +body { + margin: 1rem; +} + +h1, +h2 { + font-style: italic; +} + +hr { + border: 1px solid var(--col-gray); +} + +a { + color: var(--col-blue-l); +} + +.txt-hl-blue { + background: var(--col-blue-t); + color: var(--col-blue); +} + +.txt-hl-purple { + background: var(--col-purple-t); + color: var(--col-purple); +} + +.txt-hl-green { + background: var(--col-green-t); + color: var(--col-green); +} + +.txt-hl-orange { + background: var(--col-orange-t); + color: var(--col-orange); +} + +.header { + display: flex; + flex-direction: row; + } + .header .ident { + display: flex; + flex-direction: row; + } + .header .ident .name.mobile { + display: none; + align-self: center; + margin-left: 1rem; + } + .header .logo { + height: 5rem; + } + .header .name { + font-weight: bold; + font-size: 1.2rem; + margin-left: 2rem; + } + .header .links { + width: 100%; + } + .header ul { + padding: 0; + margin-left: 2rem; + margin-top: 0.5rem; + } + .header li { + font-size: 1rem; + list-style: none; + margin-top: 0.25rem; + } + .header li a { + padding: 0.1rem 0.25rem; + text-decoration: none; + color: var(--col-black); + } + .header li a:hover { + color: var(--col-white); + background: var(--col-black); + cursor: pointer; + } + .header li.selected a{ + color: var(--col-white); + background: var(--col-black); + } + .header .columns { + display: flex; + flex-direction: row; + } + @media screen and (max-width: 600px) { + .header { + flex-direction: column; + } + .header .ident .name.mobile { + display: block; + } + .header .logo { + height: 3rem; + } + .header .links .name { + display: none; + } + .header .links .columns>ul:first-child { + margin-left: 0; + } + }
D
html/index.html
@@ -1,244 +0,0 @@
-<!DOCTYPE html> -<html> - <head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width,initial-scale=1.0"> - <title>deserthorns</title> - <link rel="stylesheet" href="/css/style.css"> - <style> - html { - background: var(--col-white); - color: var(--col-black); - } - body { - margin: 1rem; - } - h1, - h2 { - font-style: italic; - } - hr { - border: 1px solid var(--col-gray); - } - a { - color: var(--col-blue-l); - } - .palette { - display: flex; - flex-direction: row; - } - .palette > div { - width: 3rem; - height: 3rem; - border-radius: 50%; - margin: 0.5rem; - } - .stripes > div { - height: 1rem; - } - .box { - width: 3rem; - height: 3rem; - border: 1px solid; - margin: 1rem; - } - .box.blue { - border-color: var(--col-blue); - background-color: var(--col-blue-t); - } - .box.purple { - border-color: var(--col-purple); - background-color: var(--col-purple-t); - } - .box.green { - border-color: var(--col-green); - background-color: var(--col-green-t); - } - .box.orange { - border-color: var(--col-orange); - background-color: var(--col-orange-t); - } - .txt-hl-blue { - background: var(--col-blue-t); - color: var(--col-blue); - } - - .txt-hl-purple { - background: var(--col-purple-t); - color: var(--col-purple); - } - - .txt-hl-green { - background: var(--col-green-t); - color: var(--col-green); - } - - .txt-hl-orange { - background: var(--col-orange-t); - color: var(--col-orange); - } - .header { - display: flex; - flex-direction: row; - } - .header .ident { - display: flex; - flex-direction: row; - } - .header .ident .name.mobile { - display: none; - align-self: center; - margin-left: 1rem; - } - .header .logo { - height: 5rem; - } - .header .name { - font-weight: bold; - font-size: 1.2rem; - margin-left: 2rem; - } - .header .links { - width: 100%; - } - .header ul { - padding: 0; - margin-left: 2rem; - margin-top: 0.5rem; - } - .header li { - font-size: 1rem; - list-style: none; - margin-top: 0.25rem; - } - .header li span { - padding: 0.1rem 0.25rem; - } - .header li span:hover { - color: var(--col-white); - background: var(--col-black); - cursor: pointer; - } - .header li.selected span{ - color: var(--col-white); - background: var(--col-black); - } - .header .columns { - display: flex; - flex-direction: row; - } - @media screen and (max-width: 600px) { - .header { - flex-direction: column; - } - .header .ident .name.mobile { - display: block; - } - .header .logo { - height: 3rem; - } - .header .links .name { - display: none; - } - .header .links .columns>ul:first-child { - margin-left: 0; - } - } - </style> - </head> - <body> - <div class="header"> - <div class="ident"> - <img class="logo" src="/assets/svg/logo-block-mono.svg"> - <span class="name mobile">deserthorns</span> - </div> - <div class="links"> - <span class="name">deserthorns</span> - <div class="columns"> - <ul> - <li> - <span>creative</span> - <li> - <span>knowledge</span> - </li> - <li class="selected"> - <span>meta</span> - </li> - </ul> - <ul> - <li> - <span>about</span> - </li> - <li class="selected"> - <span>style</span> - </li> - <li> - <span>links - </span> - </li> - </ul> - </div> - </div> - </div> - <h1>Style</h1> - <h2>Colors - Dark</h2> - <strong>Uses:</strong> borders, highlighted text, decoration - <div class="palette"> - <div style="background: var(--col-blue);"></div> - <div style="background: var(--col-purple);"></div> - <div style="background: var(--col-green);"></div> - <div style="background: var(--col-orange);"></div> - </div> - <hr> - <h2>Colors - Light</h2> - <strong>Uses:</strong> colored text - <div class="palette"> - <div style="background: var(--col-blue-l);"></div> - <div style="background: var(--col-purple-l);"></div> - <div style="background: var(--col-green-l);"></div> - <div style="background: var(--col-orange-l);"></div> - </div> - <hr> - <h2>Colors - Tints</h2> - <strong>Uses:</strong> text highlights, contrast areas/cards - <div class="palette"> - <div style="background: var(--col-blue-t);"></div> - <div style="background: var(--col-purple-t);"></div> - <div style="background: var(--col-green-t);"></div> - <div style="background: var(--col-orange-t);"></div> - </div> - <hr> - <h2>Contrasts</h2> - <strong>Uses:</strong> text, structure, contrast areas/cards - <div class="palette"> - <div style="background: var(--col-black);"></div> - <div style="background: var(--col-gray);"></div> - </div> - <hr> - <div class="palette"> - <div class="box blue"></div> - <div class="box purple"></div> - <div class="box green"></div> - <div class="box orange"></div> - </div> - <p> - Text with <span style="color: var(--col-blue-l)">colors</span> and <span style="color: var(--col-purple-l)">colors</span> and <span style="color: var(--col-orange-l)">colors</span> and <span style="color: var(--col-green-l)">colors</span>. - </p> - <p> - Text with highlight <span class="txt-hl-blue">colors</span> and <span class="txt-hl-purple">colors</span> and <span class="txt-hl-green">colors</span> and <span class="txt-hl-orange">colors</span>. - </p> - <p> - Text with an <a href="https://deserthorns.net">inline link</a> in it. - </p> - <pre> -Preformatted text... -<span style="color: var(--col-orange)">...with color!</span> - </pre> - <pre style="background-color: var(--col-gray);"> -// Code block -public foo() { - return "bar"; -} -</pre> - </body> -</html>
D
pull.sh
@@ -1,3 +0,0 @@
-#!/bin/bash - -rsync -e "ssh -i /home/sage/.ssh/kiko" -r sage@deserthorns.net:/var/www/ $(pwd)/
A
site.tab
@@ -0,0 +1,13 @@
+creative + music + visuals + +knowledge + tools + +meta + about + style + links + foo + bar
A
site/about.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/bar.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/creative.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/foo.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
M
site/index.html
→
site/index.html
@@ -0,0 +1,9 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +<h1>main</h1> +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/knowledge.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/links.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/meta.html
@@ -0,0 +1,9 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +<h1>meta</h1> +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/music.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/style.html
@@ -0,0 +1,106 @@
+{{ define "styles" }} +<style> +.palette { + display: flex; + flex-direction: row; +} +.palette > div { + width: 3rem; + height: 3rem; + border-radius: 50%; + margin: 0.5rem; +} +.stripes > div { + height: 1rem; +} +.box { + width: 3rem; + height: 3rem; + border: 1px solid; + margin: 1rem; +} +.box.blue { + border-color: var(--col-blue); + background-color: var(--col-blue-t); +} +.box.purple { + border-color: var(--col-purple); + background-color: var(--col-purple-t); +} +.box.green { + border-color: var(--col-green); + background-color: var(--col-green-t); +} +.box.orange { + border-color: var(--col-orange); + background-color: var(--col-orange-t); +} +</style> +{{ end }} + + +{{ define "content" }} +<h1>Style</h1> +<h2>Colors - Dark</h2> +<strong>Uses:</strong> borders, highlighted text, decoration +<div class="palette"> + <div style="background: var(--col-blue);"></div> + <div style="background: var(--col-purple);"></div> + <div style="background: var(--col-green);"></div> + <div style="background: var(--col-orange);"></div> +</div> +<hr> +<h2>Colors - Light</h2> +<strong>Uses:</strong> colored text +<div class="palette"> + <div style="background: var(--col-blue-l);"></div> + <div style="background: var(--col-purple-l);"></div> + <div style="background: var(--col-green-l);"></div> + <div style="background: var(--col-orange-l);"></div> +</div> +<hr> +<h2>Colors - Tints</h2> +<strong>Uses:</strong> text highlights, contrast areas/cards +<div class="palette"> + <div style="background: var(--col-blue-t);"></div> + <div style="background: var(--col-purple-t);"></div> + <div style="background: var(--col-green-t);"></div> + <div style="background: var(--col-orange-t);"></div> +</div> +<hr> +<h2>Contrasts</h2> +<strong>Uses:</strong> text, structure, contrast areas/cards +<div class="palette"> + <div style="background: var(--col-black);"></div> + <div style="background: var(--col-gray);"></div> +</div> +<hr> +<div class="palette"> + <div class="box blue"></div> + <div class="box purple"></div> + <div class="box green"></div> + <div class="box orange"></div> +</div> +<p> + Text with <span style="color: var(--col-blue-l)">colors</span> and <span style="color: var(--col-purple-l)">colors</span> and <span style="color: var(--col-orange-l)">colors</span> and <span style="color: var(--col-green-l)">colors</span>. +</p> +<p> + Text with highlight <span class="txt-hl-blue">colors</span> and <span class="txt-hl-purple">colors</span> and <span class="txt-hl-green">colors</span> and <span class="txt-hl-orange">colors</span>. +</p> +<p> + Text with an <a href="https://deserthorns.net">inline link</a> in it. +</p> +<pre> +Preformatted text... +<span style="color: var(--col-orange)">...with color!</span> +</pre> +<pre style="background-color: var(--col-gray);"> +// Code block +public foo() { + return "bar"; +} +</pre> +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/tools.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
site/visuals.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}
A
templates/document.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width,initial-scale=1.0"> + <title>{{.Title}}</title> + <link rel="stylesheet" href="{{.StylePath}}"> + {{ template "styles"}} + </head> + <body> + {{ template "header" . }} + {{ template "content" }} + </body> + {{ template "scripts" }} +</html>
A
templates/header.html
@@ -0,0 +1,22 @@
+{{ define "header" }} +<div class="header"> + <div class="ident"> + <img class="logo" src="/assets/svg/logo-block-mono.svg"> + <span class="name mobile">deserthorns</span> + </div> + <div class="links"> + <span class="name">deserthorns</span> + <div class="columns"> + {{ range .HeaderLinks }} + <ul> + {{ range . }} + <li {{ if .Active }} class="selected" {{ end }}> + <a href="{{.Name}}">{{ .Name }}</a> + </li> + {{ end }} + </ul> + {{ end }} + </div> + </div> +</div> +{{ end }}
A
templates/page.html
@@ -0,0 +1,8 @@
+{{ define "styles" }} +{{ end }} + +{{ define "content" }} +{{ end }} + +{{ define "scripts" }} +{{ end }}