Läuft
This commit is contained in:
@@ -74,7 +74,7 @@ func main() {
|
||||
http.NotFound(w, r)
|
||||
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)
|
||||
}
|
||||
})
|
||||
@@ -83,7 +83,7 @@ func main() {
|
||||
slug := strings.TrimPrefix(r.URL.Path, "/post/")
|
||||
for _, a := range articles {
|
||||
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)
|
||||
}
|
||||
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 🌟
|
||||
|
||||
|
@@ -26,7 +26,6 @@ func LoadDir(root string) ([]Article, error) {
|
||||
|
||||
// gültige Extension‑Maske
|
||||
exts := parser.CommonExtensions | parser.AutoHeadingIDs | parser.DefinitionLists
|
||||
mdParser := parser.NewWithExtensions(exts)
|
||||
mdRenderer := html.NewRenderer(html.RendererOptions{
|
||||
Flags: html.CommonFlags | html.HrefTargetBlank,
|
||||
})
|
||||
@@ -57,6 +56,7 @@ func LoadDir(root string) ([]Article, error) {
|
||||
Title string `json:"title"`
|
||||
Date string `json:"date"`
|
||||
Slug string `json:"slug"`
|
||||
Cover string `json:"cover"` // NEW
|
||||
}
|
||||
clean := strings.TrimSpace(string(headerLine))
|
||||
|
||||
@@ -80,14 +80,19 @@ func LoadDir(root string) ([]Article, error) {
|
||||
|
||||
htmlBody := bodyBytes
|
||||
if ext == ".md" {
|
||||
mdParser := parser.NewWithExtensions(exts)
|
||||
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{
|
||||
Title: meta.Title,
|
||||
Slug: meta.Slug,
|
||||
Date: date,
|
||||
Cover: meta.Cover, // NEW
|
||||
Body: template.HTML(htmlBody),
|
||||
})
|
||||
return nil
|
||||
|
@@ -10,5 +10,6 @@ type Article struct {
|
||||
Title string
|
||||
Slug string
|
||||
Date time.Time
|
||||
Cover string // NEW
|
||||
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" }}
|
||||
{{ template "base.html" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "title" }}{{ .Title }} – B1tsblog{{ end }}
|
||||
{{ define "title" }}{{ .Title }} – B1tsblog{{ end }}
|
||||
|
||||
{{ define "body" }}
|
||||
<article class="article">
|
||||
<header>
|
||||
<h1>{{ .Title }}</h1>
|
||||
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time>
|
||||
</header>
|
||||
<article>
|
||||
{{ if .Cover }}
|
||||
<img class="hero" src="{{ .Cover }}" alt="">
|
||||
{{ end }}
|
||||
|
||||
<h1>{{ .Title }}</h1>
|
||||
<time datetime="{{ .Date.Format "2006-01-02" }}">{{ .Date.Format "02.01.2006" }}</time>
|
||||
|
||||
<div class="article-content">
|
||||
{{ .Body }}
|
||||
</div>
|
||||
|
||||
<p><a href="/">← Alle Artikel</a></p>
|
||||
<p><a href="/">← Zurück zur Übersicht</a></p>
|
||||
</article>
|
||||
{{ end }}
|
||||
|
||||
{{ define "article" }}{{ template "layout" . }}{{ end }}
|
||||
|
@@ -1,15 +1,30 @@
|
||||
<!DOCTYPE html><html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{{ block "title" . }}B1tsblog{{ end }}</title>
|
||||
<link rel="stylesheet" href="/static/main.css">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<!-- Monospace‑Font für Code (optional) -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<header><h1><a href="/">B1tsblog</a></h1></header>
|
||||
<main>{{ block "body" . }}{{ end }}</main>
|
||||
<footer>© {{ now.Year }}</footer>
|
||||
</body></html>
|
||||
{{ define "layout" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{{ block "title" . }}B1tsblog{{ end }}</title>
|
||||
<link rel="stylesheet" href="/static/main.css">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<!-- Monospace‑Font für Code (optional) -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<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 "body" }}
|
||||
<ul>
|
||||
<ul class="post-list" style="list-style:none; padding:0; display:grid; gap:var(--gap);">
|
||||
{{ range . }}
|
||||
<li>
|
||||
<a href="/post/{{ .Slug }}">{{ .Title }}</a>
|
||||
— {{ .Date.Format "02.01.2006" }}
|
||||
<a class="card" href="/post/{{ .Slug }}">
|
||||
{{ 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>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
|
||||
{{ define "list" }}{{ template "layout" . }}{{ end }}
|
||||
|
Reference in New Issue
Block a user