ArticleRenderer è ora più generico e miglioramenti allo stile del Markdown
This commit is contained in:
+53
-40
@@ -25,7 +25,7 @@ import (
|
||||
var md goldmark.Markdown
|
||||
|
||||
// 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()
|
||||
if entering {
|
||||
if ok {
|
||||
@@ -43,7 +43,6 @@ func customWrapper(w util.BufWriter, c highlighting.CodeBlockContext, entering b
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
md = goldmark.New(
|
||||
goldmark.WithExtensions(
|
||||
extension.GFM,
|
||||
@@ -52,7 +51,7 @@ func init() {
|
||||
mathjax.NewMathJax(),
|
||||
highlighting.NewHighlighting(
|
||||
highlighting.WithStyle("github"),
|
||||
highlighting.WithWrapperRenderer(customWrapper),
|
||||
highlighting.WithWrapperRenderer(customCodeBlockWrapper),
|
||||
highlighting.WithFormatOptions(
|
||||
chromahtml.PreventSurroundingPre(true),
|
||||
),
|
||||
@@ -69,11 +68,10 @@ func init() {
|
||||
}
|
||||
|
||||
type ArticleFrontMatter struct {
|
||||
Title string `yaml:"title"`
|
||||
Description string `yaml:"description"`
|
||||
Tags []string `yaml:"tags,flow"`
|
||||
PublishDate string `yaml:"publish_date"`
|
||||
Important bool `yaml:"important"`
|
||||
Title string `yaml:"title"`
|
||||
Description string `yaml:"description"`
|
||||
Tags string `yaml:"tags"`
|
||||
PublishDate string `yaml:"publish_date"`
|
||||
}
|
||||
|
||||
type Article struct {
|
||||
@@ -82,14 +80,44 @@ type Article struct {
|
||||
Description string
|
||||
Tags []string
|
||||
PublishDate time.Time
|
||||
Important bool
|
||||
|
||||
SourceMarkdown string
|
||||
OutputHTML string
|
||||
MarkdownSource string
|
||||
renderedHTML string
|
||||
}
|
||||
|
||||
type ArticleRegistry struct {
|
||||
Articles map[string]*Article
|
||||
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 ArticleRenderer struct {
|
||||
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 {
|
||||
@@ -104,8 +132,8 @@ func removeBlanks(v []string) []string {
|
||||
return r
|
||||
}
|
||||
|
||||
func (ar *ArticleRegistry) LoadArticle(name string) (*Article, error) {
|
||||
fileBytes, err := os.ReadFile(path.Join("./news/", name+".md"))
|
||||
func (registry *ArticleRenderer) Load(articlePath string) (*Article, error) {
|
||||
fileBytes, err := os.ReadFile(path.Join(registry.RootPath, articlePath+".md"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -128,28 +156,22 @@ func (ar *ArticleRegistry) LoadArticle(name string) (*Article, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := md.Convert([]byte(markdownSource), &buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tags := trimAll(strings.Split(frontMatter.Tags, ","))
|
||||
|
||||
article := &Article{
|
||||
Id: name,
|
||||
Id: articlePath,
|
||||
Title: frontMatter.Title,
|
||||
Description: frontMatter.Description,
|
||||
Tags: frontMatter.Tags,
|
||||
Tags: tags,
|
||||
PublishDate: publishDate,
|
||||
Important: frontMatter.Important,
|
||||
SourceMarkdown: markdownSource,
|
||||
OutputHTML: buf.String(),
|
||||
MarkdownSource: markdownSource,
|
||||
}
|
||||
|
||||
ar.Articles[name] = article
|
||||
return article, nil
|
||||
}
|
||||
|
||||
func (ar *ArticleRegistry) LoadAll() ([]*Article, error) {
|
||||
entries, err := os.ReadDir("./news")
|
||||
func (registry *ArticleRenderer) LoadAll() ([]*Article, error) {
|
||||
entries, err := os.ReadDir(registry.RootPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -159,7 +181,7 @@ func (ar *ArticleRegistry) LoadAll() ([]*Article, error) {
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
name := strings.TrimRight(entry.Name(), ".md")
|
||||
article, err := ar.LoadArticle(name)
|
||||
article, err := registry.Load(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -175,17 +197,8 @@ func (ar *ArticleRegistry) LoadAll() ([]*Article, error) {
|
||||
return articles, nil
|
||||
}
|
||||
|
||||
func NewArticleRegistry() *ArticleRegistry {
|
||||
return &ArticleRegistry{
|
||||
map[string]*Article{},
|
||||
func NewArticleRegistry(rootPath string) *ArticleRenderer {
|
||||
return &ArticleRenderer{
|
||||
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
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(middleware.RedirectSlashes)
|
||||
|
||||
// Static content
|
||||
r.Handle("/public/*", http.StripPrefix("/public", http.FileServer(http.Dir("./public"))))
|
||||
@@ -26,8 +27,7 @@ func main() {
|
||||
|
||||
// Templates & Renderer
|
||||
renderer := NewTemplateRenderer("base.html")
|
||||
articleRegistry := NewArticleRegistry()
|
||||
articleRegistry.LoadAll()
|
||||
newsArticlesRegistry := NewArticleRegistry("./news")
|
||||
|
||||
// Routes
|
||||
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) {
|
||||
articles, err := articleRegistry.LoadAll()
|
||||
articles, err := newsArticlesRegistry.LoadAll()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -70,16 +70,23 @@ func main() {
|
||||
})
|
||||
|
||||
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 {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := renderer.Render(w, "news-base.html", util.H{
|
||||
"ContentHTML": template.HTML(htmlSource),
|
||||
"Article": article,
|
||||
"ContentHTML": template.HTML(html),
|
||||
}); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
---
|
||||
important: yes
|
||||
title: "Notiza 1"
|
||||
tags: [prova, test, foo, bar]
|
||||
title: "Notizia 1"
|
||||
tags: important, prova, test, foo, bar
|
||||
publish_date: 2021/12/22 22:00
|
||||
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...
|
||||
---
|
||||
|
||||
# Notizia 1
|
||||
|
||||
## Heading 2
|
||||
|
||||
### Heading 3
|
||||
@@ -36,17 +33,58 @@ Lorem ipsum dolor, sit amet consectetur adipisicing elit. Repudiandae optio ad,
|
||||
- Item 5
|
||||
|
||||
- foo
|
||||
- bar with some `code`
|
||||
```go
|
||||
```
|
||||
type Article struct {
|
||||
Id string
|
||||
Title string
|
||||
Description string
|
||||
Tags []string
|
||||
PublishDate time.Time
|
||||
Important bool
|
||||
|
||||
SourceMarkdown string
|
||||
OutputHTML string
|
||||
MarkdownSource string
|
||||
renderedHTML string
|
||||
}
|
||||
```
|
||||
```
|
||||
- bar with some `code`
|
||||
```go
|
||||
type Article struct {
|
||||
Id string
|
||||
Title string
|
||||
Description string
|
||||
Tags []string
|
||||
PublishDate time.Time
|
||||
|
||||
MarkdownSource 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"
|
||||
tags: [prova, test, foo, bar]
|
||||
title: "Notizia 2"
|
||||
tags: prova, test, foo, bar
|
||||
publish_date: 2021/12/23 22:00
|
||||
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...
|
||||
---
|
||||
|
||||
# Notizia 1
|
||||
|
||||
## Heading 2
|
||||
|
||||
### Heading 3
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
---
|
||||
title: "Notiza 3"
|
||||
tags: [prova, test, foo, bar]
|
||||
title: "Notizia 3"
|
||||
tags: prova, test, foo, bar
|
||||
publish_date: 2021/12/24 18:00
|
||||
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...
|
||||
---
|
||||
|
||||
# Notizia 1
|
||||
|
||||
## Heading 2
|
||||
|
||||
### Heading 3
|
||||
|
||||
+68
-7
@@ -332,6 +332,29 @@ section {
|
||||
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 */
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
@@ -359,7 +382,7 @@ h4 {
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
p, ul, ol, li, pre {
|
||||
p, ul, ol, li {
|
||||
margin: 0.5rem 0;
|
||||
width: 70ch;
|
||||
max-width: 100%;
|
||||
@@ -370,12 +393,17 @@ ul, ol {
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0.5rem 0;
|
||||
|
||||
background: var(--bg-lighter);
|
||||
border-radius: 2px;
|
||||
border: 1px solid #cbcbcb;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 4px 0 #00000033;
|
||||
|
||||
font-size: 90%;
|
||||
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
@@ -629,10 +657,6 @@ form .field-set input {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.news-content .math.display {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.news-content img {
|
||||
display: block;
|
||||
margin: 1rem auto;
|
||||
@@ -643,10 +667,47 @@ form .field-set input {
|
||||
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 */
|
||||
|
||||
.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);
|
||||
}
|
||||
|
||||
@@ -70,4 +70,8 @@ body.dark-mode {
|
||||
/* Trick molto molto malvagio per non dover rendere il colorscheme dei blocchi di codice dinamici rispetto alla dark mode */
|
||||
.dark-mode pre > code > span {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
.dark-mode table td, .dark-mode table th {
|
||||
border-color: var(--fg);
|
||||
}
|
||||
@@ -4,6 +4,15 @@
|
||||
|
||||
{{define "body"}}
|
||||
<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 }}
|
||||
</section>
|
||||
{{end}}
|
||||
+12
-2
@@ -10,7 +10,7 @@
|
||||
</h2>
|
||||
<div class="card-list">
|
||||
{{ range .Articles }}
|
||||
{{ if .Important }}
|
||||
{{ if .HasTag "important" }}
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
<a href="/news/{{ .Id }}">
|
||||
@@ -19,6 +19,11 @@
|
||||
</div>
|
||||
<div class="date">{{ .PublishDate.Format "2006/01/02" }}</div>
|
||||
<div class="description">{{ .Description }}</div>
|
||||
<div class="tags">
|
||||
{{ range .Tags }}
|
||||
<span class="tag">{{ . }}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
@@ -32,7 +37,7 @@
|
||||
</h2>
|
||||
<div class="card-list">
|
||||
{{ range .Articles }}
|
||||
{{ if not .Important }}
|
||||
{{ if not (.HasTag "important") }}
|
||||
<div class="card">
|
||||
<div class="title">
|
||||
<a href="/news/{{ .Id }}">
|
||||
@@ -41,6 +46,11 @@
|
||||
</div>
|
||||
<div class="date">{{ .PublishDate.Format "2006/01/02" }}</div>
|
||||
<div class="description">{{ .Description }}</div>
|
||||
<div class="tags">
|
||||
{{ range .Tags }}
|
||||
<span class="tag">{{ . }}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
Reference in New Issue
Block a user