blog @main -
refs -
log -
-
https://git.jolheiser.com/blog.git
My nonexistent blog
wip
Signed-off-by: jolheiser <john.olheiser@gmail.com>
Signature
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEgqEQpE3xoo1QwJO/uFOtpdp7v3oFAmXWxIoACgkQuFOtpdp7
v3okNhAAk+lCQ+Qotg3zgadBbvYkHErqQXQrwx+eXWH7WeGg3B0Qj67+fN2W8fx7
aoGhB0JltJPpMT+lCAM7vW4gmAi918/PC6s35lZ9QsABBSlxVKoxQat7ZLOoFFey
BsDCpDdsPcXQ2/Qgr1Vop+gNmTGUMfm5yBfs+E9vHaKQcwq5+I5/dZloe75d+nn1
EPRQm8tseGqtO41ZqpZqFGf+qoh23IXTkrrxoC2P3BOxtJJwQXKEi9n4Aoh8rr/E
ZQ1P1pL00VSu9QhrWUq9FsPB90uU2aP518/htiD1lJlFNB9Rv+5TJGTha3Jali4g
StR3Zy+DWU+1BlKmGNP0hSvcm3+yvIisV/Mco+3bc1xMJlDTe9gYQtLIXDNQhWBG
z1BVXT/ITKeHbclZdQpqCwtdLxvvHMeHXlN9KDaihvWXOAIFTd6M9jsdhgNqE2g0
AWfKqzcoCLMsBtc3JRUIypPnwksgdmGK/5zIf2sMw9vJvZd57PdxF3Cv2hjVvfCh
I1s4nEATpK1GqPZgmS6nqq9hmHEklqw4idXqQqI9bb2EIGc+YW8R+ukVBN66jaGK
HxOZ5ighJlM80DszW9/HlLnk0RMi404zhdxKf+imklhANl6r2cJ7zbvJIgEjCwq5
cw9L1KYTRnHT9XtiNOoKlqkMnU1D+hY9SWSlW0JWd/geV+UMS4A=
=Tt3l
-----END PGP SIGNATURE-----
18 changed files, 524 additions(+), 661 deletions(-)
diff --git a/.gitignore b/.gitignore
index c6aa2a4851f3a5271fdf9b86ce78e6e8ce1cbcef..89f9ac04aac6c8ee66e158853e7d0439b3ec782d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1 @@
-/blog*
-!blog.go
-!blog_test.go
-
-_example/out
+out/
diff --git a/_example/README.md b/_example/README.md
deleted file mode 100644
index 9a7224f619191edd2bcdf5d9f5acc90976f2e60d..0000000000000000000000000000000000000000
--- a/_example/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Example blog structure
-
-The `articles` dir contains a list of articles, namely this one.
-
-The `templates` dir contains a handful of [go templates](https://pkg.go.dev/html/template) that make up how to render the blog.
-- `article.tmpl` is a template for an individual article (this page).
-- `index.tmpl` is the main blog overview.
-- `base.tmpl` is a collection of [defined blocks](https://pkg.go.dev/text/template#hdr-Nested_template_definitions)
-that are then re-used in `index.tmpl` and `article.tmpl` to create a cohesive look-and-feel.
-
-Finally, `config.yaml` (which can also be TOML if desired) allows you to not have to continuously re-type all the flags to define things like your
-`--article-dir`, `--template-dir`, or `--out`.
-
diff --git a/_example/articles/introduction.md b/articles/introduction.md
rename from _example/articles/introduction.md
rename to articles/introduction.md
index 67956be729182b0ac63aa2360d99554f7152f141..221258b975335736ddb331fa0f3d20d269d75495 100644
--- a/_example/articles/introduction.md
+++ b/articles/introduction.md
@@ -1,12 +1,14 @@
----
-title: "Introduction"
-summary: "An introduction of `blog`"
+time: 2024-02-17
time: 2024-02-17
+time: 2024-02-17
category: Miscellaneous
+time: 2024-02-17
authors:
+time: 2024-02-17
- name: "jolheiser"
+time: 2024-02-17
email: "john+blog@jolheiser.com"
----
++++
# Hello and welcome to blog!
@@ -64,3 +66,4 @@ that are then re-used in `index.tmpl` and `article.tmpl` to create a cohesive look-and-feel.
Finally, `config.yaml` (which can also be TOML if desired) allows you to not have to continuously re-type all the flags to define things like your
`--article-dir`, `--template-dir`, or `--out`.
+
diff --git a/_example/config.yaml b/_example/config.yaml
deleted file mode 100644
index 914646cc3ba1a16edd9b65861c517ff51634acc7..0000000000000000000000000000000000000000
--- a/_example/config.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-article-dir: articles
-template-dir: templates
-out-dir: out
-author:
- name: jolheiser
- email: john+blog@jolheiser.com
- links:
- - name: GitHub
- url: "https://github.com/jolheiser"
diff --git a/_example/templates/article.tmpl b/_example/templates/article.tmpl
deleted file mode 100644
index db4e9ecb5eab85d4a3d0f5bbc553b3b5f6659c02..0000000000000000000000000000000000000000
--- a/_example/templates/article.tmpl
+++ /dev/null
@@ -1,8 +0,0 @@
-{{template "head" .Article.Title}}
-<body>
-<header>
- <a href="index.html">{{.Author.Name}}</a>
-</header>
-<hr/>
-<main>{{.Article.Content}}</main>
-</body>
diff --git a/_example/templates/base.tmpl b/_example/templates/base.tmpl
deleted file mode 100644
index 7a05f6c9c322f6db80c4885b595f3a2c944552ee..0000000000000000000000000000000000000000
--- a/_example/templates/base.tmpl
+++ /dev/null
@@ -1,8 +0,0 @@
-{{define "head"}}
-<!DOCTYPE html>
-<head>
-<title>{{.}}</title>
-<link id="style" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"/>
-<link rel="stylesheet" href="chroma.css"/>
-</head>
-{{end}}
diff --git a/_example/templates/index.tmpl b/_example/templates/index.tmpl
deleted file mode 100644
index 20ddba201b1aab26c30e6d8bc357fddfaaed7f0b..0000000000000000000000000000000000000000
--- a/_example/templates/index.tmpl
+++ /dev/null
@@ -1,17 +0,0 @@
-{{template "head" (printf "%s's Blog" .Author.Name)}}
-<body>
-<header>
- <h1>{{.Author.Name}}</h1>
- <p>I really like to hack on Gitea and I use Catppuccin (btw)</p>
-</header>
-<main>
-{{range $category, $articles := .ArticlesByCategory}}
- <h3>{{$category}}</h3>
- <ul>
- {{range $article := $articles}}
- <li><a href="{{$article.Filename}}.html">{{$article.Title}}</a> ({{$article.Time.Format "01/02/2006"}}) - {{$article.Summary}}</li>
- {{end}}
- </ul>
-{{end}}
-</main>
-</body>
diff --git a/blog.go b/blog.go
deleted file mode 100644
index 21f761408aedd5cedc2f77052c46a8941dca5727..0000000000000000000000000000000000000000
--- a/blog.go
+++ /dev/null
@@ -1,244 +0,0 @@
-package blog
-
-import (
- "bytes"
- "errors"
- "fmt"
- "html/template"
- "io"
- "io/fs"
- "net/url"
- "os"
- "path/filepath"
- "sort"
- "strings"
- "time"
-
- "github.com/bmatcuk/doublestar/v4"
- "github.com/pelletier/go-toml/v2"
- "gopkg.in/yaml.v3"
-)
-
-// Blog is a collection of [Article]
-type Blog struct {
- indexTemplate *template.Template
- articleTemplate *template.Template
- Articles []Article
- Author Author
-}
-
-// Article is a blog post/article
-type Article struct {
- Filename string
- Content template.HTML
- ArticleMeta
-}
-
-// ArticleMeta is the metadata of an [Article]
-// The meta is parsed from frontmatter in an article
-type ArticleMeta struct {
- Title string
- Subtitle string
- Summary string
- Time time.Time
- Author Author
- Tags []string
- Category string
-}
-
-// Author is an author of a blog/post
-type Author struct {
- Name string
- Job string
- Email string
- Links []Link
-}
-
-// Link is a link name and URL
-type Link struct {
- Name string
- URL LinkURL
-}
-
-// LinkURL is a URL
-type LinkURL url.URL
-
-func (l *LinkURL) UnmarshalText(data []byte) error {
- u, err := url.Parse(string(data))
- if err != nil {
- return err
- }
- *l = LinkURL(*u)
- return nil
-}
-
-// NewBlog constructs a new blog from articles in articleDir and templates in templateDir
-func NewBlog(articleDir, templateDir string, author Author) (*Blog, error) {
- tmpl, err := parseTemplates(os.DirFS(templateDir))
- if err != nil {
- return nil, fmt.Errorf("could not parse templates in %q: %w", templateDir, err)
- }
- indexTmpl := tmpl.Lookup("index.tmpl")
- if indexTmpl == nil {
- indexTmpl = tmpl.Lookup("index")
- if indexTmpl == nil {
- return nil, errors.New("`index` template is required but was not found")
- }
- }
- articleTmpl := tmpl.Lookup("article.tmpl")
- if articleTmpl == nil {
- articleTmpl = tmpl.Lookup("article")
- if articleTmpl == nil {
- return nil, errors.New("`article` template is required but was not found")
- }
- }
- articles, err := parseArticles(os.DirFS(articleDir))
- if err != nil {
- return nil, fmt.Errorf("could not parse articles in %q: %w", articleDir, err)
- }
- return &Blog{
- indexTemplate: indexTmpl,
- articleTemplate: articleTmpl,
- Articles: articles,
- Author: author,
- }, nil
-}
-
-// Index renders the blog index to w
-func (b *Blog) Index(w io.Writer) error {
- byCat := make(map[string][]Article)
- for _, article := range b.Articles {
- byCat[article.Category] = append(byCat[article.Category], article)
- }
- return b.indexTemplate.Execute(w, map[string]any{
- "Articles": b.Articles,
- "ArticlesByCategory": byCat,
- "Author": b.Author,
- })
-}
-
-// Article renders an article to w
-func (b *Blog) Article(w io.Writer, a Article) error {
- return b.articleTemplate.Execute(w, map[string]any{
- "Article": a,
- "Author": b.Author,
- })
-}
-
-func parseTemplates(fs fs.FS) (*template.Template, error) {
- matches, err := doublestar.Glob(fs, "**/*.{tmpl,gohtml}")
- if err != nil {
- return nil, fmt.Errorf("could not glob templates: %w", err)
- }
- tmpl, err := template.New("").ParseFS(fs, matches...)
- if err != nil {
- return nil, fmt.Errorf("could not parse templates: %w", err)
- }
- return tmpl, nil
-}
-
-func parseArticles(fs fs.FS) ([]Article, error) {
- matches, err := doublestar.Glob(fs, "**/*.md")
- if err != nil {
- return nil, fmt.Errorf("could not glob articles: %w", err)
- }
- articles := make([]Article, 0, len(matches))
- for _, match := range matches {
- if err := func() error {
- fi, err := fs.Open(match)
- if err != nil {
- return err
- }
- defer fi.Close()
-
- content, err := io.ReadAll(fi)
- if err != nil {
- return err
- }
-
- article, err := parseArticle(string(content))
- if err != nil {
- return err
- }
- article.Filename = strings.TrimSuffix(filepath.Base(match), filepath.Ext(match))
- articles = append(articles, article)
- return nil
- }(); err != nil {
- return nil, err
- }
- }
- sort.SliceStable(articles, func(i, j int) bool {
- return articles[i].Time.After(articles[j].Time)
- })
- return articles, nil
-}
-
-func parseArticle(content string) (Article, error) {
- lines := strings.Split(content, "\n")
-
- start, end := -1, -1
- var isSep func(string) bool
- var decoder func([]byte, any) error
-
- for idx, line := range lines {
- if strings.TrimSpace(line) == "" {
- continue
- }
- if isSep != nil && isSep(line) {
- end = idx
- break
- }
-
- if isTOMLSeparator(line) {
- start = idx
- isSep = isTOMLSeparator
- decoder = toml.Unmarshal
- continue
- }
-
- if isYAMLSeparator(line) {
- start = idx
- isSep = isYAMLSeparator
- decoder = yaml.Unmarshal
- continue
- }
- }
-
- var meta ArticleMeta
- body := content
- if start != -1 && end != -1 {
- body = strings.Join(lines[end+1:], "\n")
- if err := decoder([]byte(strings.Join(lines[start+1:end], "\n")), &meta); err != nil {
- return Article{}, fmt.Errorf("could not parse frontmatter: %w", err)
- }
- }
-
- var buf bytes.Buffer
- err := Markdown.Convert([]byte(body), &buf)
- if err != nil {
- return Article{}, fmt.Errorf("could not convert article: %w", err)
- }
-
- return Article{
- Content: template.HTML(buf.String()),
- ArticleMeta: meta,
- }, nil
-}
-
-func isTOMLSeparator(line string) bool {
- for _, char := range line {
- if char != '+' {
- return false
- }
- }
- return true
-}
-
-func isYAMLSeparator(line string) bool {
- for _, char := range line {
- if char != '-' {
- return false
- }
- }
- return true
-}
diff --git a/blog_test.go b/blog_test.go
deleted file mode 100644
index 821a659ae04f26a6c4bb1157f8c4835e58de07b2..0000000000000000000000000000000000000000
--- a/blog_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package blog
-
-import (
- "net/url"
- "testing"
- "time"
-
- "github.com/alecthomas/assert"
-)
-
-func TestParseArticle(t *testing.T) {
- var (
- raw = `+++
-title = "Honk"
-subtitle = "Bonk"
-summary = """This
-is
-a
-summary"""
-time = 2024-02-16
-tags = ["bocchi", "rocks"]
-
-[author]
-name = "Bocchi"
-job = "Guitarist"
-email = "bocchi@rock.s"
-
-[[author.links]]
-name = "website"
-url = "https://example.com/bocchi"
-+++
-
-# Hello world
-
-Beep boop`
- meta = ArticleMeta{
- Title: "Honk",
- Subtitle: "Bonk",
- Summary: `This
-is
-a
-summary`,
- Time: func() time.Time {
- t, _ := time.ParseInLocation("2006-01-02", "2024-02-16", time.Local)
- return t
- }(),
- Author: Author{
- Name: "Bocchi",
- Job: "Guitarist",
- Email: "bocchi@rock.s",
- Links: []Link{
- {
- Name: "website",
- URL: func() LinkURL {
- u, _ := url.Parse("https://example.com/bocchi")
- return LinkURL(*u)
- }(),
- },
- },
- },
- Tags: []string{"bocchi", "rocks"},
- }
- body = `<h1 id="hello-world">Hello world</h1>
-<p>Beep boop</p>
-`
- )
-
- article, err := parseArticle(raw)
- assert.NoError(t, err)
- assert.Equal(t, meta, article.ArticleMeta)
- assert.Equal(t, body, article.Content)
-}
diff --git a/cmd/blog/main.go b/cmd/blog/main.go
deleted file mode 100644
index d5d835e2eab4153e10fc157dd07aec9cbb7507d4..0000000000000000000000000000000000000000
--- a/cmd/blog/main.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "os"
- "path/filepath"
-
- "github.com/pelletier/go-toml/v2"
- "go.jolheiser.com/blog"
- "gopkg.in/yaml.v3"
-)
-
-type args struct {
- ArticleDir string `yaml:"article-dir" toml:"article-dir"`
- TemplateDir string `yaml:"template-dir" toml:"template-dir"`
- OutDir string `yaml:"out-dir" toml:"out-dir"`
- Author blog.Author `yaml:"author" toml:"author"`
-}
-
-func main() {
- if err := maine(); err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
-}
-
-func maine() error {
- var args args
-
- fs := flag.NewFlagSet("blog", flag.ExitOnError)
-
- configFlag := fs.String("config", "", "Configuration, TOML or YAML")
- fs.StringVar(configFlag, "c", *configFlag, "--config")
- articlesFlag := fs.String("articles", "", "Path to articles")
- fs.StringVar(articlesFlag, "a", *articlesFlag, "--articles")
- templatesFlag := fs.String("templates", "", "Path to templates")
- fs.StringVar(templatesFlag, "t", *templatesFlag, "--templates")
- outFlag := fs.String("out", "", "Path to output")
- fs.StringVar(outFlag, "o", *outFlag, "--out")
-
- if err := fs.Parse(os.Args[1:]); err != nil {
- return err
- }
-
- if *configFlag != "" {
- data, err := os.ReadFile(*configFlag)
- if err != nil {
- return err
- }
- switch ext := filepath.Ext(*configFlag); ext {
- case ".yaml", ".yml":
- if err := yaml.Unmarshal(data, &args); err != nil {
- return err
- }
- case ".toml":
- if err := toml.Unmarshal(data, &args); err != nil {
- return err
- }
- default:
- return fmt.Errorf("could not determine config type %q", ext)
- }
- }
-
- if *articlesFlag != "" {
- args.ArticleDir = *articlesFlag
- }
- if args.ArticleDir == "" {
- args.ArticleDir = "articles"
- }
- if *templatesFlag != "" {
- args.TemplateDir = *templatesFlag
- }
- if args.TemplateDir == "" {
- args.TemplateDir = "templates"
- }
- if *outFlag != "" {
- args.OutDir = *outFlag
- }
- if args.OutDir == "" {
- args.OutDir = "out"
- }
-
- if err := os.MkdirAll(args.OutDir, os.ModePerm); err != nil {
- return err
- }
-
- blog, err := blog.NewBlog(args.ArticleDir, args.TemplateDir, args.Author)
- if err != nil {
- return err
- }
-
- indexFile, err := os.Create(filepath.Join(args.OutDir, "index.html"))
- if err != nil {
- return err
- }
- defer indexFile.Close()
- if err := blog.Index(indexFile); err != nil {
- return err
- }
-
- for _, article := range blog.Articles {
- if err := func() error {
- articleFile, err := os.Create(filepath.Join(args.OutDir, article.Filename+".html"))
- if err != nil {
- return err
- }
- defer articleFile.Close()
- return blog.Article(articleFile, article)
- }(); err != nil {
- return err
- }
- }
-
- return nil
-}
diff --git a/cmd/chromacss/main.go b/cmd/chromacss/main.go
deleted file mode 100644
index e2b1eefe7aa3763e530c534812bee0f951a768b8..0000000000000000000000000000000000000000
--- a/cmd/chromacss/main.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "os"
-
- "github.com/alecthomas/chroma/v2/formatters/html"
- "github.com/alecthomas/chroma/v2/styles"
- "go.jolheiser.com/blog"
-)
-
-func main() {
- if err := maine(); err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
-}
-
-func maine() error {
- fs := flag.NewFlagSet("chromacss", flag.ExitOnError)
- lightFlag := fs.String("light", "catppuccin-latte", "Light theme")
- fs.StringVar(lightFlag, "l", *lightFlag, "--light")
- darkFlag := fs.String("dark", "catppuccin-mocha", "Dark theme")
- fs.StringVar(darkFlag, "d", *darkFlag, "--dark")
- outFlag := fs.String("out", "", "Output (default: stdout)")
- fs.StringVar(outFlag, "o", *outFlag, "--out")
- if err := fs.Parse(os.Args[1:]); err != nil {
- return err
- }
-
- out := os.Stdout
- if *outFlag != "" {
- fi, err := os.Create(*outFlag)
- if err != nil {
- return err
- }
- defer fi.Close()
- out = fi
- }
-
- formatter := html.New(blog.ChromaOpts...)
-
- lightStyle := *lightFlag
- darkStyle := *darkFlag
-
- if lightStyle == "" {
- lightStyle = "catppuccin-latte"
- if darkStyle != "" {
- lightStyle = darkStyle
- darkStyle = ""
- }
- }
-
- styles.Fallback = styles.Get("catppuccin-latte")
- style := styles.Get(lightStyle)
- if err := formatter.WriteCSS(out, style); err != nil {
- return err
- }
-
- if darkStyle != "" {
- out.WriteString("@media (prefers-color-scheme: dark) {")
- styles.Fallback = styles.Get("catpuccin-mocha")
- style = styles.Get(darkStyle)
- if err := formatter.WriteCSS(out, style); err != nil {
- return err
- }
- out.WriteString("}")
- }
-
- return nil
-}
diff --git a/go.mod b/go.mod
index 8c1aa017c7924a7b8ed3b4ceab8cde26ece9de51..3d226dae1f71f357af8b8c8cbd8f38889bcba113 100644
--- a/go.mod
+++ b/go.mod
@@ -3,29 +3,26 @@
go 1.21.6
require (
- github.com/BurntSushi/toml v1.3.2 // indirect
+
github.com/alecthomas/assert v1.0.0 // indirect
+
github.com/alecthomas/chroma/v2 v2.12.0 // indirect
+
github.com/alecthomas/colour v0.1.0 // indirect
+
github.com/alecthomas/repr v0.2.0 // indirect
-module go.jolheiser.com/blog
+go 1.21.6
- github.com/dlclark/regexp2 v1.10.0 // indirect
+go 1.21.6
module go.jolheiser.com/blog
-
-module go.jolheiser.com/blog
go 1.21.6
+
- github.com/mattn/go-isatty v0.0.14 // indirect
-module go.jolheiser.com/blog
+
github.com/BurntSushi/toml v1.3.2 // indirect
- github.com/peterbourgon/ff/v3 v3.4.0 // indirect
+
- github.com/sergi/go-diff v1.2.0 // indirect
+require (
module go.jolheiser.com/blog
- github.com/alecthomas/colour v0.1.0 // indirect
module go.jolheiser.com/blog
- github.com/alecthomas/repr v0.2.0 // indirect
- github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc // indirect
+ github.com/kr/pretty v0.3.1 // indirect
- github.com/yuin/goldmark-meta v1.1.0 // indirect
- golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
+ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
- gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 62a81853e4095cb8c64512a1f9d8ff9e25a8fc83..c4b1bc6fb037c92e5fe68ac1efae39f51b36f823 100644
--- a/go.sum
+++ b/go.sum
@@ -1,67 +1,51 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/a-h/templ v0.2.543 h1:8YyLvyUtf0/IE2nIwZ62Z/m2o2NqwhnMynzOL78Lzbk=
+github.com/a-h/templ v0.2.543/go.mod h1:jP908DQCwI08IrnTalhzSEH9WJqG/Q94+EODQcJGFUA=
+github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
+github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/assert v1.0.0/go.mod h1:va/d2JC+M7F6s+80kl/R3G7FUiW6JzUO+hPhLyJ36ZY=
-github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
-github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk=
-github.com/alecthomas/colour v0.1.0/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
-github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
-github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
-github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-github.com/gorilla/feeds v1.1.2 h1:pxzZ5PD3RJdhFH2FsJJ4x6PqMqbgFk1+Vez4XWBW8Iw=
-github.com/gorilla/feeds v1.1.2/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
-github.com/jolheiser/goldmark-meta v0.0.2 h1:3qnvGnp1n7Cdu1L9LpuZrM6dfmbMAS22+hNJnxGPn6s=
-github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
-github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
-github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk=
-github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
+github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
-github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
+github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
-github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
+github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
+github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
-github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
+github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
github.com/alecthomas/assert v1.0.0/go.mod h1:va/d2JC+M7F6s+80kl/R3G7FUiW6JzUO+hPhLyJ36ZY=
github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
-github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
-github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
-github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
github.com/alecthomas/chroma/v2 v2.12.0 h1:Wh8qLEgMMsN7mgyG8/qIpegky2Hvzr4By6gEF7cmWgw=
-github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurGF0EpseFXdKMBw=
+github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
-github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/alecthomas/assert v1.0.0/go.mod h1:va/d2JC+M7F6s+80kl/R3G7FUiW6JzUO+hPhLyJ36ZY=
-github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/alecthomas/assert v1.0.0/go.mod h1:va/d2JC+M7F6s+80kl/R3G7FUiW6JzUO+hPhLyJ36ZY=
-github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/yuin/goldmark v1.3.7 h1:NSaHgaeJFCtWXCBkBKXw0rhgMuJ0VoE9FB5mWldcrQ4=
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.15 h1:CFa84T0goNn/UIXYS+dmjjVxMyTAvpOmzld40N/nfK0=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
@@ -72,15 +56,10 @@ github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
-github.com/alecthomas/assert v1.0.0/go.mod h1:va/d2JC+M7F6s+80kl/R3G7FUiW6JzUO+hPhLyJ36ZY=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+github.com/alecthomas/assert v1.0.0 h1:3XmGh/PSuLzDbK3W2gUbRXwgW5lqPkuqvRgeQ30FI5o=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/goldmark.go b/goldmark.go
deleted file mode 100644
index 195e16337b248d03814a8d54db027cad16c6ec19..0000000000000000000000000000000000000000
--- a/goldmark.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package blog
-
-import (
- chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
- "github.com/yuin/goldmark"
- emoji "github.com/yuin/goldmark-emoji"
- highlighting "github.com/yuin/goldmark-highlighting/v2"
- meta "github.com/yuin/goldmark-meta"
- "github.com/yuin/goldmark/extension"
- "github.com/yuin/goldmark/parser"
- "github.com/yuin/goldmark/renderer/html"
-)
-
-// Markdown is the default markdown converter
-// Set over this variable to alter how the other functions convert
-var Markdown = goldmark.New(
- goldmark.WithParserOptions(
- parser.WithAutoHeadingID(),
- ),
- goldmark.WithRendererOptions(
- html.WithUnsafe(),
- ),
- goldmark.WithExtensions(
- extension.GFM,
- meta.Meta,
- emoji.Emoji,
- highlighting.NewHighlighting(
- highlighting.WithFormatOptions(
- ChromaOpts...,
- ),
- ),
- ),
-)
-
-var ChromaOpts = []chromahtml.Option{
- chromahtml.WithClasses(true),
- chromahtml.WithAllClasses(true),
- chromahtml.WithLineNumbers(true),
-}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..f0c5c9ee09a29fe144c863df27be02a3a1bf7a3e
--- /dev/null
+++ b/main.go
@@ -0,0 +1,103 @@
+//go:generate templ generate
+//go:generate go run .
+package main
+
+import (
+ "context"
+ "embed"
+ "flag"
+ "fmt"
+ iofs "io/fs"
+ "os"
+ "path/filepath"
+
+ "github.com/alecthomas/chroma/v2/styles"
+)
+
+//go:embed articles/*
+var articleFS embed.FS
+
+func maine() error {
+ fs := flag.NewFlagSet("blog", flag.ExitOnError)
+ outFlag := fs.String("out", "out", "Output directory")
+ fs.StringVar(outFlag, "o", *outFlag, "--out")
+ if err := fs.Parse(os.Args[1:]); err != nil {
+ return err
+ }
+
+ files, err := articleFS.ReadDir("articles")
+ if err != nil {
+ return err
+ }
+
+ articles := make(Articles)
+ for _, file := range files {
+ if filepath.Ext(file.Name()) != ".md" {
+ continue
+ }
+ content, err := iofs.ReadFile(articleFS, fmt.Sprintf("articles/%s", file.Name()))
+ if err != nil {
+ return err
+ }
+ article, err := Parse(string(content))
+ if err != nil {
+ return err
+ }
+ articles[article.Category] = append(articles[article.Category], article)
+ if err := writeArticle(*outFlag, article); err != nil {
+ return err
+ }
+ }
+
+ if err := writeCSS(*outFlag); err != nil {
+ return err
+ }
+
+ fi, err := os.Create(filepath.Join(*outFlag, "index.html"))
+ if err != nil {
+ return err
+ }
+ defer fi.Close()
+
+ return IndexTemplate(articles).Render(context.Background(), fi)
+}
+
+func writeArticle(out string, article Article) error {
+ dest := filepath.Join(out, article.Slug(), "index.html")
+ if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
+ return err
+ }
+
+ fi, err := os.Create(dest)
+ if err != nil {
+ return err
+ }
+ defer fi.Close()
+
+ return ArticleTemplate(article).Render(context.Background(), fi)
+}
+
+func writeCSS(out string) error {
+ fi, err := os.Create(filepath.Join(out, "chroma.css"))
+ if err != nil {
+ return err
+ }
+ defer fi.Close()
+
+ if err := CSS.WriteCSS(fi, styles.Get("catpuccin-latte")); err != nil {
+ return err
+ }
+ fi.WriteString("@media (prefers-color-scheme: dark) {")
+ if err := CSS.WriteCSS(fi, styles.Get("catppuccin-mocha")); err != nil {
+ return err
+ }
+ fi.WriteString("}")
+ return nil
+}
+
+func main() {
+ if err := maine(); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+}
diff --git a/templates.go b/templates.go
new file mode 100644
index 0000000000000000000000000000000000000000..8af412b693c9a8150dd0e116a5d93f31a59570d1
--- /dev/null
+++ b/templates.go
@@ -0,0 +1,119 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/BurntSushi/toml"
+ chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
+ "github.com/yuin/goldmark"
+ emoji "github.com/yuin/goldmark-emoji"
+ highlighting "github.com/yuin/goldmark-highlighting/v2"
+ meta "github.com/yuin/goldmark-meta"
+ "github.com/yuin/goldmark/extension"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/renderer/html"
+)
+
+// Articles by Category
+type Articles map[string][]Article
+
+type Article struct {
+ Content string
+ Meta
+}
+
+func (a Article) Slug() string {
+ slug := strings.NewReplacer(" ", "-").Replace(strings.ToLower(a.Meta.Title))
+ return fmt.Sprintf("%s/%s/", a.Meta.Date.Format("2006"), slug)
+}
+
+type Meta struct {
+ Title string
+ Summary string
+ Date time.Time
+ Category string
+}
+
+type Author struct {
+ Name string
+ Email string
+}
+
+func Parse(content string) (Article, error) {
+ lines := strings.Split(content, "\n")
+
+ start, end := -1, -1
+ for idx, line := range lines {
+ if strings.TrimSpace(line) == "" {
+ continue
+ }
+
+ if isTOMLSeparator(line) {
+ if start == -1 {
+ start = idx
+ continue
+ }
+ end = idx
+ break
+ }
+ }
+
+ var meta Meta
+ body := content
+ if start != -1 && end != -1 {
+ body = strings.Join(lines[end+1:], "\n")
+ if err := toml.Unmarshal([]byte(strings.Join(lines[start+1:end], "\n")), &meta); err != nil {
+ return Article{}, fmt.Errorf("could not parse frontmatter: %w", err)
+ }
+ }
+
+ var buf bytes.Buffer
+ err := Markdown.Convert([]byte(body), &buf)
+ if err != nil {
+ return Article{}, fmt.Errorf("could not convert article: %w", err)
+ }
+
+ return Article{
+ Content: buf.String(),
+ Meta: meta,
+ }, nil
+}
+
+func isTOMLSeparator(line string) bool {
+ for _, char := range line {
+ if char != '+' {
+ return false
+ }
+ }
+ return true
+}
+
+var (
+ opts = []chromahtml.Option{
+ chromahtml.WithClasses(true),
+ chromahtml.WithAllClasses(true),
+ chromahtml.WithLineNumbers(true),
+ }
+ Markdown = goldmark.New(
+ goldmark.WithParserOptions(
+ parser.WithAutoHeadingID(),
+ ),
+ goldmark.WithRendererOptions(
+ html.WithUnsafe(),
+ ),
+ goldmark.WithExtensions(
+ extension.GFM,
+ meta.Meta,
+ emoji.Emoji,
+ highlighting.NewHighlighting(
+ highlighting.WithFormatOptions(
+ opts...,
+ ),
+ ),
+ ),
+ )
+ CSS = chromahtml.New(opts...)
+)
diff --git a/templates.templ b/templates.templ
new file mode 100644
index 0000000000000000000000000000000000000000..daee1e81f364b2c7d571ffe38b7e3eb24d24def0
--- /dev/null
+++ b/templates.templ
@@ -0,0 +1,45 @@
+package main
+
+templ baseTemplate(title, description string) {
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="UTF-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+ <title>{ title }</title>
+ <meta property="og:title" content={ title }/>
+ <meta property="og:description" content={ description }/>
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"/>
+ <link rel="stylesheet" href="/chroma.css"/>
+ </head>
+ <body>
+ { children... }
+ </body>
+ </html>
+}
+
+templ IndexTemplate(articles Articles) {
+ @baseTemplate("jolheiser's blog", "Hahaha yes.....YES!") {
+ <main>
+ for category, articles := range articles {
+ <h2>{ category }</h2>
+ <ul>
+ for _, article := range articles {
+ <li><a href={ templ.SafeURL(article.Slug()) }>{ article.Title }</a></li>
+ }
+ </ul>
+ }
+ </main>
+ }
+}
+
+templ ArticleTemplate(article Article) {
+ @baseTemplate(article.Title, article.Summary) {
+ <header>
+ <h1>{ article.Title }</h1>
+ <h2>{ article.Date.Format("01/02/2006") }</h2>
+ </header>
+ <main>@templ.Raw(article.Content)</main>
+ }
+}
+
diff --git a/templates_templ.go b/templates_templ.go
new file mode 100644
index 0000000000000000000000000000000000000000..eb1a5286ff88f24036184a633dcb98ac78aa66d5
--- /dev/null
+++ b/templates_templ.go
@@ -0,0 +1,219 @@
+// Code generated by templ - DO NOT EDIT.
+
+// templ: version: 0.2.480
+package main
+
+//lint:file-ignore SA4006 This context is only used if a nested component is present.
+
+import "github.com/a-h/templ"
+import "context"
+import "io"
+import "bytes"
+
+func baseTemplate(title, description string) templ.Component {
+ return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
+ if !templ_7745c5c3_IsBuffer {
+ templ_7745c5c3_Buffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var1 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var1 == nil {
+ templ_7745c5c3_Var1 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var2 string = title
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</title><meta property=\"og:title\" content=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(title))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><meta property=\"og:description\" content=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(description))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css\"><link rel=\"stylesheet\" href=\"/chroma.css\"></head><body>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body></html>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if !templ_7745c5c3_IsBuffer {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func IndexTemplate(articles Articles) templ.Component {
+ return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
+ if !templ_7745c5c3_IsBuffer {
+ templ_7745c5c3_Buffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var3 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var3 == nil {
+ templ_7745c5c3_Var3 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Var4 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
+ if !templ_7745c5c3_IsBuffer {
+ templ_7745c5c3_Buffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<main>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for category, articles := range articles {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h2>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var5 string = category
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2><ul>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ for _, article := range articles {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li><a href=\"")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var6 templ.SafeURL = templ.SafeURL(article.Slug())
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var7 string = article.Title
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></li>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</main>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if !templ_7745c5c3_IsBuffer {
+ _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
+ }
+ return templ_7745c5c3_Err
+ })
+ templ_7745c5c3_Err = baseTemplate("jolheiser's blog", "Hahaha yes.....YES!").Render(templ.WithChildren(ctx, templ_7745c5c3_Var4), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if !templ_7745c5c3_IsBuffer {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
+ }
+ return templ_7745c5c3_Err
+ })
+}
+
+func ArticleTemplate(article Article) templ.Component {
+ return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
+ if !templ_7745c5c3_IsBuffer {
+ templ_7745c5c3_Buffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
+ }
+ ctx = templ.InitializeContext(ctx)
+ templ_7745c5c3_Var8 := templ.GetChildren(ctx)
+ if templ_7745c5c3_Var8 == nil {
+ templ_7745c5c3_Var8 = templ.NopComponent
+ }
+ ctx = templ.ClearChildren(ctx)
+ templ_7745c5c3_Var9 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
+ templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
+ if !templ_7745c5c3_IsBuffer {
+ templ_7745c5c3_Buffer = templ.GetBuffer()
+ defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<header><h1>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var10 string = article.Title
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1><h2>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ var templ_7745c5c3_Var11 string = article.Date.Format("01/02/2006")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2></header><main>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ templ_7745c5c3_Err = templ.Raw(article.Content).Render(ctx, templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</main>")
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if !templ_7745c5c3_IsBuffer {
+ _, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
+ }
+ return templ_7745c5c3_Err
+ })
+ templ_7745c5c3_Err = baseTemplate(article.Title, article.Summary).Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
+ if templ_7745c5c3_Err != nil {
+ return templ_7745c5c3_Err
+ }
+ if !templ_7745c5c3_IsBuffer {
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
+ }
+ return templ_7745c5c3_Err
+ })
+}