Compare commits

...

27 Commits

Author SHA1 Message Date
m
7fda9dde36 fix 2026-03-26 16:13:57 +01:00
m
194b1e04cc fix 2026-03-26 16:11:51 +01:00
m
f3ff920e38 render 2026-03-26 16:09:38 +01:00
m
a47ad949bd preview 2026-03-26 16:07:05 +01:00
m
2df2f5f3a8 import 2026-03-25 13:02:41 +01:00
m
62445a1812 check comp 2026-03-25 12:56:06 +01:00
m
6f046b3743 fix typo 2026-03-25 12:45:43 +01:00
m
1c23c05cc7 render fn 2026-03-25 12:42:26 +01:00
m
0272ec3d07 fix 2026-03-25 08:14:00 +01:00
m
81c093aa99 fix 2026-03-25 08:12:34 +01:00
m
2118d9fbc0 code template 2026-03-25 08:08:07 +01:00
m
20fce270a5 inject css 2026-03-25 05:03:29 +01:00
m
9a83309d36 preview 2026-03-25 01:51:21 +01:00
m
a90f06d913 combination 2026-03-25 01:32:03 +01:00
m
67ec5753bf testline 2026-03-24 21:54:07 +01:00
m
a38aad6576 add test 2026-03-24 21:47:48 +01:00
m
11aac567ec syntax fix 2026-03-24 21:44:12 +01:00
m
6c5649d721 image html 2026-03-24 21:41:05 +01:00
m
86588c0080 image templates 2026-03-24 21:33:10 +01:00
m
664900cbd1 image template 2026-03-24 21:27:59 +01:00
m
80c66c1802 blog title fix 2026-03-22 20:59:58 +01:00
m
8ad581658b title fix 2026-03-22 20:56:09 +01:00
m
8193bdd7f2 add favicon 2026-03-22 20:20:30 +01:00
m
acc9710991 logic path 2026-03-22 09:21:50 +01:00
m
034d9bc884 Merge branch 'main' of https://git.etwasse.de/Mira/flask_blog 2026-03-22 08:06:50 +01:00
m
fae653a2b9 about template 2026-03-22 08:01:38 +01:00
396ad2de38 Update readme.md 2026-03-17 10:03:24 +00:00
22 changed files with 354 additions and 184 deletions

52
app.py
View File

@@ -1,7 +1,7 @@
from flask import Flask, render_template, request, redirect, url_for
from flask import Flask, render_template, request, redirect, url_for, send_from_directory
from content.posts import BLOG_POSTS
from flask_logic.logic import get_enriched_post, get_comments_for_post, save_comment
from flask_logic.renderer import process_post_content, collect_css, generate_preview
# 🌟 IMPORT THE content from separate files.
from content.posts import BLOG_POSTS
from content.about_text import ABOUT, TITLE
@@ -78,13 +78,26 @@ def calculate_pagination(posts, posts_per_page, page):
'total_posts': total_posts
}
@app.route('/')
def home():
posts_with_preview = []
for post in BLOG_POSTS:
preview = generate_preview(post.get("content", ""))
post_copy = dict(post)
post_copy["preview"] = preview
posts_with_preview.append(post_copy)
"""Home page with paginated blog posts."""
page = request.args.get('page', 1, type=int)
blog_title = TITLE
pagination = calculate_pagination(BLOG_POSTS, POSTS_PER_PAGE, page)
pagination = calculate_pagination(posts_with_preview, POSTS_PER_PAGE, page)
return render_template('index.html',
posts=pagination['posts_to_show'],
@@ -107,18 +120,43 @@ def about():
@app.route('/post/<int:post_id>')
def post_detail(post_id):
# One call to get the data + the extra logic (templates/timelines)
post = get_enriched_post(post_id, BLOG_POSTS)
context = {"used_components": set()}
post = get_enriched_post(post_id, BLOG_POSTS)
if not post:
return "Post not found", 404
comments = get_comments_for_post(post_id)
return render_template('post_detail.html', post=post, comments=comments, blog_title = TITLE)
processed_content = process_post_content(
post.get('content', ''), context=context
)
css_files = collect_css(context)
return render_template(
"post_detail.html",
post=post,
content=processed_content,
comments=comments,
blog_title=TITLE,
component_css=css_files
)
@app.route('/content/image/<path:filename>')
def content_image_files(filename):
directory = 'content/image'
full_path = os.path.join(os.getcwd(), directory, filename) # Your actual file location
print(f"Requested: {filename}")
print(f"Directory: {os.getcwd()}/{directory}")
print(f"Full path: {full_path}")
print(f"Exists: {os.path.exists(full_path)}")
if not os.path.exists(full_path):
return f"File not found: {full_path}", 404
return send_from_directory(directory, filename)
if __name__ == '__main__':
app.run(debug=True)

Submodule content updated: d0e3035242...cb53a98cca

0
flask_logic/__init__.py Normal file
View File

60
flask_logic/components.py Normal file
View File

@@ -0,0 +1,60 @@
from flask_logic.registry import register_component
@register_component("image", css="css/components/image.css")
def render_image_component(src, caption=None, css_class=None, context=None):
if context:
context["used_components"].add("image")
base_class = "image-container"
full_class = f"{base_class} {css_class}" if css_class else base_class
caption_html = f'<div class="caption">{caption}</div>' if caption else ""
return f"""
<div class="{full_class}">
<img src="/content/image/{src}" alt="{caption or ''}">
{caption_html}
</div>
"""
@register_component("code", css="css/components/code.css")
def render_code_block(value=None, code=None, context=None):
if context:
context["used_components"].add("code")
code = code or value or ""
return f"""
<pre class="code-block"><code>{code}</code></pre>
"""
@register_component("tree", css="css/components/tree.css")
def render_tree_component(context=None):
if context:
context["used_components"].add("tree")
return f"""
<div class="background-svg">
<img src="/content/image/animation.svg" alt="" />
<br />
<p>tree source: codepen @uchardon</p>
</div>"""
@register_component("timeline", css="css/components/timeline.css")
def render_timeline_component(timeline=None, value=None, context=None):
if context:
context["used_components"].add("timeline")
timeline = timeline or value or ""
return f"""
<div class="tw-w-full tw-mx-auto tw-px-0 tw-py-12">
<ol
class="tw-relative tw-border-l-2 tw-border-gray-300 tw-list-none tw-p-0 tw-m-0 tw-ml-4"
>
{timeline}
</ol>
</div>
<li class="tw-mb-12 tw-ml-6 tw-list-none tw-relative"></li>
"""

View File

@@ -48,14 +48,20 @@ def load_snippet(filename):
def get_enriched_post(post_id, BLOG_POSTS):
post = next((p for p in BLOG_POSTS if p.get('id') == post_id), None)
if not post:
return None
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
DATA_DIR = BASE_DIR / "content" / "data"
file_path_10 = DATA_DIR / "post_10_timeline.html"
# Mapping config: keeps logic.py tiny
configs = {
10: {
"template": "components/timeline.html",
"timeline_file": "post_10_timeline.html"
"timeline_file": file_path_10
},
8: {
"template": "components/christmas_post.html",

12
flask_logic/registry.py Normal file
View File

@@ -0,0 +1,12 @@
# component_registry.py
COMPONENTS = {}
def register_component(name, css=None):
def decorator(func):
COMPONENTS[name] = {
"render": func,
"css": css
}
return func
return decorator

128
flask_logic/renderer.py Normal file
View File

@@ -0,0 +1,128 @@
import re
# FORCE component registration
import flask_logic.components
from flask_logic.registry import COMPONENTS
# ------------------------
# Content renderer (blocks)
# ------------------------
def render_blocks(blocks, context=None):
html_output = []
print(COMPONENTS)
for block in blocks:
block_type = block.get("type")
if block_type == "text":
html_output.append(block.get("value", ""))
elif block_type in COMPONENTS:
render_func = COMPONENTS[block_type]["render"]
# remove "type" before passing kwargs
kwargs = {k: v for k, v in block.items() if k != "type"}
html_output.append(
render_func(**kwargs, context=context)
)
return "\n".join(html_output)
# ------------------------
# Old string renderer (tokens)
# ------------------------
def parse_options(option_string):
options = {}
if not option_string:
return options
parts = option_string.split("|")
for part in parts:
if "=" in part:
key, value = part.split("=", 1)
options[key.strip()] = value.strip()
return options
def render_content(content, context=None):
if not content:
return ""
def replace_image(match):
src = match.group("src").strip()
options = parse_options(match.group("options"))
render_func = COMPONENTS["image"]["render"]
return render_func(
src=src,
caption=options.get("caption"),
css_class=options.get("class"),
context=context
)
pattern = r"\[image:(?P<src>[^\|\]]+)(?:\|(?P<options>[^\]]+))?\]"
return re.sub(pattern, replace_image, content)
# ------------------------
# Unified entry
# ------------------------
def process_post_content(content, context=None):
if isinstance(content, str):
return render_content(content, context=context)
elif isinstance(content, list):
return render_blocks(content, context=context)
return ""
# ------------------------
# CSS collector
# ------------------------
def collect_css(context):
css_files = []
for comp in context["used_components"]:
css = COMPONENTS.get(comp, {}).get("css")
if css:
css_files.append(css)
return css_files
def generate_preview(content, max_length=200):
if isinstance(content, str):
# old system
text = re.sub(r"<[^>]+>", "", content) # strip HTML
return text[:max_length]
elif isinstance(content, list):
# new system
for block in content:
if block.get("type") == "text":
text = re.sub(r"<[^>]+>", "", block.get("value", ""))
return text[:max_length]
return ""
# optinal, for images
def generate_preview_html(content, context=None):
if isinstance(content, list):
for block in content:
if block.get("type") == "text":
return f"<div class='blog-preview'>{block.get('value')}</div>"
elif block.get("type") in COMPONENTS:
# optional: allow image preview
render_func = COMPONENTS[block["type"]]["render"]
kwargs = {k: v for k, v in block.items() if k != "type"}
return render_func(**kwargs, context=context)
elif isinstance(content, str):
return content[:200]
return ""

View File

@@ -1,7 +1,9 @@
# Flask Blog Templates
A minimal **Flask template set** for my blog.
This project adapts and evolves the original [simple-blog-template](https://github.com/earlbread/simple-blog-template) by [Seunghun Lee](https://github.com/earlbread).
This project adapts and the original [simple-blog-template](https://github.com/earlbread/simple-blog-template) by [Seunghun Lee](https://github.com/earlbread).
Content is **static**; selected templates (with **fixed** names/paths) are loaded dynamically.
Initially deployed using uWSGI and Nginx; later migrated to Gunicorn, Caddy, and Docker. Most versions should work.
## Git Tag

View File

@@ -1,124 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 131 140">
<style>
svg {
max-height: 60vh;
overflow: visible;
}
path {
fill: #379157;
stroke: #379157;
stroke-width: 0.2;
transform: scale(0);
transform-origin: 50% 50%;
animation: star 8s ease-in-out infinite;
animation-delay: calc(var(--no) * 0.025s);
transform-box: fill-box;
}
@keyframes star {
0% {
transform: scale(0);
animation-timing-function: cubic-bezier(0.74, 1.72, 0.57, 1.01);
}
10% {
transform: scale(1);
}
65% {
transform: translateY(0px) scale(1);
}
75% {
transform: translateY(50px) scale(0);
}
100% {
transform: translateY(0px) scale(0);
}
}
</style>
<path d="m49.35 6.37-1.88-.93-1.85 1.01.32-2.07-1.54-1.43 2.08-.34.9-1.9.96 1.86 2.1.25-1.48 1.5z" style="--no:100;fill:#d8e540;stroke:#d8e540;"/>
<path d="m129.54 125.15-.82-.26-.68.53-.01-.86-.71-.48.81-.28.23-.83.52.7.86-.04-.5.7z" style="--no: 1; transform: scale(222);"/>
<path d="m126.73 127.78-1.39-.2-.95 1.03-.24-1.38-1.27-.59 1.24-.65.16-1.39 1 .98 1.38-.28-.62 1.26z" style="--no:2"/>
<path d="m120.86 129.62-1.36-.62-1.28.78.16-1.49-1.14-.97 1.46-.31.58-1.38.75 1.3 1.49.12-1 1.1z" style="--no:3"/>
<path d="m115.25 131.06-2.48-1.02-2.25 1.47.2-2.68-2.1-1.68 2.62-.64.95-2.5 1.41 2.28 2.68.12-1.73 2.05z" style="--no:4"/>
<path d="m106.57 132.05-1.32-.75-1.37.67.3-1.49-1.05-1.09 1.5-.17.71-1.34.64 1.37 1.49.27-1.12 1.02z" style="--no:5"/>
<path d="m107.36 126.39-1.26-.48-1.11.77.06-1.35-1.07-.82 1.3-.35.44-1.28.74 1.13 1.36.03-.85 1.05z" style="--no:6"/>
<path d="m101.6 129.66-3.35-1.58-3.2 1.87.47-3.68-2.77-2.46 3.64-.7 1.5-3.39 1.78 3.25 3.69.37-2.54 2.7z" style="--no:7"/>
<path d="m91.68 125.45-2.42-1.71-2.78 1.03.87-2.83-1.83-2.33 2.96-.05 1.64-2.46.96 2.8 2.85.8-2.37 1.78z" style="--no:8"/>
<path d="m88.75 115.32-1.92.87-.37 2.08-1.42-1.55-2.1.29 1.05-1.84-.93-1.9 2.07.43 1.52-1.47.24 2.1z" style="--no:9"/>
<path d="m80.86 119.34-1.99-.75-1.74 1.22.1-2.12-1.7-1.28 2.05-.57.7-2 1.17 1.77 2.12.04-1.33 1.66z" style="--no:10"/>
<path d="m80.76 112.4-1.91-.46-1.46 1.32-.16-1.96-1.7-.98 1.8-.76.4-1.92 1.3 1.49 1.95-.21-1.02 1.68z" style="--no:11"/>
<path d="m73.42 115.87-2.31-1.18-2.28 1.23.4-2.55-1.87-1.79 2.56-.4 1.11-2.34 1.18 2.3 2.57.35-1.83 1.83z" style="--no:12"/>
<path d="m73.07 106.79-.93.2-.36.87-.48-.82-.95-.07.63-.7-.22-.93.87.38.8-.5-.09.95z" style="--no:13"/>
<path d="m66.62 105.3-.92-.58-.99.42.27-1.05-.7-.81 1.07-.07.55-.92.4 1 1.05.24-.83.7z" style="--no:14"/>
<path d="m64.58 114.97-3.79-2.46-4.18 1.72 1.17-4.36-2.93-3.45 4.52-.23 2.37-3.85 1.61 4.22 4.4 1.06-3.52 2.85z" style="--no:15"/>
<path d="m57.23 104.7-1.17-.97-1.46.43.56-1.42-.86-1.26 1.52.1.94-1.2.38 1.47 1.43.51-1.28.82z" style="--no:16"/>
<path d="m51.83 107.18-2.77-.7-2.14 1.9-.2-2.85-2.46-1.45 2.65-1.07.62-2.78 1.84 2.18 2.83-.27-1.5 2.42z" style="--no:17"/>
<path d="m54.36 113.53-2.02.23-.96 1.8-.84-1.86-2-.36 1.5-1.37-.28-2.01 1.77 1 1.83-.88-.41 1.99z" style="--no:18"/>
<path d="m44.68 113.98-1.72-1.4-2.12.63.8-2.06-1.25-1.83 2.2.12 1.35-1.75.57 2.14 2.09.74-1.86 1.2z" style="--no:19"/>
<path d="m44.48 117.46-.94-.5-.94.5.18-1.05-.76-.74 1.05-.15.46-.95.48.95 1.05.15-.76.74z" style="--no:20"/>
<path d="m38.72 116.76-2.49-.72-2 1.64-.08-2.59-2.18-1.4 2.43-.87.66-2.51 1.59 2.04 2.59-.15-1.46 2.15z" style="--no:21"/>
<path d="m42.9 104.65-1.42-.85-1.5.68.37-1.6-1.1-1.21 1.63-.15.81-1.43.65 1.52 1.61.33-1.24 1.08z" style="--no:22"/>
<path d="m31.82 119.39-1.35-.43-1.12.87-.01-1.43-1.17-.8 1.34-.44.4-1.37.85 1.15 1.42-.04-.83 1.15z" style="--no:23"/>
<path d="M30.14 114.38h-1l-.54.83-.32-.94-.95-.27.8-.59-.04-.99.8.58.93-.34-.3.94z" style="--no:24"/>
<path d="m25.22 120.18-2.06-.89-1.9 1.19.21-2.24-1.72-1.44 2.2-.49.84-2.08 1.14 1.93 2.24.16-1.49 1.69z" style="--no:25"/>
<path d="m18.67 122.17-1.53-.5-1.27.99v-1.61l-1.33-.91 1.53-.5.45-1.54.95 1.3 1.6-.05-.94 1.3z" style="--no:26"/>
<path d="m12.72 123.8-1.1-.26-.84.74-.07-1.12-.97-.57 1.04-.42.24-1.1.73.86 1.11-.1-.6.95z" style="--no:27"/>
<path d="m8.25 126.29-1.18-.52-1.1.67.14-1.28-.98-.84 1.26-.26.49-1.2.65 1.12 1.28.1-.86.96z" style="--no:28"/>
<path d="m3.43 128.67-1.11-.26-.85.78-.1-1.14-1-.57 1.06-.45.22-1.12.76.86 1.14-.13-.6.99z" style="--no:29"/>
<path d="m36.74 105.6-3.33-1.68-3.27 1.79.58-3.69L28 99.46l3.68-.6 1.6-3.36 1.7 3.32 3.7.48-2.64 2.64z" style="--no:30"/>
<path d="m28.14 106.59-1.84-.42-1.39 1.28-.17-1.87-1.65-.93 1.74-.75.37-1.85 1.24 1.42 1.88-.22-.97 1.63z" style="--no:31"/>
<path d="m23.8 102.17-.71-.49-.8.31.24-.82-.54-.66.85-.03.47-.71.29.8.82.22-.68.52z" style="--no:32"/>
<path d="m20.1 108.3-1.52-1.19-1.85.58.66-1.82-1.12-1.58 1.94.07 1.16-1.55.53 1.86 1.84.62-1.61 1.08z" style="--no:33"/>
<path d="m14.73 109.75-1.37-.47-1.16.87.02-1.45-1.18-.84 1.38-.43.43-1.38.84 1.18 1.45-.02-.87 1.17z" style="--no:34"/>
<path d="m8.87 112.07-.96-.54-1 .5.22-1.1-.77-.78 1.1-.14.5-.98.47 1 1.1.19-.82.75z" style="--no:35"/>
<path d="m37.72 97.29-.82-.4-.8.45.13-.9-.67-.62.9-.16.38-.83.43.8.9.1-.63.67z" style="--no:36"/>
<path d="m43.69 97.64-1.87-1.15-2.01.88.52-2.14-1.47-1.64 2.2-.16 1.1-1.9.84 2.04 2.15.46-1.68 1.42z" style="--no:37"/>
<path d="m51.23 94.34-1.54.2-.72 1.37-.65-1.4-1.54-.27 1.14-1.05-.23-1.54 1.36.75 1.39-.7-.3 1.53z" style="--no:38"/>
<path d="m50.46 90.7-1.14-.82-1.29.58.42-1.35-.94-1.04 1.4-.02.7-1.22.45 1.33 1.38.3-1.13.84z" style="--no:39"/>
<path d="m60.25 91.43-3.43-1.99-3.46 1.93.83-3.88-2.9-2.7 3.94-.4 1.67-3.6 1.6 3.63 3.94.47-2.95 2.65z" style="--no:40"/>
<path d="m64.71 84.62-.84-.82-1.13.3.52-1.04-.64-.99 1.16.17.74-.91.2 1.15 1.09.42-1.04.55z" style="--no:41"/>
<path d="m71.62 83.43-.96-.53-.95.57.2-1.09-.82-.72 1.09-.15.43-1.01.48 1 1.1.1-.8.75z" style="--no:42"/>
<path d="m69.06 91.57-2.25-1.58-2.52 1.12.81-2.63-1.85-2.04 2.76-.05L67.38 84l.9 2.6 2.69.57-2.2 1.66z" style="--no:43"/>
<path d="m76.64 88.66-1.87-.98-1.8 1.11.35-2.08-1.61-1.36 2.09-.32.8-1.95.94 1.89 2.1.15-1.5 1.49z" style="--no:44"/>
<path d="m83.66 86.9-2.15-1.06-2 1.3.34-2.36L78 83.27l2.36-.4.86-2.24 1.11 2.12 2.4.13-1.68 1.71z" style="--no:45"/>
<path d="m89.28 83.7-1.25-.96-1.47.57.53-1.48-1-1.23 1.58.06.86-1.34.44 1.52 1.52.4-1.3.89z" style="--no:47"/>
<path d="m94.37 80.9-1.12-.38-.9.76.03-1.18-1-.62 1.12-.34.28-1.14.68.96 1.17-.09-.7.94z" style="--no:48"/>
<path d="m53.76 83.53-.99-.56-.99.55.24-1.11-.84-.78 1.13-.12.48-1.03.46 1.04 1.13.13-.84.77z" style="--no:49"/>
<path d="m49.34 84.58-2.28-1.18-2.18 1.36.41-2.53-1.96-1.66 2.54-.39.97-2.37 1.15 2.3 2.56.18-1.82 1.8z" style="--no:50"/>
<path d="m40.4 85.88-1.61-1.35-1.99.68.8-1.94-1.27-1.68 2.1.15 1.2-1.72.5 2.04 2.01.62-1.79 1.1z" style="--no:51"/>
<path d="m34.53 85.6-3.5-1.38-2.97 2.3.23-3.76-3.1-2.13 3.64-.93 1.07-3.6 2 3.17 3.77-.1-2.4 2.9z" style="--no:52"/>
<path d="m23.9 86.9-1.68-1.5-2.15.66L21 84l-1.3-1.84 2.24.24 1.35-1.8.47 2.2 2.13.72-1.95 1.13z" style="--no:53"/>
<path d="m18.21 88.34-1.62-.6-1.34 1.1.06-1.73-1.45-.94 1.66-.47.44-1.67.97 1.43 1.72-.1-1.06 1.37z" style="--no:54"/>
<path d="m35.48 77.6-1.07-.58-1.05.62.22-1.2-.92-.8 1.21-.16.48-1.12.53 1.1 1.21.11-.88.84z" style="--no:55"/>
<path d="M41.72 78.62 40.12 77l-2.2.54 1.05-2.02-1.2-1.93 2.25.37 1.47-1.73.33 2.25 2.1.85-2.03 1.02z" style="--no:56"/>
<path d="m49.62 74.51-1.93-.58-1.5 1.36-.03-2.02-1.76-1 1.9-.66.41-1.97 1.22 1.6 2-.22-1.14 1.66z" style="--no:57"/>
<path d="m55.27 71.78-1.65-.53-1.3 1.14v-1.73l-1.49-.88 1.64-.54.38-1.69 1.02 1.4 1.73-.16-1.02 1.4z" style="--no:58"/>
<path d="m63 71.81-2.43-1.38-2.42 1.38.56-2.73-2.06-1.88 2.77-.3 1.15-2.54 1.15 2.53 2.77.31-2.06 1.88z" style="--no:59"/>
<path d="m69.27 69.46-1.16-1.2-1.62.4.77-1.48-.87-1.41 1.64.27 1.08-1.27.24 1.65 1.54.64-1.49.74z" style="--no:60"/>
<path d="m76.01 70.02-1.24-.55-1.11.78.14-1.36-1.09-.82 1.33-.28.45-1.28.68 1.18 1.35.02-.9 1.01z" style="--no:61"/>
<path d="m81.2 67.28-1.02-.37-.84.7.03-1.1-.92-.58 1.05-.3.27-1.06.62.9 1.08-.06-.66.86z" style="--no:62"/>
<path d="m56.57 65.7-.93-.61-1.01.48.3-1.08-.77-.81 1.11-.05.54-.98.39 1.05 1.1.2-.88.7z" style="--no:63"/>
<path d="m50.57 67.67-1.71-1.65-2.3.63 1.04-2.14-1.3-1.98 2.35.32L50.14 61l.41 2.34 2.23.84-2.1 1.12z" style="--no:64"/>
<path d="m43.73 67.04-1.33-1.07-1.59.58.61-1.58-1.05-1.33 1.7.09.94-1.42.44 1.65 1.63.46-1.42.92z" style="--no:65"/>
<path d="m38.39 65.28-2.84-1.44-2.7 1.7.5-3.15-2.46-2.04 3.15-.5 1.18-2.97 1.45 2.84 3.19.21-2.25 2.26z" style="--no:66"/>
<path d="m29.58 65.7-1.85-.51-1.39 1.32-.08-1.91-1.69-.92 1.8-.67.35-1.89 1.2 1.51 1.9-.26-1.07 1.6z" style="--no:67"/>
<path d="m22.67 67.35-.95-.82-1.19.4.5-1.16-.75-1.01 1.25.1.73-1 .28 1.21 1.2.39-1.08.64z" style="--no:68"/>
<path d="m46.67 60.54-2.1-1.18-2.09 1.2.48-2.36-1.79-1.62 2.4-.28.99-2.2 1 2.2 2.4.26-1.78 1.62z" style="--no:69"/>
<path d="m52.22 59.39-1.15-.58-1.08.7.2-1.27-1-.82 1.27-.2.46-1.2.6 1.14 1.27.08-.9.9z" style="--no:70"/>
<path d="m57.73 56.34-.74-.95-1.19.16.67-1-.52-1.08 1.15.33.88-.82.04 1.2 1.05.57-1.12.4z" style="--no:71"/>
<path d="m66.78 48.61-1.14-.34-.88.81-.03-1.19-1.04-.59 1.12-.4.24-1.17.73.95 1.18-.13-.68.98z" style="--no:72"/>
<path d="m60.82 51.53-1.55-1.18-1.8.73.63-1.83-1.25-1.49 1.95.04 1.03-1.65.56 1.86 1.88.47-1.6 1.1z" style="--no:73"/>
<path d="m53.55 54.76-2.32-1.74-2.68 1.11.93-2.74-1.88-2.21 2.9.04 1.5-2.48.86 2.77 2.82.68-2.37 1.67z" style="--no:74"/>
<path d="M45.34 50.09 44 49.44l-1.23.82.2-1.47-1.16-.93 1.46-.26.52-1.38.7 1.3 1.48.07-1.03 1.07z" style="--no:75"/>
<path d="m40.18 48.79-1.77-1.15-1.9.93.55-2.04-1.47-1.51 2.1-.11.99-1.87.76 1.97 2.08.36-1.64 1.33z" style="--no:76"/>
<path d="m33.37 48.93-1.14-.4-.92.79.02-1.21-1.03-.64 1.16-.35.28-1.18.7 1 1.2-.1-.73.97z" style="--no:77"/>
<path d="m44 43.45-1.1-.09-.66.9-.25-1.08-1.06-.34.95-.58v-1.11l.84.73 1.06-.35-.44 1.03z" style="--no:78"/>
<path d="m47.65 45.49-1.2-1.17-1.63.44.74-1.51-.92-1.41 1.67.23 1.06-1.3.28 1.65 1.58.6-1.49.79z" style="--no:79"/>
<path d="m55.3 43.35-2.53-.88-2.06 1.72.06-2.68-2.28-1.42 2.57-.78.65-2.6 1.53 2.2 2.68-.19-1.62 2.14z" style="--no:80"/>
<path d="m59.03 37.84-1.01-.56-1 .58.22-1.13-.86-.77 1.15-.14.46-1.06.49 1.05 1.14.12-.84.78z" style="--no:81"/>
<path d="m53.55 34.33-1.91-1.11-1.93 1.07.46-2.16-1.62-1.51 2.2-.22.94-2.01.9 2.02 2.19.27-1.65 1.48z" style="--no:82"/>
<path d="m46.29 36.65-.8-.77-1.05.3.48-1-.6-.91 1.09.15.68-.85.2 1.08 1.02.39-.97.51z" style="--no:83"/>
<path d="m39.44 36.68-1.15-1.11-1.56.42.7-1.45-.88-1.34 1.6.22 1-1.25.29 1.58 1.5.57-1.42.75z" style="--no:84"/>
<path d="m46.78 32.72-2.41-1.47-2.49 1.33.65-2.75-2.03-1.96 2.8-.23 1.25-2.53 1.09 2.6 2.78.4-2.13 1.83z" style="--no:85"/>
<path d="m50.22 23.8-2.4-.6-1.74 1.73-.16-2.46-2.19-1.13 2.3-.9.39-2.44 1.57 1.9 2.43-.38-1.32 2.08z" style="--no:86"/>
<path d="m48.64 16.71-1.1-.7-1.17.59.33-1.26-.92-.93 1.3-.08.6-1.16.48 1.21 1.29.22-1.01.82z" style="--no:87"/>
</svg>

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,16 @@
.code-block {
display: block;
white-space: pre-wrap; /* Allows the code to wrap within the container */
word-wrap: break-word; /* Ensures long words break to fit the container */
max-width: 100%; /* Ensures the code block doesn't exceed the container's width */
padding: 10px; /* Adds padding for better readability */
background-color: #f5f5f5; /* Light gray background for contrast */
border: 1px solid #ddd; /* Subtle border to distinguish the code block */
border-radius: 5px; /* Rounded corners for aesthetics */
overflow-x: auto; /* Adds horizontal scroll if necessary */
}
.code-block .comment {
text-indent: 8em;
color: #408080;
}

View File

@@ -0,0 +1,16 @@
.image-container {
margin-top: 10px;
margin-bottom: 10px;
width: 100%;
height: 20vh; /* Show 20% of the viewport height */
overflow: hidden; /* Hide parts of the image outside the container */
position: relative; /* Position context for the image */
}
.image-container img {
width: 100%; /* Make the image fit the container width */
height: auto; /* Maintain aspect ratio */
position: absolute; /* Position image relative to container */
top: 50%; /* Move image down by 50% of its height */
transform: translateY(-50%); /* Pull it back up by 50% of its own height */
}

View File

@@ -0,0 +1,10 @@
/* --- Decoration --- */
.background-svg {
position: fixed;
bottom: 100px;
right: 200px;
width: 200px;
z-index: -1;
pointer-events: none;
opacity: 0.6;
}

View File

@@ -221,17 +221,6 @@ nav {
border-color: #ccc;
}
/* --- Decoration --- */
.background-svg {
position: fixed;
bottom: 100px;
right: 200px;
width: 200px;
z-index: -1;
pointer-events: none;
opacity: 0.6;
}
/* Login, Sign up, New post */
.login,
.signup {

39
static/favicon.svg Normal file
View File

@@ -0,0 +1,39 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2415 4020 c-645 -57 -1178 -445 -1378 -1004 l-32 -90 -105 -31
c-370 -111 -623 -269 -706 -441 -23 -49 -28 -72 -28 -139 -1 -71 3 -88 31
-145 92 -187 365 -349 785 -466 70 -19 174 -45 232 -57 l105 -22 86 -80 c228
-211 505 -354 806 -415 288 -59 609 -41 891 50 126 41 334 146 441 223 48 35
126 99 173 143 67 62 94 81 127 87 246 47 519 132 696 218 202 97 325 199 384
319 28 57 32 74 31 145 0 67 -5 90 -28 139 -82 170 -337 330 -700 440 l-109
34 -29 78 c-190 524 -651 895 -1233 994 -134 22 -319 31 -440 20z m274 -160
c366 -33 677 -170 922 -407 155 -150 266 -323 328 -508 43 -130 47 -161 24
-173 -74 -39 -304 -100 -508 -136 -295 -51 -436 -61 -900 -61 -461 0 -575 8
-881 60 -271 46 -534 124 -534 159 0 34 55 199 94 282 65 137 137 237 260 360
271 272 621 417 1046 433 19 0 86 -4 149 -9z m-1719 -1128 c0 -34 33 -65 106
-98 226 -103 605 -180 1047 -214 178 -14 697 -14 874 0 455 35 817 109 1048
215 72 32 105 63 105 97 0 10 2 18 4 18 19 0 232 -81 312 -120 232 -110 351
-233 330 -343 -18 -95 -118 -187 -311 -282 -156 -77 -283 -122 -499 -175
-1067 -264 -2632 -182 -3351 174 -159 79 -251 152 -289 227 -14 29 -26 66 -26
81 0 131 198 281 525 398 129 46 125 45 125 22z m923 -1187 c448 -44 988 -40
1445 11 79 9 146 14 148 11 3 -3 -32 -30 -78 -60 -506 -335 -1182 -336 -1693
-3 -47 31 -85 59 -85 62 0 4 26 4 58 0 31 -4 124 -14 205 -21z"/>
<path d="M735 2335 c-29 -28 -32 -70 -10 -103 23 -32 129 -104 220 -150 182
-91 426 -157 715 -192 280 -33 851 -32 904 3 33 22 44 63 25 96 -25 45 -48 49
-249 40 -208 -10 -521 2 -705 26 -330 43 -609 137 -756 255 -72 58 -106 64
-144 25z"/>
<path d="M3505 2110 c-116 -17 -132 -21 -152 -45 -29 -33 -29 -64 -2 -99 25
-32 58 -33 241 -8 134 19 161 31 172 78 7 28 -16 79 -39 88 -25 9 -76 6 -220
-14z"/>
<path d="M2735 2028 c-35 -30 -37 -79 -5 -113 23 -25 26 -25 190 -25 154 0
169 2 194 21 35 28 36 80 1 114 -24 25 -27 25 -189 25 -157 0 -166 -1 -191
-22z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,13 +1,9 @@
{% extends "base.html" %} {% block title %}About - Simple Blog Template{%
endblock %} {% block content %}
{% extends "base.html" %} {% block title %}{{blog_title.title}}{% endblock %}{%
block content %}
<div class="row">
<div class="col-lg-12">
<h1>About</h1>
<hr />
<!-- Post Content -->
<div>{{ about_txt.content | safe }}</div>
<p>Last Update : October 2024</p>
<hr />
</div>
</div>
{% endblock %}

View File

@@ -8,6 +8,10 @@
<meta name="author" content="" />
<title>{% block title %}Default Title{% endblock %}</title>
<link
rel="shortcut icon"
href="{{ url_for('static', filename='favicon.svg') }}"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
@@ -27,10 +31,6 @@
href="{{ url_for('static', filename='css/blog.css') }}"
rel="stylesheet"
/>
<link
href="{{ url_for('static', filename='css/components/timeline.css') }}"
rel="stylesheet"
/>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
@@ -41,6 +41,7 @@
};
</script>
<script src="{{ url_for('static', filename='js/timeline.js') }}"></script>
<style></style>
</head>
<body>

View File

@@ -1,5 +0,0 @@
<div class="background-svg">
<img src="{{ url_for('static', filename='animation.svg') }}" alt="" />
<br />
<p>tree source: codepen @uchardon</p>
</div>

View File

@@ -6,7 +6,7 @@
{% else %}
<div class="blog-preview">{{ post.content | striptags | truncate(200) }}</div>
<div class="blog-preview">{{ post.preview }}</div>
<a class="btn-read-more" href="{{ url_for('post_detail', post_id=post.id) }}">
Read More

View File

@@ -1,3 +0,0 @@
{% macro render_post(post) %}
<article class="blog-content">{{ post.content | safe }}</article>
{% if post.template %} {% include post.template %} {% endif %} {% endmacro %}

View File

@@ -1,9 +0,0 @@
<div class="tw-w-full tw-mx-auto tw-px-0 tw-py-12">
<ol
class="tw-relative tw-border-l-2 tw-border-gray-300 tw-list-none tw-p-0 tw-m-0 tw-ml-4"
>
{{ post.timeline | safe }}
</ol>
</div>
<li class="tw-mb-12 tw-ml-6 tw-list-none tw-relative"></li>

View File

@@ -1,6 +1,7 @@
{% extends "base.html" %} {% block title %}{{ post.title }} - Simple Blog
Template{% endblock %} {% block content %} {% from
"components/post_renderer.html" import render_post %} {% from
{% for css in component_css %}
<link rel="stylesheet" href="{{ url_for('static', filename=css) }}" />
{% endfor %} {% extends "base.html" %} {% block title %}{{ post.title }} -
Simple Blog Template{% endblock %} {% block content %} {% from
"components/comment_renderer.html" import render_comment %}
<div class="row">
<div class="col-lg-12">
@@ -9,7 +10,7 @@ Template{% endblock %} {% block content %} {% from
<p>
<span class="glyphicon glyphicon-time"></span> Posted on {{ post.date }}
</p>
{{ render_post(post) }}
{{ content | safe }}
<div class="post-actions">
<a href="{{ url_for('home') }}" class="btn btn-default btn-custom"
>← Back to Posts</a

View File

@@ -1,3 +0,0 @@
<h1 class="text-3xl font-bold mb-8">Project Timeline</h1>
{% include "components/timeline.html" %}