M main/templates/assets/style.css => main/templates/assets/style.css +2 -1
@@ 268,8 268,9 @@ td {
}
pre {
- background: var(--airy-grey-color);
+ background: var(--airy-grey-color) !important;
overflow-x: auto;
+ padding: 12px;
}
code {
M main/templates/main/page_detail.html => main/templates/main/page_detail.html +15 -0
@@ 8,6 8,21 @@
{% include 'partials/rsl_license_head.html' %}
{% endblock head_rsl_license %}
+{% block head_extra %}
+<style>
+
+ /* Light theme */
+ @media (prefers-color-scheme: light) {
+ {{ light_css|safe }}
+ }
+
+ /* Dark theme */
+ @media (prefers-color-scheme: dark) {
+ {{ dark_css|safe }}
+ }
+</style>
+{% endblock head_extra %}
+
{% block content %}
<main>
{% if blog_user.blog_title %}
M main/templates/main/post_detail.html => main/templates/main/post_detail.html +15 -0
@@ 8,6 8,21 @@
{% include 'partials/rsl_license_head.html' %}
{% endblock head_rsl_license %}
+{% block head_extra %}
+<style>
+
+ /* Light theme */
+ @media (prefers-color-scheme: light) {
+ {{ light_css|safe }}
+ }
+
+ /* Dark theme */
+ @media (prefers-color-scheme: dark) {
+ {{ dark_css|safe }}
+ }
+</style>
+{% endblock head_extra %}
+
{% block content %}
<main>
{% if blog_user.blog_title %}
M main/util.py => main/util.py +16 -1
@@ 15,6 15,7 @@ from django.utils.text import slugify
from pygments.formatters import HtmlFormatter
from pygments.lexers import ClassNotFound, get_lexer_by_name, get_lexer_for_filename
from l2m4m import LaTeX2MathMLExtension
+from pygments import highlight
from main import denylist, models
@@ 24,13 25,15 @@ from mdit_py_plugins.tasklists import tasklists_plugin
from graphviz import Source
md = (
- MarkdownIt("commonmark", {"html": True})
+ MarkdownIt("commonmark", {"html": True, "highlight": None})
.enable("strikethrough")
.enable("table")
.use(footnote_plugin)
.use(tasklists_plugin)
)
+formatter = HtmlFormatter(nowrap=True)
+
# Define allowed CSS properties and SVG attributes
ALLOWED_CSS_PROPERTIES = frozenset([
"azimuth", "background-color", "border-bottom-color", "border-collapse",
@@ 162,6 165,15 @@ def syntax_highlight(text):
return processed_text
+def highlight_code(code: str, lang: str) -> str:
+ try:
+ lexer = get_lexer_by_name(lang, stripall=True)
+ except Exception:
+ # fallback for unknown languages
+ return f"<pre><code>{code}</code></pre>"
+ return f'<pre class="code-block {lang}"><code>' + \
+ highlight(code, lexer, formatter) + \
+ "</code></pre>"
def clean_html(dirty_html, strip_tags=False):
allowed_tags = list(bleach.sanitizer.ALLOWED_TAGS) + denylist.ALLOWED_HTML_ELEMENTS + SVG_TAGS + MATHML_TAGS
@@ 191,6 203,9 @@ def fence_override(tokens, idx, options, env):
return svg_str
except Exception:
return f"<pre>{code}</pre>"
+
+ if lang:
+ return highlight_code(code, lang)
# fallback to default renderer
if default_fence:
M main/views/general.py => main/views/general.py +14 -0
@@ 41,6 41,8 @@ from main import denylist, forms, models, util
from main.sitemaps import PageSitemap, PostSitemap, StaticSitemap
from main.views import billing
+from pygments.formatters import HtmlFormatter
+
logger = logging.getLogger(__name__)
@@ 334,6 336,8 @@ def post_detail_redir(request, slug):
class PostDetail(DetailView):
model = models.Post
+ light_css = HtmlFormatter(style="default").get_style_defs('.code-block')
+ dark_css = HtmlFormatter(style="monokai").get_style_defs('.code-block')
def get_queryset(self):
queryset = models.Post.objects.filter(owner__username=self.request.subdomain)
@@ 367,6 371,10 @@ class PostDetail(DetailView):
# Reading time calculation
context["reading_time"] = util.reading_time(self.object.body)
+ # Pygments CSS for code highlighting
+ context["light_css"] = self.light_css
+ context["dark_css"] = self.dark_css
+
# do not record analytic if post is authed user's
if (
self.request.user.is_authenticated
@@ 931,6 939,8 @@ class PageCreate(LoginRequiredMixin, SuccessMessageMixin, CreateView):
class PageDetail(DetailView):
model = models.Page
+ light_css = HtmlFormatter(style="default").get_style_defs('.code-block')
+ dark_css = HtmlFormatter(style="monokai").get_style_defs('.code-block')
def get_queryset(self):
queryset = models.Page.objects.filter(owner__username=self.request.subdomain)
@@ 955,6 965,10 @@ class PageDetail(DetailView):
context["license_url"] = license_url
+ # Pygments CSS for code highlighting
+ context["light_css"] = self.light_css
+ context["dark_css"] = self.dark_css
+
# do not record analytic if post is authed user's
if (
self.request.user.is_authenticated