From 8bcbf3c6cfadf42371cfde94b4760e922a0ec60e Mon Sep 17 00:00:00 2001 From: mar77i Date: Sun, 16 Feb 2025 06:10:18 +0100 Subject: [PATCH] initial commit --- .gitignore | 2 + content/index.md | 5 ++ generate.py | 65 ++++++++++++++++++++++ md.py | 140 +++++++++++++++++++++++++++++++++++++++++++++++ post-receive.sh | 50 +++++++++++++++++ style.css | 0 template.html | 21 +++++++ template.py | 100 +++++++++++++++++++++++++++++++++ utils.py | 14 +++++ 9 files changed, 397 insertions(+) create mode 100644 .gitignore create mode 100644 content/index.md create mode 100755 generate.py create mode 100644 md.py create mode 100755 post-receive.sh create mode 100644 style.css create mode 100644 template.html create mode 100644 template.py create mode 100644 utils.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2483976 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +__pycache__/ diff --git a/content/index.md b/content/index.md new file mode 100644 index 0000000..0601254 --- /dev/null +++ b/content/index.md @@ -0,0 +1,5 @@ +# Home + +Hi, I'm mar77i, and I'm from Switzerland. + +For blog posts, see under [Blog](blog/)... diff --git a/generate.py b/generate.py new file mode 100755 index 0000000..9f4b50f --- /dev/null +++ b/generate.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +import shutil +from argparse import ArgumentParser +from pathlib import Path + +from md import MDRenderer +from template import Template +from utils import get_content, write_content + + +def main(): + ap = ArgumentParser() + ap.add_argument("--output-dir", default="/dev/shm/output") + args = ap.parse_args() + source_path = Path(__file__).parent + content_path = source_path / "content" + output_path = Path(args.output_dir) + if output_path.exists(): + shutil.rmtree(output_path) + output_path.mkdir(0o755) + template = Template(source_path / "template.html") + context = { + "nav-items": [ + { + "link": "/", + "text": "Home", + }, + { + "link": "/blog/", + "text": "Blog", + }, + { + "link": "https://git.mar77i.info/", + "text": "Git", + }, + ] + } + context["title"], context["page"] = MDRenderer( + get_content(content_path / "index.md") + ).render_html() + write_content(output_path / "index.html", template.render(context)) + + #context["blogs"] = [] + #context["hashtags"] = [] + #for file in (content_path / "blog").iterdir(): + # if file.name == "index.md" or not file.name.endswith(".md"): + # continue + # context["blogs"].append(f"blog/{file.name[:-3]}.html") + # context["title"], context["page"] = MDRenderer(get_content(file)).render_html() + # write_content( + # blog_path / f"{file.name[:-3]}.html", template.render(context), + # ) + #context["title"], context["page"] = MDRenderer( + # get_content(content_path / "blog" / "index.md") + #).render_html() + + blog_path = output_path / "blog" + blog_path.mkdir(0o755) + context["title"], context["page"] = "Blog stub", "

Blog stub

" + write_content(blog_path / "index.html", template.render(context)) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/md.py b/md.py new file mode 100644 index 0000000..acc64d4 --- /dev/null +++ b/md.py @@ -0,0 +1,140 @@ +from collections import OrderedDict +from html import escape +from io import SEEK_END, StringIO + + +class Paragraph: + name = "p" + + def __init__(self): + self.lines = [] + self.attributes = OrderedDict() + + def join_attrs(self): + if not self.attributes: + return "" + return "".join( + f' {key}="{escape(value)}"' for key, value in self.attributes.items() + ) + + def join_lines(self): + content = '\n'.join(self.lines) + sio = StringIO() + backslash = False + link = None + for i, c in enumerate(content): + if backslash: + assert c in "\\()[]`*/_\n" + if c == "\n": + c = " " + sio.write(c) + elif link is not None: + if c == "[]()"[len(link)]: + link.append(sio.tell()) + if len(link) == 4: + sio.seek(link[0]) + text = sio.read(link[1] - link[0]) + sio.read(1 + link[2] - link[1]) + url = sio.read() + sio.seek(link[0]) + link = None + sio.truncate() + sio.write('') + sio.write(escape(text, False)) + sio.write("") + continue + if c == "\\": + backslash = True + continue + elif c == "[": + link = [sio.tell()] + continue + sio.write(c) + assert backslash is False and link is None + return sio.getvalue() + + def join(self): + return f"<{self.name}{self.join_attrs()}>{self.join_lines()}" + + +class Heading2(Paragraph): + name = "h2" + + +class BulletList(Paragraph): + name = "ul" + + class ListItem(Paragraph): + name = "li" + + def __init__(self): + self.list_items = [] + super().__init__() + + @property + def lines(self): + return + + @lines.setter + def lines(self, value): + self.list_items.append(self.ListItem()) + + +class MDRenderer: + """ + Simplified markdown to html translator. + """ + METACHARS = "\\[*/_~`" + INLINE_TAGS = { + "*": "b", + "/": "i", + "_": "u", + "~": "del", + "`": "code", + } + + def __init__(self, page): + self.page = page + self.tag = None + self.sio = StringIO() + + def render_html(self): + # let's initially support #/

,

, and single level lists