Läuft
This commit is contained in:
@@ -74,7 +74,7 @@ func main() {
|
|||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := tplList.ExecuteTemplate(w, "base", articles); err != nil {
|
if err := tplList.ExecuteTemplate(w, "layout", articles); err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -83,7 +83,7 @@ func main() {
|
|||||||
slug := strings.TrimPrefix(r.URL.Path, "/post/")
|
slug := strings.TrimPrefix(r.URL.Path, "/post/")
|
||||||
for _, a := range articles {
|
for _, a := range articles {
|
||||||
if a.Slug == slug {
|
if a.Slug == slug {
|
||||||
if err := tplArticle.ExecuteTemplate(w, "base", a); err != nil {
|
if err := tplArticle.ExecuteTemplate(w, "layout", a); err != nil {
|
||||||
http.Error(w, err.Error(), 500)
|
http.Error(w, err.Error(), 500)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
3
content/2025/demo.md
Normal file
3
content/2025/demo.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<!--{"title": "ChatGPT + Go: So geht's", "date": "2025-05-04", "slug": "chatgpt-go", "cover": ""}-->
|
||||||
|
# Hello Gophers 🤖
|
||||||
|
*(Markdown‑Inhalt)*
|
@@ -1,4 +1,4 @@
|
|||||||
<!-- { "title": "Mein erster Markdown‑Post", "date": "2025‑05‑04", "slug": "mein-erster-markdown-post" } -->
|
<!--{"title": "Mein erster Markdown‑Post", "date": "2025-05-04", "slug": "mein-erster-markdown-post", "cover": "/static/img/placeholder.png"}-->
|
||||||
|
|
||||||
# Hello Markdown 🌟
|
# Hello Markdown 🌟
|
||||||
|
|
||||||
|
@@ -26,7 +26,6 @@ func LoadDir(root string) ([]Article, error) {
|
|||||||
|
|
||||||
// gültige Extension‑Maske
|
// gültige Extension‑Maske
|
||||||
exts := parser.CommonExtensions | parser.AutoHeadingIDs | parser.DefinitionLists
|
exts := parser.CommonExtensions | parser.AutoHeadingIDs | parser.DefinitionLists
|
||||||
mdParser := parser.NewWithExtensions(exts)
|
|
||||||
mdRenderer := html.NewRenderer(html.RendererOptions{
|
mdRenderer := html.NewRenderer(html.RendererOptions{
|
||||||
Flags: html.CommonFlags | html.HrefTargetBlank,
|
Flags: html.CommonFlags | html.HrefTargetBlank,
|
||||||
})
|
})
|
||||||
@@ -57,6 +56,7 @@ func LoadDir(root string) ([]Article, error) {
|
|||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Date string `json:"date"`
|
Date string `json:"date"`
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
|
Cover string `json:"cover"` // NEW
|
||||||
}
|
}
|
||||||
clean := strings.TrimSpace(string(headerLine))
|
clean := strings.TrimSpace(string(headerLine))
|
||||||
|
|
||||||
@@ -80,14 +80,19 @@ func LoadDir(root string) ([]Article, error) {
|
|||||||
|
|
||||||
htmlBody := bodyBytes
|
htmlBody := bodyBytes
|
||||||
if ext == ".md" {
|
if ext == ".md" {
|
||||||
|
mdParser := parser.NewWithExtensions(exts)
|
||||||
htmlBody = md.ToHTML(bodyBytes, mdParser, mdRenderer)
|
htmlBody = md.ToHTML(bodyBytes, mdParser, mdRenderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
date, _ := time.Parse("2006-01-02", meta.Date)
|
date, err := time.Parse("2006-01-02", meta.Date)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Time", err, date)
|
||||||
|
}
|
||||||
out = append(out, Article{
|
out = append(out, Article{
|
||||||
Title: meta.Title,
|
Title: meta.Title,
|
||||||
Slug: meta.Slug,
|
Slug: meta.Slug,
|
||||||
Date: date,
|
Date: date,
|
||||||
|
Cover: meta.Cover, // NEW
|
||||||
Body: template.HTML(htmlBody),
|
Body: template.HTML(htmlBody),
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
|
@@ -10,5 +10,6 @@ type Article struct {
|
|||||||
Title string
|
Title string
|
||||||
Slug string
|
Slug string
|
||||||
Date time.Time
|
Date time.Time
|
||||||
|
Cover string // NEW
|
||||||
Body template.HTML // already trusted
|
Body template.HTML // already trusted
|
||||||
}
|
}
|
||||||
|
BIN
internal/web/static/img/#placeholder.png
Normal file
BIN
internal/web/static/img/#placeholder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.0 MiB |
BIN
internal/web/static/img/placeholder.png
Normal file
BIN
internal/web/static/img/placeholder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 MiB |
133
internal/web/static/main.css
Normal file
133
internal/web/static/main.css
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/* ---------- Farbpalette (Feel‑free to tune) ---------- */
|
||||||
|
:root {
|
||||||
|
--bg: #0d1117;
|
||||||
|
--bg‑alt: #161b22;
|
||||||
|
--text: #c9d1d9;
|
||||||
|
--text‑muted: #8b949e;
|
||||||
|
--accent: #3b82f6; /* Blau */
|
||||||
|
--accent‑light:#60a5fa;
|
||||||
|
--code‑bg: #1e242c;
|
||||||
|
--code‑border: #30363d;
|
||||||
|
--radius: 0.6rem;
|
||||||
|
--gap: 1.5rem;
|
||||||
|
font‑size: 16px;
|
||||||
|
font‑family: system‑ui, "Segoe UI", Roboto, sans‑serif;
|
||||||
|
color‑scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Globale Elemente ---------- */
|
||||||
|
* { box‑sizing: border‑box; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
line‑height: 1.6;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--accent);
|
||||||
|
text‑decoration: none;
|
||||||
|
}
|
||||||
|
a:hover { text‑decoration: underline; }
|
||||||
|
|
||||||
|
/* ---------- Layout ---------- */
|
||||||
|
.wrapper {
|
||||||
|
max‑width: 768px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: var(--gap);
|
||||||
|
}
|
||||||
|
header, footer {
|
||||||
|
display: flex;
|
||||||
|
align‑items: center;
|
||||||
|
justify‑content: space-between;
|
||||||
|
background: var(--bg‑alt);
|
||||||
|
padding: 1rem var(--gap);
|
||||||
|
border‑bottom: 1px solid var(--code‑border);
|
||||||
|
}
|
||||||
|
header h1 {
|
||||||
|
margin: 0;
|
||||||
|
font‑size: 1.4rem;
|
||||||
|
letter‑spacing: 0.5px;
|
||||||
|
}
|
||||||
|
header a { color: var(--text); }
|
||||||
|
|
||||||
|
footer {
|
||||||
|
border‑top: 1px solid var(--code‑border);
|
||||||
|
font‑size: 0.9rem;
|
||||||
|
color: var(--text‑muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Artikelliste ---------- */
|
||||||
|
.post‑list li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem 0;
|
||||||
|
border‑bottom: 1px solid var(--code‑border);
|
||||||
|
}
|
||||||
|
.post‑list li:last‑child { border‑bottom: none; }
|
||||||
|
.post‑list time { color: var(--text‑muted); font‑size: 0.9rem; }
|
||||||
|
|
||||||
|
/* ---------- Einzelartikel ---------- */
|
||||||
|
article h1 { margin‑top: 0; font‑size: 2rem; }
|
||||||
|
article time { color: var(--text‑muted); font‑size: 0.9rem; }
|
||||||
|
article img, article video {
|
||||||
|
max‑width: 100%; height: auto; border‑radius: var(--radius);
|
||||||
|
}
|
||||||
|
article pre, article code {
|
||||||
|
font‑family: "Fira Code", Consolas, monospace;
|
||||||
|
}
|
||||||
|
article pre {
|
||||||
|
background: var(--code‑bg);
|
||||||
|
border: 1px solid var(--code‑border);
|
||||||
|
padding: 1rem;
|
||||||
|
border‑radius: var(--radius);
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
article blockquote {
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border‑left: 4px solid var(--accent‑light);
|
||||||
|
background: var(--bg‑alt);
|
||||||
|
color: var(--text‑muted);
|
||||||
|
}
|
||||||
|
/* ---------- Karten‑Layout für Artikelliste ---------- */
|
||||||
|
.card {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 160px 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 1px solid var(--code-border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: var(--bg-alt);
|
||||||
|
transition: transform .15s ease, border-color .15s ease;
|
||||||
|
}
|
||||||
|
.card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
.card img {
|
||||||
|
width: 160px;
|
||||||
|
height: 100px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
.card h2 {
|
||||||
|
margin: 0 0 .4rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
.card time { color: var(--text-muted); font-size: .9rem; }
|
||||||
|
|
||||||
|
/* ---------- Hero‑Bild im Artikel ---------- */
|
||||||
|
.hero {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 340px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ---------- Responsive ---------- */
|
||||||
|
@media (max‑width: 600px) {
|
||||||
|
:root { font‑size: 15px; }
|
||||||
|
header, footer { flex‑direction: column; gap: 0.5rem; }
|
||||||
|
}
|
||||||
|
|
@@ -1,20 +1,20 @@
|
|||||||
{{ define "article" }}
|
{{ define "title" }}{{ .Title }} – B1tsblog{{ end }}
|
||||||
{{ template "base.html" . }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "title" }}{{ .Title }} – B1tsblog{{ end }}
|
|
||||||
|
|
||||||
{{ define "body" }}
|
{{ define "body" }}
|
||||||
<article class="article">
|
<article>
|
||||||
<header>
|
{{ if .Cover }}
|
||||||
<h1>{{ .Title }}</h1>
|
<img class="hero" src="{{ .Cover }}" alt="">
|
||||||
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time>
|
{{ end }}
|
||||||
</header>
|
|
||||||
|
<h1>{{ .Title }}</h1>
|
||||||
|
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time>
|
||||||
|
|
||||||
<div class="article-content">
|
<div class="article-content">
|
||||||
{{ .Body }}
|
{{ .Body }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p><a href="/">← Alle Artikel</a></p>
|
<p><a href="/">← Zurück zur Übersicht</a></p>
|
||||||
</article>
|
</article>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "article" }}{{ template "layout" . }}{{ end }}
|
||||||
|
@@ -1,15 +1,30 @@
|
|||||||
<!DOCTYPE html><html lang="de">
|
{{ define "layout" }}
|
||||||
<head>
|
<!DOCTYPE html>
|
||||||
<meta charset="utf-8">
|
<html lang="de">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
<head>
|
||||||
<title>{{ block "title" . }}B1tsblog{{ end }}</title>
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" href="/static/main.css">
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
<title>{{ block "title" . }}B1tsblog{{ end }}</title>
|
||||||
<!-- Monospace‑Font für Code (optional) -->
|
<link rel="stylesheet" href="/static/main.css">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||||
</head>
|
<!-- Monospace‑Font für Code (optional) -->
|
||||||
<body>
|
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
||||||
<header><h1><a href="/">B1tsblog</a></h1></header>
|
</head>
|
||||||
<main>{{ block "body" . }}{{ end }}</main>
|
<body>
|
||||||
<footer>© {{ now.Year }}</footer>
|
<header>
|
||||||
</body></html>
|
<h1><a href="/">B1tsblog</a></h1>
|
||||||
|
<nav>
|
||||||
|
<!-- Platz für spätere Links -->
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="wrapper">
|
||||||
|
{{ template "body" . }}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="wrapper">
|
||||||
|
© {{ now.Year }} · Powered by Go
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
@@ -1,16 +1,23 @@
|
|||||||
{{ define "list" }} {{/* ‑‑ ausführbarer Entry‑Point */}}
|
|
||||||
{{ template "base.html" . }} {{/* ruft das Layout auf */}}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "title" }}Alle Artikel{{ end }}
|
{{ define "title" }}Alle Artikel{{ end }}
|
||||||
|
|
||||||
{{ define "body" }}
|
{{ define "body" }}
|
||||||
<ul>
|
<ul class="post-list" style="list-style:none; padding:0; display:grid; gap:var(--gap);">
|
||||||
{{ range . }}
|
{{ range . }}
|
||||||
<li>
|
<li>
|
||||||
<a href="/post/{{ .Slug }}">{{ .Title }}</a>
|
<a class="card" href="/post/{{ .Slug }}">
|
||||||
— {{ .Date.Format "02.01.2006" }}
|
{{ if .Cover }}
|
||||||
|
<img src="{{ .Cover }}" alt="">
|
||||||
|
{{ else }}
|
||||||
|
<img src="/static/img/placeholder.png" alt="">
|
||||||
|
{{ end }}
|
||||||
|
<div>
|
||||||
|
<h2>{{ .Title }}</h2>
|
||||||
|
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "list" }}{{ template "layout" . }}{{ end }}
|
||||||
|
Reference in New Issue
Block a user