iuna

iuna

iuna - experimental mainnet-candidate protocol
git clone https://getiuna.org/git/iuna.git
Log | Files | Refs | README | LICENSE

commit 68ed76b65add4f265a9990ebad658e2ce2a73e57
parent b986866cce1ac2705d373d163a32ac75c4b78544
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Mon, 17 Aug 2026 23:29:24 +0200

Style markdown files in git browser

Diffstat:
Mscripts/postprocess_stagit_site.py | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mwww/assets/static-listing.css | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 202 insertions(+), 3 deletions(-)

diff --git a/scripts/postprocess_stagit_site.py b/scripts/postprocess_stagit_site.py @@ -19,7 +19,11 @@ TOPBAR = """ </div>""" -def postprocess_html(html: str) -> str: +LINE_RE = re.compile(r'(?P<line><a\s+[^>]*class="line"[^>]*>.*?</a>)(?P<body>.*)', re.S) +BLOB_RE = re.compile(r'(<pre id="blob"[^>]*>)(.*?)(</pre>)', re.S) + + +def postprocess_html(html: str, path: Path | None = None) -> str: if '<div class="topbar">' not in html: html = re.sub(r"<body([^>]*)>", r"<body\1>\n" + TOPBAR, html, count=1) @@ -37,7 +41,134 @@ def postprocess_html(html: str) -> str: count=1, flags=re.S, ) - return re.sub(r"<tr([^>]*)><td></td><td>", r"<tr\1><td>", html) + html = re.sub(r"<tr([^>]*)><td></td><td>", r"<tr\1><td>", html) + + if path is not None and is_markdown_file_page(path): + html = style_markdown_blob(html) + return html + + +def is_markdown_file_page(path: Path) -> bool: + return path.name.endswith(".md.html") or path.name.endswith(".markdown.html") + + +def style_markdown_blob(html: str) -> str: + def replace_blob(match: re.Match[str]) -> str: + opening, blob, closing = match.groups() + if re.search(r'class="[^"]*\bmd-source\b', opening): + return match.group(0) + if 'class="' in opening: + opening = re.sub(r'class="([^"]*)"', r'class="\1 md-source"', opening, count=1) + else: + opening = opening[:-1] + ' class="md-source">' + return opening + style_markdown_lines(blob) + closing + + return BLOB_RE.sub(replace_blob, html, count=1) + + +def style_markdown_lines(blob: str) -> str: + lines = blob.splitlines(keepends=True) + in_fence = False + styled = [] + for line in lines: + newline = "" + if line.endswith("\r\n"): + line, newline = line[:-2], "\r\n" + elif line.endswith("\n"): + line, newline = line[:-1], "\n" + + match = LINE_RE.match(line) + if not match: + styled.append(line + newline) + continue + + anchor = match.group("line") + body = match.group("body") + stripped = body.lstrip(" ") + leading = body[: len(body) - len(stripped)] + if stripped.startswith("```") or stripped.startswith("~~~"): + in_fence = not in_fence + body = leading + '<span class="md-fence">' + stripped + "</span>" + elif not in_fence: + body = style_markdown_line_body(body) + styled.append(anchor + body + newline) + return "".join(styled) + + +def style_markdown_line_body(body: str) -> str: + if not body.strip(): + return body + + leading = body[: len(body) - len(body.lstrip(" "))] + text = body[len(leading) :] + heading = re.match(r"(#{1,6})(\s+)(.*)", text) + if heading: + level = len(heading.group(1)) + return ( + leading + + f'<span class="md-heading md-heading-{level}">' + + '<span class="md-marker">' + + heading.group(1) + + heading.group(2) + + "</span>" + + style_markdown_inline(heading.group(3)) + + "</span>" + ) + + blockquote = re.match(r"(&gt;)(\s?)(.*)", text) + if blockquote: + return ( + leading + + '<span class="md-blockquote"><span class="md-marker">' + + blockquote.group(1) + + blockquote.group(2) + + "</span>" + + style_markdown_inline(blockquote.group(3)) + + "</span>" + ) + + unordered = re.match(r"([-*+])(\s+)(.*)", text) + if unordered: + return leading + style_markdown_marker_line("md-list", unordered) + + ordered = re.match(r"(\d+\.)(\s+)(.*)", text) + if ordered: + return leading + style_markdown_marker_line("md-list", ordered) + + thematic_break = re.match(r"((?:[-*_]\s*){3,})$", text) + if thematic_break: + return leading + '<span class="md-rule">' + thematic_break.group(1) + "</span>" + + return leading + style_markdown_inline(text) + + +def style_markdown_marker_line(class_name: str, match: re.Match[str]) -> str: + return ( + f'<span class="{class_name}"><span class="md-marker">' + + match.group(1) + + match.group(2) + + "</span>" + + style_markdown_inline(match.group(3)) + + "</span>" + ) + + +def style_markdown_inline(text: str) -> str: + spans: list[str] = [] + + def stash(class_name: str, value: str) -> str: + spans.append(f'<span class="{class_name}">{value}</span>') + return f"\0{len(spans) - 1}\0" + + text = re.sub(r"`([^`\n]+)`", lambda m: stash("md-code", m.group(1)), text) + text = re.sub(r"\*\*([^*\n]+)\*\*", r"<strong>\1</strong>", text) + text = re.sub(r"__([^_\n]+)__", r"<strong>\1</strong>", text) + text = re.sub(r"(?<!\*)\*([^*\n]+)\*(?!\*)", r"<em>\1</em>", text) + + def restore(match: re.Match[str]) -> str: + return spans[int(match.group(1))] + + return re.sub(r"\0(\d+)\0", restore, text) def main() -> int: @@ -52,7 +183,7 @@ def main() -> int: for path in git_root.rglob("*.html"): html = path.read_text(encoding="utf-8") - path.write_text(postprocess_html(html), encoding="utf-8") + path.write_text(postprocess_html(html, path), encoding="utf-8") return 0 diff --git a/www/assets/static-listing.css b/www/assets/static-listing.css @@ -154,6 +154,74 @@ pre#blob, table-layout: fixed; } +pre#blob { + --line-number-width: 5.25rem; + padding-left: var(--line-number-width); +} + +pre#blob a.line { + display: inline-block; + width: var(--line-number-width); + margin-left: calc(-1 * var(--line-number-width)); + padding-right: .75rem; + text-align: right; + user-select: none; +} + +pre#blob.md-source { + line-height: 1.62; +} + +pre#blob.md-source .md-heading { + display: inline-block; + margin: .45rem 0 .15rem; + color: var(--text); + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-weight: 850; +} + +pre#blob.md-source .md-heading-1 { font-size: 1.75rem; } +pre#blob.md-source .md-heading-2 { font-size: 1.42rem; } +pre#blob.md-source .md-heading-3 { font-size: 1.18rem; } +pre#blob.md-source .md-heading-4, +pre#blob.md-source .md-heading-5, +pre#blob.md-source .md-heading-6 { font-size: 1.02rem; } + +pre#blob.md-source strong { + color: #f4f8fa; + font-weight: 850; +} + +pre#blob.md-source em { + color: #d7e0e5; +} + +pre#blob.md-source .md-code { + padding: .08rem .28rem; + border: 1px solid rgba(141, 233, 205, .22); + border-radius: 4px; + background: rgba(141, 233, 205, .08); + color: var(--cyan); +} + +pre#blob.md-source .md-marker, +pre#blob.md-source .md-fence { + color: #66747b; +} + +pre#blob.md-source .md-blockquote { + color: #c9d4d9; +} + +pre#blob.md-source .md-list { + color: #c6d1d6; +} + +pre#blob.md-source .md-rule { + color: var(--line); + letter-spacing: .12rem; +} + #blob td:first-child { width: 1%; white-space: nowrap;