Initial commit
This commit is contained in:
6
config.py
Normal file
6
config.py
Normal file
@@ -0,0 +1,6 @@
|
||||
import os
|
||||
|
||||
class Config:
|
||||
MAIN_PAGE_TITLE = "Selected poems"
|
||||
CONTENT_DIR = os.environ.get('CONTENT_DIR') or 'content'
|
||||
HEADER_IMAGE = 'static/header.jpg'
|
||||
88
hydrogen.py
Normal file
88
hydrogen.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from pathlib import Path
|
||||
import frontmatter
|
||||
import markdown
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
import shutil
|
||||
from config import Config
|
||||
|
||||
content_dir = Path('content')
|
||||
template_dir = Path('templates')
|
||||
output_dir = Path('public')
|
||||
img_dir = Path('public/images')
|
||||
assets_dir = Path('public/assets')
|
||||
assets_css_dir = Path('public/assets/css')
|
||||
assets_js_dir = Path('public/assets/js')
|
||||
static_dir = Path('static')
|
||||
header_image = Config.HEADER_IMAGE or '/static/header.jpg'
|
||||
|
||||
class ContentItemPrototype:
|
||||
def render_content(self):
|
||||
if self.image_src_file.exists():
|
||||
shutil.copyfile(self.image_src_file, output_dir / self.image)
|
||||
if self.custom_css_src_file.exists():
|
||||
shutil.copyfile(self.custom_css_src_file, output_dir / self.custom_css)
|
||||
if self.custom_js_src_file.exists():
|
||||
shutil.copyfile(self.custom_js_src_file, output_dir / self.custom_js)
|
||||
with self.content_output_path.open("w", encoding="utf-8") as f:
|
||||
#f.write(content_item_template.render(page_title = self.title, header_image = header_image, image = self.image, custom_css = self.custom_css, custom_js = self.custom_js, content=self.html))
|
||||
f.write(content_item_template.render(content_item = self, page_title = self.title))
|
||||
|
||||
def __init__(self, md_file):
|
||||
self.slug = md_file.stem
|
||||
self.data = frontmatter.load(md_file)
|
||||
self.html = markdown.markdown(self.data.content)
|
||||
self.preview = self.html[:300] + " <a href=" + f"{self.slug}.html" + ">...read more</a>"
|
||||
self.title = self.data.get("title", self.slug)
|
||||
self.omit_second_title = self.data.get("omit_second_title", False)
|
||||
self.date = self.data.get("data", "2000-01-01T00:00:00+03:00")
|
||||
self.content_output_path = output_dir / f"{self.slug}.html"
|
||||
self.image_src_file = Path(f"{content_dir}/{md_file.stem}.jpg")
|
||||
self.image = f"images/{md_file.stem}.jpg" if self.image_src_file.exists() else None
|
||||
if self.slug == 'about':
|
||||
self.image = 'static/about.jpg'
|
||||
self.custom_css_src_file = Path(f"{content_dir}/{md_file.stem}.css")
|
||||
self.custom_css = f"assets/css/{md_file.stem}.css" if self.custom_css_src_file.exists() else None
|
||||
self.custom_js_src_file = Path(f"{content_dir}/{md_file.stem}.js")
|
||||
self.custom_js = f"assets/js/{md_file.stem}.js" if self.custom_js_src_file.exists() else None
|
||||
|
||||
# Recreate the output dir if needed
|
||||
if output_dir.exists():
|
||||
shutil.rmtree(output_dir)
|
||||
output_dir.mkdir()
|
||||
|
||||
# Create public subdirs
|
||||
img_dir.mkdir()
|
||||
assets_dir.mkdir()
|
||||
assets_css_dir.mkdir()
|
||||
assets_js_dir.mkdir()
|
||||
|
||||
# Copy static files if exist
|
||||
if static_dir.exists():
|
||||
shutil.copytree(static_dir, output_dir / "static")
|
||||
|
||||
# Prepare template rendering engine
|
||||
env = Environment(loader=FileSystemLoader(str(template_dir)))
|
||||
env.globals['header_image'] = header_image
|
||||
index_template = env.get_template("index.html")
|
||||
content_item_template = env.get_template("content_item.html")
|
||||
|
||||
# Parse the content files
|
||||
content_items = []
|
||||
for md_file in content_dir.glob("*.md"):
|
||||
citem = ContentItemPrototype(md_file)
|
||||
citem.render_content()
|
||||
content_items.append({
|
||||
"slug": citem.slug,
|
||||
"title": citem.title,
|
||||
"date": citem.date,
|
||||
"preview": markdown.markdown(citem.preview),
|
||||
"image": citem.image,
|
||||
})
|
||||
|
||||
# Render the index file
|
||||
with (output_dir / "index.html").open("w", encoding="utf-8") as f:
|
||||
f.write(index_template.render(page_title = Config.MAIN_PAGE_TITLE, content_items=content_items))
|
||||
|
||||
# Render the about file
|
||||
about_content = ContentItemPrototype(Path('static/about.md'))
|
||||
about_content.render_content()
|
||||
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
python-frontmatter
|
||||
jinja2
|
||||
markdown
|
||||
BIN
static/about.jpg
Normal file
BIN
static/about.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
6
static/about.md
Normal file
6
static/about.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "About"
|
||||
omit_second_title: True
|
||||
---
|
||||
|
||||
|
||||
BIN
static/header.jpg
Normal file
BIN
static/header.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
24
templates/content_item.html
Normal file
24
templates/content_item.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends "default.html" %}
|
||||
|
||||
{% block head_includes %}
|
||||
{% if content_item.custom_css %}
|
||||
<link href="{{ content_item.custom_css }}">
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4 mb-5">
|
||||
{% if content_item.image %}<img src="{{ content_item.image }}" class="img-fluid mb-5" style="border-radius: 5px;">{% endif %}
|
||||
{% if not content_item.omit_second_title %}
|
||||
<h1>{{ content_item.title }}</h1>
|
||||
{% endif %}
|
||||
<article>{{ content_item.html | safe }}</article>
|
||||
<a href="index.html" class="btn btn-secondary mt-4">← Back</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer_includes %}
|
||||
{% if content_item.custom_js %}
|
||||
<script src="{{ content_item.custom_js }}"></script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
90
templates/default.html
Normal file
90
templates/default.html
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
{% set base = "" %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
|
||||
<title>{{ page_title }}</title>
|
||||
<style>
|
||||
.top_header {
|
||||
position: relative;
|
||||
height: 250px;
|
||||
background: rgb(70, 70, 124);
|
||||
background-image: url("{{ header_image }}");
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.top_header_text {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 2.5rem;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border-radius: 5px;
|
||||
padding: 10px 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
||||
.top_menu a {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
padding: 6px 12px;
|
||||
margin-left: 10px;
|
||||
text-decoration: none;
|
||||
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.top_menu a:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.top_header_text {
|
||||
font-size: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
article {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
{% block head_includes %}
|
||||
{% endblock %}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="top_header">
|
||||
<div >
|
||||
<h1 class="top_header_text"><a style="border-radius: 5px; padding: 10px; opacity: 0.7; background: black">{{ page_title }}</a></h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="top_menu d-flex justify-content-end" style="margin-bottom: 20px;">
|
||||
|
||||
<a href="index.html">Home</a>
|
||||
<a href="about.html">About</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="content">{% block content %}{% endblock %}</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
|
||||
{% block footer_includes %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
19
templates/index.html
Normal file
19
templates/index.html
Normal file
@@ -0,0 +1,19 @@
|
||||
{% extends "default.html" %}
|
||||
{% block content %}
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-3 g-4 px-1 py-2">
|
||||
{% for content_item in content_items %}
|
||||
<div class="col mb-3">
|
||||
<div class="card h-100" style="border-radius: 5px;">
|
||||
<img src="{{ content_item.image }}" alt="{{ content_item.title }}" class="card-img-top img-fluid" style="background: rgb(77, 77, 77); object-fit: cover; height: 200px; border-radius: 5px;">
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title">{{ content_item.title }}</h5>
|
||||
<p class="card-text mb-3">{{ content_item.preview | safe}}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user