ArticleRenderer è ora più generico e miglioramenti allo stile del Markdown

main-old
Antonio De Lucreziis 3 years ago
parent 1a27b2d91e
commit 106531603c

@ -25,7 +25,7 @@ import (
var md goldmark.Markdown var md goldmark.Markdown
// https://github.com/yuin/goldmark-highlighting/blob/9216f9c5aa010c549cc9fc92bb2593ab299f90d4/highlighting_test.go#L27 // https://github.com/yuin/goldmark-highlighting/blob/9216f9c5aa010c549cc9fc92bb2593ab299f90d4/highlighting_test.go#L27
func customWrapper(w util.BufWriter, c highlighting.CodeBlockContext, entering bool) { func customCodeBlockWrapper(w util.BufWriter, c highlighting.CodeBlockContext, entering bool) {
lang, ok := c.Language() lang, ok := c.Language()
if entering { if entering {
if ok { if ok {
@ -43,7 +43,6 @@ func customWrapper(w util.BufWriter, c highlighting.CodeBlockContext, entering b
} }
func init() { func init() {
md = goldmark.New( md = goldmark.New(
goldmark.WithExtensions( goldmark.WithExtensions(
extension.GFM, extension.GFM,
@ -52,7 +51,7 @@ func init() {
mathjax.NewMathJax(), mathjax.NewMathJax(),
highlighting.NewHighlighting( highlighting.NewHighlighting(
highlighting.WithStyle("github"), highlighting.WithStyle("github"),
highlighting.WithWrapperRenderer(customWrapper), highlighting.WithWrapperRenderer(customCodeBlockWrapper),
highlighting.WithFormatOptions( highlighting.WithFormatOptions(
chromahtml.PreventSurroundingPre(true), chromahtml.PreventSurroundingPre(true),
), ),
@ -71,9 +70,8 @@ func init() {
type ArticleFrontMatter struct { type ArticleFrontMatter struct {
Title string `yaml:"title"` Title string `yaml:"title"`
Description string `yaml:"description"` Description string `yaml:"description"`
Tags []string `yaml:"tags,flow"` Tags string `yaml:"tags"`
PublishDate string `yaml:"publish_date"` PublishDate string `yaml:"publish_date"`
Important bool `yaml:"important"`
} }
type Article struct { type Article struct {
@ -82,14 +80,44 @@ type Article struct {
Description string Description string
Tags []string Tags []string
PublishDate time.Time PublishDate time.Time
Important bool
SourceMarkdown string MarkdownSource string
OutputHTML string renderedHTML string
}
func (article *Article) HasTag(tag string) bool {
for _, t := range article.Tags {
if t == tag {
return true
}
}
return false
}
func (article *Article) Render() (string, error) {
if article.renderedHTML == "" {
var buf bytes.Buffer
if err := md.Convert([]byte(article.MarkdownSource), &buf); err != nil {
return "", err
}
article.renderedHTML = buf.String()
}
return article.renderedHTML, nil
} }
type ArticleRegistry struct { type ArticleRenderer struct {
Articles map[string]*Article RootPath string
}
func trimAll(vs []string) []string {
r := []string{}
for _, v := range vs {
r = append(r, strings.TrimSpace(v))
}
return r
} }
func removeBlanks(v []string) []string { func removeBlanks(v []string) []string {
@ -104,8 +132,8 @@ func removeBlanks(v []string) []string {
return r return r
} }
func (ar *ArticleRegistry) LoadArticle(name string) (*Article, error) { func (registry *ArticleRenderer) Load(articlePath string) (*Article, error) {
fileBytes, err := os.ReadFile(path.Join("./news/", name+".md")) fileBytes, err := os.ReadFile(path.Join(registry.RootPath, articlePath+".md"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -128,28 +156,22 @@ func (ar *ArticleRegistry) LoadArticle(name string) (*Article, error) {
return nil, err return nil, err
} }
var buf bytes.Buffer tags := trimAll(strings.Split(frontMatter.Tags, ","))
if err := md.Convert([]byte(markdownSource), &buf); err != nil {
return nil, err
}
article := &Article{ article := &Article{
Id: name, Id: articlePath,
Title: frontMatter.Title, Title: frontMatter.Title,
Description: frontMatter.Description, Description: frontMatter.Description,
Tags: frontMatter.Tags, Tags: tags,
PublishDate: publishDate, PublishDate: publishDate,
Important: frontMatter.Important, MarkdownSource: markdownSource,
SourceMarkdown: markdownSource,
OutputHTML: buf.String(),
} }
ar.Articles[name] = article
return article, nil return article, nil
} }
func (ar *ArticleRegistry) LoadAll() ([]*Article, error) { func (registry *ArticleRenderer) LoadAll() ([]*Article, error) {
entries, err := os.ReadDir("./news") entries, err := os.ReadDir(registry.RootPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -159,7 +181,7 @@ func (ar *ArticleRegistry) LoadAll() ([]*Article, error) {
for _, entry := range entries { for _, entry := range entries {
if !entry.IsDir() { if !entry.IsDir() {
name := strings.TrimRight(entry.Name(), ".md") name := strings.TrimRight(entry.Name(), ".md")
article, err := ar.LoadArticle(name) article, err := registry.Load(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -175,17 +197,8 @@ func (ar *ArticleRegistry) LoadAll() ([]*Article, error) {
return articles, nil return articles, nil
} }
func NewArticleRegistry() *ArticleRegistry { func NewArticleRegistry(rootPath string) *ArticleRenderer {
return &ArticleRegistry{ return &ArticleRenderer{
map[string]*Article{}, rootPath,
} }
} }
func (ar *ArticleRegistry) Render(name string) (string, error) {
article, err := ar.LoadArticle(name)
if err != nil {
return "", err
}
return article.OutputHTML, nil
}

@ -19,6 +19,7 @@ func main() {
// Middleware // Middleware
r.Use(middleware.Logger) r.Use(middleware.Logger)
r.Use(middleware.Recoverer) r.Use(middleware.Recoverer)
r.Use(middleware.RedirectSlashes)
// Static content // Static content
r.Handle("/public/*", http.StripPrefix("/public", http.FileServer(http.Dir("./public")))) r.Handle("/public/*", http.StripPrefix("/public", http.FileServer(http.Dir("./public"))))
@ -26,8 +27,7 @@ func main() {
// Templates & Renderer // Templates & Renderer
renderer := NewTemplateRenderer("base.html") renderer := NewTemplateRenderer("base.html")
articleRegistry := NewArticleRegistry() newsArticlesRegistry := NewArticleRegistry("./news")
articleRegistry.LoadAll()
// Routes // Routes
r.Get("/", func(w http.ResponseWriter, r *http.Request) { r.Get("/", func(w http.ResponseWriter, r *http.Request) {
@ -55,7 +55,7 @@ func main() {
}) })
r.Get("/news", func(w http.ResponseWriter, r *http.Request) { r.Get("/news", func(w http.ResponseWriter, r *http.Request) {
articles, err := articleRegistry.LoadAll() articles, err := newsArticlesRegistry.LoadAll()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
@ -70,16 +70,23 @@ func main() {
}) })
r.Get("/news/{article}", func(w http.ResponseWriter, r *http.Request) { r.Get("/news/{article}", func(w http.ResponseWriter, r *http.Request) {
article := chi.URLParam(r, "article") articleName := chi.URLParam(r, "article")
htmlSource, err := articleRegistry.Render(article) article, err := newsArticlesRegistry.Load(articleName)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
html, err := article.Render()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
if err := renderer.Render(w, "news-base.html", util.H{ if err := renderer.Render(w, "news-base.html", util.H{
"ContentHTML": template.HTML(htmlSource), "Article": article,
"ContentHTML": template.HTML(html),
}); err != nil { }); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return

@ -1,14 +1,11 @@
--- ---
important: yes title: "Notizia 1"
title: "Notiza 1" tags: important, prova, test, foo, bar
tags: [prova, test, foo, bar]
publish_date: 2021/12/22 22:00 publish_date: 2021/12/22 22:00
description: | description: |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur... Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
--- ---
# Notizia 1
## Heading 2 ## Heading 2
### Heading 3 ### Heading 3
@ -36,6 +33,18 @@ Lorem ipsum dolor, sit amet consectetur adipisicing elit. Repudiandae optio ad,
- Item 5 - Item 5
- foo - foo
```
type Article struct {
Id string
Title string
Description string
Tags []string
PublishDate time.Time
MarkdownSource string
renderedHTML string
}
```
- bar with some `code` - bar with some `code`
```go ```go
type Article struct { type Article struct {
@ -44,9 +53,38 @@ Lorem ipsum dolor, sit amet consectetur adipisicing elit. Repudiandae optio ad,
Description string Description string
Tags []string Tags []string
PublishDate time.Time PublishDate time.Time
Important bool
SourceMarkdown string MarkdownSource string
OutputHTML string renderedHTML string
} }
``` ```
#### Tables
<https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#tables>
Colons can be used to align columns.
| Tables | Are | Cool |
| ------------- |:-------------:| -----:|
| col 3 is | right-aligned | $1600 |
| col 2 is | centered | $12 |
| zebra stripes | are neat | $1 |
There must be at least 3 dashes separating each header cell.
The outer pipes (|) are optional, and you don't need to make the
raw Markdown line up prettily. You can also use inline Markdown.
Markdown | Less | Pretty
--- | --- | ---
*Still* | `renders` | **nicely**
1 | 2 | 3
| Expression | Derivative | Integral |
| :---: | :---: | :---: |
| $x^a$ | $a x^{a-1}$ | $\displaystyle \frac{1}{a+1} x^{a+1} + c$ se $a \neq -1$ |
| $\sin x$ | $\cos x$ | $\displaystyle -\cos x + c$ |
| $e^x$ | $e^x$ | $\displaystyle e^x + c$ |

@ -1,13 +1,11 @@
--- ---
title: "Notiza 2" title: "Notizia 2"
tags: [prova, test, foo, bar] tags: prova, test, foo, bar
publish_date: 2021/12/23 22:00 publish_date: 2021/12/23 22:00
description: | description: |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur... Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
--- ---
# Notizia 1
## Heading 2 ## Heading 2
### Heading 3 ### Heading 3

@ -1,13 +1,11 @@
--- ---
title: "Notiza 3" title: "Notizia 3"
tags: [prova, test, foo, bar] tags: prova, test, foo, bar
publish_date: 2021/12/24 18:00 publish_date: 2021/12/24 18:00
description: | description: |
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur... Lorem ipsum dolor sit amet consectetur adipisicing elit. Quis nemo aperiam, voluptas quam alias esse sed natus tempore suscipit fugiat sit delectus exercitationem numquam ipsum assumenda recusandae consequatur...
--- ---
# Notizia 1
## Heading 2 ## Heading 2
### Heading 3 ### Heading 3

@ -332,6 +332,29 @@ section {
color: var(--card-content); color: var(--card-content);
} }
.tags {
display: flex;
flex-direction: row;
gap: 0 0.5rem;
margin-top: 0.5rem;
}
.tags .tag {
height: 1.5rem;
border-radius: calc(1.5rem / 2);
padding: 0 calc(1.5rem / 2);
background: var(--bg-darker-2);
color: var(--card-date);
font-size: 15px;
display: flex;
align-items: center;
justify-content: center;
}
/* Typography */ /* Typography */
h1, h2, h3, h4 { h1, h2, h3, h4 {
@ -359,7 +382,7 @@ h4 {
font-weight: var(--font-weight-bold); font-weight: var(--font-weight-bold);
} }
p, ul, ol, li, pre { p, ul, ol, li {
margin: 0.5rem 0; margin: 0.5rem 0;
width: 70ch; width: 70ch;
max-width: 100%; max-width: 100%;
@ -370,12 +393,17 @@ ul, ol {
} }
pre { pre {
margin: 0.5rem 0;
background: var(--bg-lighter); background: var(--bg-lighter);
border-radius: 2px;
border: 1px solid #cbcbcb; border: 1px solid #cbcbcb;
border-radius: 2px;
box-shadow: 0 2px 4px 0 #00000033; box-shadow: 0 2px 4px 0 #00000033;
font-size: 90%; font-size: 90%;
display: flex;
overflow-x: auto;
} }
pre > code { pre > code {
@ -629,10 +657,6 @@ form .field-set input {
text-align: center; text-align: center;
} }
.news-content .math.display {
margin: 1rem 0;
}
.news-content img { .news-content img {
display: block; display: block;
margin: 1rem auto; margin: 1rem auto;
@ -643,10 +667,47 @@ form .field-set input {
box-shadow: 0 2px 8px 0 #00000033; box-shadow: 0 2px 8px 0 #00000033;
} }
.news-content .date {
font-size: 15px;
color: var(--card-date);
display: flex;
justify-content: center;
}
.news-content .tags, .news-content .date {
justify-content: center;
}
.news-content table {
margin: 1rem auto;
}
/* Math */ /* Math */
.katex-display { .katex-display {
margin: 0; margin: 1rem 0;
font-size: 105%;
}
/* Tables */
table {
border-collapse: collapse;
}
table td, table th {
padding: 0.5rem;
}
table td:not(:first-child), table th:not(:first-child) {
border-left: 1px solid var(--bg-darker-3);
} }
table td {
border-top: 1px solid var(--bg-darker-3);
}
table tbody tr:hover {
background: var(--bg-darker);
}

@ -71,3 +71,7 @@ body.dark-mode {
.dark-mode pre > code > span { .dark-mode pre > code > span {
filter: invert(1); filter: invert(1);
} }
.dark-mode table td, .dark-mode table th {
border-color: var(--fg);
}

@ -4,6 +4,15 @@
{{define "body"}} {{define "body"}}
<section class="news-content"> <section class="news-content">
<h1>{{ .Article.Title }}</h1>
<div class="date">
{{ .Article.PublishDate.Format "2006/01/02" }}
</div>
<div class="tags">
{{ range .Article.Tags }}
<span class="tag">{{ . }}</span>
{{ end }}
</div>
{{ .ContentHTML }} {{ .ContentHTML }}
</section> </section>
{{end}} {{end}}

@ -10,7 +10,7 @@
</h2> </h2>
<div class="card-list"> <div class="card-list">
{{ range .Articles }} {{ range .Articles }}
{{ if .Important }} {{ if .HasTag "important" }}
<div class="card"> <div class="card">
<div class="title"> <div class="title">
<a href="/news/{{ .Id }}"> <a href="/news/{{ .Id }}">
@ -19,6 +19,11 @@
</div> </div>
<div class="date">{{ .PublishDate.Format "2006/01/02" }}</div> <div class="date">{{ .PublishDate.Format "2006/01/02" }}</div>
<div class="description">{{ .Description }}</div> <div class="description">{{ .Description }}</div>
<div class="tags">
{{ range .Tags }}
<span class="tag">{{ . }}</span>
{{ end }}
</div>
</div> </div>
{{ end }} {{ end }}
{{ end }} {{ end }}
@ -32,7 +37,7 @@
</h2> </h2>
<div class="card-list"> <div class="card-list">
{{ range .Articles }} {{ range .Articles }}
{{ if not .Important }} {{ if not (.HasTag "important") }}
<div class="card"> <div class="card">
<div class="title"> <div class="title">
<a href="/news/{{ .Id }}"> <a href="/news/{{ .Id }}">
@ -41,6 +46,11 @@
</div> </div>
<div class="date">{{ .PublishDate.Format "2006/01/02" }}</div> <div class="date">{{ .PublishDate.Format "2006/01/02" }}</div>
<div class="description">{{ .Description }}</div> <div class="description">{{ .Description }}</div>
<div class="tags">
{{ range .Tags }}
<span class="tag">{{ . }}</span>
{{ end }}
</div>
</div> </div>
{{ end }} {{ end }}
{{ end }} {{ end }}

Loading…
Cancel
Save